programing

특정 연결에서 다른 인증서를 사용하려면 어떻게 해야 합니까?

sourcetip 2022. 7. 12. 00:06
반응형

특정 연결에서 다른 인증서를 사용하려면 어떻게 해야 합니까?

대형 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();
}

자기서명증명서를 추가 처리하지 않으면 다음 예외를 제외하고 conn.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

이상적으로는 이 자기서명증명서 1개를 Java에 받아들이도록 코드를 설정할 필요가 있습니다.어플리케이션의 이 1개의 스폿에 대해서, 그 외의 스팟에 대해서입니다.

증명서를 JRE 인증국 스토어로 Import할 수 있으며, Java가 이를 받아들일 수 있습니다.이 접근방식은 도움이 될 수 있다면 채택하고 싶지 않습니다.고객의 모든 머신에서 사용하지 않을 수 있는 모듈 하나를 사용하는 것은 매우 번거로운 일입니다.같은 JRE를 사용하는 다른 모든 Java 어플리케이션에 영향을 미칩니다.다른 Java 어플리케이션이 이 사이트에 접속할 확률은 0이 됩니다.이것은 간단한 조작도 아닙니다.유닉스에서는, 이러한 방법으로 JRE를 수정하기 위해서 액세스 권한을 취득할 필요가 있습니다.

커스텀 체크를 실행하는 TrustManager 인스턴스를 생성할 수도 있습니다.이 증명서를 제외한 모든 인스턴스에서 실제 TrustManager에 위임하는 TrustManager를 만들 수 있을 것 같습니다.그러나 Trust Manager는 글로벌하게 설치되는 것 같습니다.또한 어플리케이션의 다른 모든 접속에 영향을 줄 것으로 생각됩니다.그것도 별로 좋지 않은 것 같습니다.

Java 어플리케이션이 자기서명증명서를 받아들이도록 설정하는 권장방법, 표준방법 또는 최선의 방법은 무엇입니까?위의 목표를 모두 달성할 수 있습니까, 아니면 타협해야 합니까?파일, 디렉토리, 설정, 코드 없음 등의 옵션이 있습니까?

작성하다SSLSocket하여 접접 the the the the the the the the the the the the the the the the the로 설정합니다.HttpsURLConnection를 참조해 주세요.

...
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslFactory);
conn.setMethod("POST");
...

해서 '1을 더하다'를 하나 것 같아요.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 Import하다로 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-----";

위의 구조를 그대로 유지한다면 증명서 문자열에 어떤 문자도 넣을 수 있다는 것을 테스트했습니다.노트북 터미널 명령줄에서 증명서 문자열을 취득했습니다.

「」를하는 경우SSLSocketFactory 됩니다.JVM Import.

  1. .$openssl s_client -connect dev-server:443그런 다음 다음과 같은 dev-server.pem 파일을 만듭니다.

    -----BEGIN CERTIFICATE----- 
    lklkkkllklklklklllkllklkl
    lklkkkllklklklklllkllklkl
    lklkkkllklk....
    -----END CERTIFICATE-----
    
  2. 를 Import Import하다.#keytool -import -alias dev-server -keystore $JAVA_HOME/jre/lib/security/cacerts -file dev-server.pem. 비밀번호 : 변경

  3. JVM 재부팅

출처: javax.net.ssl을 해결하는 방법.SSLHandshakeException?

JRE의 트러스트 스토어를 복사하고 커스텀 증명서를 트러스트 스토어에 추가한 후 시스템속성과 함께 커스텀트러스트 스토어를 사용하도록 어플리케이션에 지시합니다.이렇게 하면 기본 JRE 신뢰 스토어는 그대로 유지됩니다.

단점은 JRE를 갱신할 때 새로운 신뢰 스토어가 커스텀 신뢰 스토어와 자동으로 통합되지 않는다는 것입니다.

트러스트 스토어/jdk를 확인하고 불일치를 체크하거나 트러스트 스토어를 자동으로 갱신하는 설치 또는 스타트업 루틴을 사용하여 이 시나리오를 처리할 수 있습니다.응용 프로그램 실행 중에 신뢰 저장소를 업데이트하면 어떻게 되는지 알 수 없습니다.

이 솔루션은 100% 우아하거나 완벽하지는 않지만 심플하고 동작하며 코드가 필요하지 않습니다.

commons-httpclient를 사용하여 자체 서명 증명서를 사용하여 내부 https 서버에 액세스할 때 이와 같은 작업을 수행해야 했습니다.네, 저희 솔루션은 모든 것을 단순히 전달하는 커스텀 Trust Manager를 만드는 것이었습니다(디버깅메시지를 기록합니다).

이는 로컬 SSLContext에서 SSL 소켓을 작성하는 자체 SSL Socket Factory를 갖는 것으로 귀결됩니다.이것은 로컬 TrustManager만 관련지어지도록 설정되어 있습니다.키스토어/증명서 스토어 근처에 갈 필요가 전혀 없습니다.

LocalSSLSocket Factory에는 다음과 같은 내용이 있습니다.

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);
}

Secure Protocol Socket Factory를 구현하는 기타 방법.LocalSSLTrustManager는 전술한 더미 트러스트 매니저 구현입니다.

언급URL : https://stackoverflow.com/questions/859111/how-can-i-use-different-certificates-on-specific-connections

반응형