2012-05-03 2 views
16

자체 서명 인증서를 키 저장소에 자동으로 생성하여 Jetty에서 사용하는 몇 가지 코드가 있습니다. 특정 호스트에 대한 키가 이미 존재하는 경우 아무 일도 발생하지 않습니다하지만 존재하지 않는 경우, 우리는이 같은 새 키를 생성합니다Jetty의 키 저장소에 둘 이상의 인증서가있는 경우 어떻게 선택합니까?

public void generateKey(String commonName) { 
    X500Name x500Name = new X500Name("CN=" + commonName); 
    CertAndKeyGen keyPair = new CertAndKeyGen("DSA", "SHA1withDSA"); 
    keyPair.generate(1024); 
    PrivateKey privateKey = keyPair.getPrivateKey(); 
    X509Certificate certificate = keyPair.getSelfCertificate(x500Name, 20*365*24*60*60); 
    Certificate[] chain = { certificate }; 
    keyStore.setEntry(commonName, privateKey, "secret".toCharArray(), chain); 
} 

하나의 키와 인증서가있는 한이 모든만큼 잘 작동 키 저장소에 저장합니다. 여러 개의 키를 갖게되면, 이상한 일이 당신이 연결하려고 할 때 발생 :

java.io.IOException: HTTPS hostname wrong: should be <127.0.0.1> 

이 꽤 신비화 오류했지만, 내가 마지막으로 서버에 연결하여 주장 단위 테스트를 작성하여 그것을 추적하는 관리되는 인증서의 CN이 호스트 이름과 일치합니다. 내가 발견 한 것은 꽤 흥미 롭다. 부두 부관은 어떤 인증서를 클라이언트에게 보여줄지를 일관성있게 선택하는 것 같다. 예를 들어

은 "CN = 로컬 호스트"와 "CN = cheese.mydomain는"키 저장소에있는 경우

  • 는 항상 "CN = cheese.mydomain"를 선택했다.
  • "CN = 127.0.0.1"및 "CN = cheese.mydomain"이 키 저장소에있는 경우 항상 "CN = cheese.mydomain"을 선택했습니다.
  • "CN = 192.168.222.100"(cheese.mydomain) 및 "CN = cheese.mydomain"이 키 저장소에있는 경우 항상 "CN = 192.168.222.100"을 선택했습니다.

나는 저장소에있는 인증서를 반복하여 인쇄하여 첫 번째 인증서 또는 그와 같은 사소한 것이 항상 선택되지는 않는다는 것을 발견 한 코드를 작성했습니다.

정확히 어떤 기준을 사용합니까? 처음에는 localhost가 특별하다고 생각했지만 세 번째 예제는 완전히 나를 당혹스럽게 만들었습니다.

필자는 KeyManagerFactory로 판단합니다. 필자의 경우 SunX509입니다.

답변

13

이것은 실제로 궁극적으로 KeyManager (일반적으로 KeyManagerFactory에서 얻음)에 의해 결정됩니다.

키 스토어에는 서로 다른 별칭으로 저장된 여러 인증서가있을 수 있습니다. 별칭이 certAlias in the Jetty configuration을 통해 명시 적으로 구성되지 않은 경우 SunX509 구현은 선택한 암호 제품군 (일반적으로 RSA이지만 여기서는 귀하의 DSA)에 대해 개인 키와 올바른 유형의 키가있는 것으로 보이는 첫 번째 별칭을 선택합니다. . 당신이 Sun provider implementation을 본다면 선택 논리에 조금 더 가깝지 만, 일반적으로 별명에만 의존해서는 안됩니다.

물론 부두를 자신의 과 함께 자신의 SSLContext에 부여하여 별칭을 선택할 수 있습니다. 당신은 구현해야 할 것이다 : keyTypeissuers에서 떨어져 불행하게도

chooseServerAlias(String keyType, Principal[] issuers, Socket socket) 

, 당신이 결정을 내릴 수 모두가 socket 자체입니다. 기껏해야 유용한 정보는 로컬 IP 주소와 원격 IP 주소입니다.

서버가 동일한 포트에서 여러 IP 주소를 청취하지 않으면 항상 동일한 로컬 IP 주소를 얻습니다.(분명히, 적어도 두 가지가 있습니다 : 127.0.0.1192.168.222.100이지만, 테스트를 제외하고는 localhost에 관심이 없다고 생각됩니다.) 서버 측에서 SNI (Server Name Indication) 지원이 필요합니다. (지원하는 클라이언트가) 요청한 호스트 이름을 기반으로 결정합니다. 불행하게도 SNI was only introduced in Java 7, but only on the client side.

또 다른 문제는 Java clients will complain about IP addresses in the Subject DN's CN입니다. 일부 브라우저는이를 허용하지만 HTTPS 사양 (RFC 2818)과 호환되지 않습니다. IP 주소는 IP 주소 유형의 주체 대체 이름 항목이어야합니다.

+0

네, 우리는 IP 주소가있는 것을 발견했습니다. 결과적으로 일치하는지 여부를 확인하기 위해 자체 HostnameVerifier를 작성하게되었습니다. 원래 우리는 항상 true를 반환하는 HostnameVerifier를 가지고 있었으며 이는 명백한 이유 때문에 그냥 던져 져야했습니다. 기존의 많은 사용자들이 IP 주소를 "호스트 이름"으로 구성 했으므로 적극적으로 공격하려는 것은 아닙니다. – Trejkaz

관련 문제