어떻게 사용할 수 있는 다른 인증서에는 특정 연결은?
-
21-08-2019 - |
문제
모듈을 추가하는 우리의 큰 Java 응용 프로그램을 대화와 함께 다른 회사의 SSL 보안 웹사이트입니다.문제는 사용하는 사이트는 자체 서명된 인증서입니다.나는 증명서의 사본을 확인하는 내가 발생하지 않는 man-in-the-middle 공격,그리고 나는 통합해야 하는 이 인증서는 우리의 코드에서는 방식으로 서버와의 연결을 성공적으로 될 것입니다.
여기에서의 기본 코드:
void sendRequest(String dataPacket) {
String urlStr = "https://host.example.com/";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setMethod("POST");
conn.setRequestProperty("Content-Length", data.length());
conn.setDoOutput(true);
OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
o.write(data);
o.flush();
}
없이 추가적인 취급 장소에 대한 자체 서명된 인증서,이것에서 사망 나타납니다.getOutputStream()다음과 같은 경우를 제외하고:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
이상적으로는 나의 코드를 가르치는 자바를 받아들이는 자체 서명된 인증서,이를 위해 한 자리에서 응용 프로그램,그리고 어디에도 없습니다.
나는 내가 가져올 수 있습으로 인증서를 JRE 의 인증 기관장,그리고할 수 있는 자바를 받아들입니다.하지 않는 방식을 가지고 싶다면 내가 도움이 될 수 있습;그것은 매우 침해에서 우리의 모든 고객의 컴퓨터를 하나의 모듈을 사용할 수 없습;에 영향을 미치는 모든 다른 Java 응용 프로그램을 사용하여 동일한 JRE,그리고 내가 좋아하지 않도록 확률의 다른 Java 응용 프로그램 혹시 이 사이트에 액세스하는 전무합니다.그것도 아닌 사소한 작업:유닉스에서 나에 대한 액세스 권한을 얻을 수정할 수 있는 권한이 JRE 에서 이 방법입니다.
나 또한 볼 수 있는 나를 만들 수 있습 TrustManager 인스턴스는 일부 사용자 정의 검사입니다.그것은 다음과 같이 나도를 만들 수 있 TrustManager 는 대리자가 실제 TrustManager 모든 경우에서를 제외하고 이 인증서입니다.하지만 그것처럼 보이는 TrustManager 설치 전 세계적으로,그리고 나는 생각에 영향을 미칠 것 다른 모든 연결에서의 응용 프로그램,그리고 냄새가 나지 않는 아주 나에게 맞는다.
무엇을 원하는 표준 또는 최고의 방법을 설정 Java 응용 프로그램을 받아들이는 자체 서명된 인증서입니까?할 수 있는 내가 달성하는 목표를 모두가 마음에,또는 나는 타협?이 있는 옵션을 포함하는 파일과 디렉터리와 구성 설정과 작은 코드?
해결책
생성 SSLSocket
공장 직접 공장에 설정하십시오 HttpsURLConnection
연결하기 전에.
...
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslFactory);
conn.setMethod("POST");
...
당신은 하나를 만들고 싶을 것입니다 SSLSocketFactory
그리고 주위에 보관하십시오. 다음은 초기화하는 방법에 대한 스케치입니다.
/* Load the keyStore that includes self-signed cert as a "trusted" entry. */
KeyStore keyStore = ...
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
sslFactory = ctx.getSocketFactory();
키 스토어를 만드는 데 도움이 필요하면 의견을 제시하십시오.
키 스토어로드의 예는 다음과 같습니다.
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(trustStore, trustStorePassword);
trustStore.close();
PEM 형식 인증서로 키 스토어를 만들려면 사용하여 자신의 코드를 작성할 수 있습니다. CertificateFactory
, 또는 그냥 가져 오십시오 keytool
JDK (KeyTool 습관 "키 입력"을 위해 일하지만 "신뢰할 수있는 항목"에 적합합니다).
keytool -import -file selfsigned.pem -alias server -keystore server.jks
다른 팁
나는이 것을 해결하기 위해 온라인으로 많은 장소를 읽었습니다. 이것은 내가 작동하도록하기 위해 쓴 코드입니다.
ByteArrayInputStream derInputStream = new ByteArrayInputStream(app.certificateString.getBytes());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);
String alias = "alias";//cert.getSubjectX500Principal().getName();
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null);
trustStore.setCertificateEntry(alias, cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(trustStore, null);
KeyManager[] keyManagers = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
URL url = new URL(someURL);
conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
app.certificatestring은 인증서가 포함 된 문자열입니다.
static public String certificateString=
"-----BEGIN CERTIFICATE-----\n" +
"MIIGQTCCBSmgAwIBAgIHBcg1dAivUzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE" +
"BhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBE" +
... a bunch of characters...
"5126sfeEJMRV4Fl2E5W1gDHoOd6V==\n" +
"-----END CERTIFICATE-----";
정확한 구조를 위의 정확한 구조를 유지하는 한 자체 서명 된 경우 인증서 문자열에 문자를 넣을 수 있음을 테스트했습니다. 노트북의 터미널 명령 줄을 사용하여 인증서 문자열을 얻었습니다.
a를 만드는 경우 SSLSocketFactory
옵션이 아니며 키를 JVM으로 가져 오십시오.
공개 키 검색 :
$openssl s_client -connect dev-server:443
, 그런 다음 파일을 만듭니다 dev-server.pem 그것은 보인다-----BEGIN CERTIFICATE----- lklkkkllklklklklllkllklkl lklkkkllklklklklllkllklkl lklkkkllklk.... -----END CERTIFICATE-----
키 가져 오기 :
#keytool -import -alias dev-server -keystore $JAVA_HOME/jre/lib/security/cacerts -file dev-server.pem
. 비밀번호: 변경JVM을 다시 시작합니다
우리는 복사 JRE 의 신뢰 저장소에 추가 우리 사용자 지정 인증서를 신뢰 저장소,다음 응용 프로그램을 사용하는 사용자 정의 신뢰 저장소와 시스템을 제공합니다.이 방법은 우리가 기본 JRE 신뢰 저장소 혼자입니다.
단점은 경우 업데이트 JRE 얻을 수 없는 새로운 신뢰 저장소가 자동으로 병합으로 사용자 정의 하나입니다.
당신은 아마 이러한 시나리오를 필요에 의해 설치하거나 시작 일상적인 확인하는 신뢰 저장소/jdk 및 확인에 대한 불일치 또는 자동 업데이트를 신뢰 저장소.I don't know what 발생한 경우 업데이트 신뢰 저장소 응용 프로그램이 실행되는 동안.
이 솔루션은 없 100%우아하거나 고장이지만,간단한 작동,그리고 필요 없는 코드입니다.
Commons-HTTPClient를 사용하여 자체 서명 된 인증서로 내부 HTTPS 서버에 액세스 할 때 이와 같은 작업을 수행해야했습니다. 예, 우리의 솔루션은 단순히 모든 것을 전달한 사용자 정의 신탁 관리자를 만드는 것이 었습니다 (디버그 메시지 로그).
이것은 로컬 SSLContext에서 SSL 소켓을 생성하는 자체 SSLSocketFactory를 갖추고 있습니다. Keystore/Certstore 근처에 전혀 갈 필요가 없습니다.
그래서 이것은 우리 지역의 SlSocketFactory에 있습니다.
static {
try {
SSL_CONTEXT = SSLContext.getInstance("SSL");
SSL_CONTEXT.init(null, new TrustManager[] { new LocalSSLTrustManager() }, null);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to initialise SSL context", e);
} catch (KeyManagementException e) {
throw new RuntimeException("Unable to initialise SSL context", e);
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
LOG.trace("createSocket(host => {}, port => {})", new Object[] { host, new Integer(port) });
return SSL_CONTEXT.getSocketFactory().createSocket(host, port);
}
SecureProtocolsocketFactory를 구현하는 다른 방법과 함께. LOCALSSLTRUSTMANAGER는 위에서 언급 한 더미 신탁 관리자 구현입니다.