2009-11-24 16 views
12

के लिए मेरे पास एकाधिक SSL प्रमाणपत्र कैसे हो सकते हैं मेरे पास जावा में एक इन-हाउस HTTP सर्वर लिखा गया है; मेरे निपटारे पर पूर्ण स्रोत कोड। HTTP सर्वर वेब साइटों के किसी भी संख्या, जिनमें से प्रत्येक के साथ बनाई गई एक अलग सुनने सॉकेट होगा कॉन्फ़िगर कर सकते हैं: जावा Keytool के साथ बनाई गई एक मानक कुंजी संग्रह का उपयोगजावा सर्वर

skt=SSLServerSocketFactory.getDefault().createServerSocket(prt,bcklog,adr); 

, मैं के लिए मुझे के जीवन से बाहर काम नहीं कर सकता अलग-अलग सुनो सॉकेट से जुड़े विभिन्न प्रमाणपत्र कैसे प्राप्त करें ताकि प्रत्येक कॉन्फ़िगर की गई वेब साइट का अपना प्रमाणपत्र हो।

मैं अब इस समय चुटकी में हूं, इसलिए कुछ कोड नमूने जो सबसे अधिक सराहना करेंगे। लेकिन इस संबंध में जेएसएसई एक साथ कैसे लटकता है, इस बारे में किसी भी अच्छे अवलोकन की सराहना करता हूं (मैंने सूर्य के जेएसएसई डॉको को तब तक खोजा है जब तक कि मेरा दिमाग दर्द न हो (शाब्दिक रूप से; हालांकि यह कैफीन निकासी हो सकती है))।

संपादित

वहाँ सुनने सॉकेट के साथ एक कुंजी संग्रह में सर्वर प्रमाणपत्र संबद्ध करने के लिए अन्य नाम का उपयोग करने के लिए कोई आसान तरीका है? ताकि:

  • ग्राहक सभी प्रमाणपत्र के लिए प्रबंधन करने के लिए एक कुंजी संग्रह, और
  • कई प्रमुख दुकानों के साथ बेला के आसपास कोई जरूरत नहीं है, आदि

मैं छाप हो रही थी है (इस दोपहर के पहले) कि मैं एक साधारण कीमैनगर लिख सकता हूं, केवल chooseServerAlias(...) गैर-शून्य लौट रहा है, जो कि उपनाम के नाम का नाम है - किसी के पास तर्क की उस पंक्ति पर कोई विचार है?

समाधान

समाधान मैं इस्तेमाल किया, slyvarking के जवाब से बनाया गया एक अस्थायी कुंजी संग्रह बना सकते हैं और इच्छित कुंजी/प्रमाणपत्र विलक्षण बाहरी कुंजी संग्रह से निकाले से पॉप्युलेट करने के लिए किया गया था। कोड जो किसी भी रुचि रखते हैं के लिए इस प्रकार है (svrctfals मेरी "सर्वर प्रमाणपत्र उर्फ" मान है):

SSLServerSocketFactory    ssf;         // server socket factory 
    SSLServerSocket      skt;         // server socket 

    // LOAD EXTERNAL KEY STORE 
    KeyStore mstkst; 
    try { 
     String kstfil=GlobalSettings.getString("javax.net.ssl.keyStore"  ,System.getProperty("javax.net.ssl.keyStore"  ,"")); 
     String ksttyp=GlobalSettings.getString("javax.net.ssl.keyStoreType" ,System.getProperty("javax.net.ssl.keyStoreType" ,"jks")); 
     char[] kstpwd=GlobalSettings.getString("javax.net.ssl.keyStorePassword",System.getProperty("javax.net.ssl.keyStorePassword","")).toCharArray(); 

     mstkst=KeyStore.getInstance(ksttyp); 
     mstkst.load(new FileInputStream(kstfil),kstpwd); 
     } 
    catch(java.security.GeneralSecurityException thr) { 
     throw new IOException("Cannot load keystore ("+thr+")"); 
     } 

    // CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING DESIRED CERTIFICATE 
    try { 
     SSLContext  ctx=SSLContext.getInstance("TLS"); 
     KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
     KeyStore   sktkst; 
     char[]   blkpwd=new char[0]; 

     sktkst=KeyStore.getInstance("jks"); 
     sktkst.load(null,blkpwd); 
     sktkst.setKeyEntry(svrctfals,mstkst.getKey(svrctfals,blkpwd),blkpwd,mstkst.getCertificateChain(svrctfals)); 
     kmf.init(sktkst,blkpwd); 
     ctx.init(kmf.getKeyManagers(),null,null); 
     ssf=ctx.getServerSocketFactory(); 
     } 
    catch(java.security.GeneralSecurityException thr) { 
     throw new IOException("Cannot create secure socket ("+thr+")"); 
     } 

    // CREATE AND INITIALIZE SERVER SOCKET 
    skt=(SSLServerSocket)ssf.createServerSocket(prt,bcklog,adr); 
    ... 
    return skt; 
+0

आप अपनी खुद की KeyManager बहुत आसानी से लागू कर सकते हैं, लेकिन "मास्टर" कुंजी संग्रह में एक उपनाम का उपयोग कर स्मृति में एक नया, अस्थायी KeyStore के निर्माण के अपने विचार एक अच्छा एक है। – erickson

उत्तर

8

ऐसा करने का सबसे आसान तरीका अपने सभी डोमेन नामों के लिए एक प्रमाणपत्र का उपयोग करना है। SAN (विषय वैकल्पिक नाम) में अन्य सभी साइट नाम रखें।

यदि आप प्रत्येक डोमेन नाम के लिए एक प्रमाणपत्र पसंद करते हैं, तो आप डोमेन की पहचान करने के लिए अपना स्वयं का मुख्य प्रबंधक लिख सकते हैं और उपनाम का उपयोग कर सकते हैं ताकि आप एक कीस्टोर का उपयोग कर सकें। हमारी प्रणाली में, हम एक सम्मेलन करते हैं कि कीस्टोर ऊर्फ हमेशा प्रमाण पत्र में सीएन के बराबर होता है।इसलिए हम

SSLContext sctx1 = SSLContext.getInstance("SSLv3"); 
sctx1.init(new X509KeyManager[] { 
    new MyKeyManager("/config/master.jks","changeme".toCharArray(),"site1.example.com") 
    },null, null); 
SSLServerSocketFactory ssf = (SSLServerSocketFactory) sctx1.getServerSocketFactory(); 
ServerSocket ss1 = ssf.createServerSocket(1234); 

... 

SSLContext sctx2 = SSLContext.getInstance("SSLv3"); 
sctx2.init(new X509KeyManager[] { 
    new MyKeyManager("/config/master.jks","changeme".toCharArray(),"site2.example.com") 
    },null, null); 
ssf = (SSLServerSocketFactory) sctx2.getServerSocketFactory(); 
ServerSocket ss2 = ssf.createServerSocket(5678); 

...

public static class MyKeyManager implements X509KeyManager { 
    private KeyStore keyStore; 
    private String alias; 
    private char[] password; 

    MyKeyManager(String keyStoreFile, char[] password, String alias) 
     throws IOException, GeneralSecurityException 
    { 
     this.alias = alias; 
     this.password = password; 
     InputStream stream = new FileInputStream(keyStoreFile); 
     keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
     keyStore.load(stream, password); 
    } 

    public PrivateKey getPrivateKey(String alias) { 
     try { 
      return (PrivateKey) keyStore.getKey(alias, password); 
     } catch (Exception e) { 
      return null; 
     } 
    } 

    public X509Certificate[] getCertificateChain(String alias) { 
     try { 
      java.security.cert.Certificate[] certs = keyStore.getCertificateChain(alias); 
      if (certs == null || certs.length == 0) 
       return null; 
      X509Certificate[] x509 = new X509Certificate[certs.length]; 
      for (int i = 0; i < certs.length; i++) 
       x509[i] = (X509Certificate)certs[i]; 
      return x509; 
     } catch (Exception e) { 
      return null; 
     }   
    } 

    public String chooseServerAlias(String keyType, Principal[] issuers, 
            Socket socket) { 
     return alias; 
    } 

    public String[] getClientAliases(String parm1, Principal[] parm2) { 
     throw new UnsupportedOperationException("Method getClientAliases() not yet implemented."); 
    } 

    public String chooseClientAlias(String keyTypes[], Principal[] issuers, Socket socket) { 
     throw new UnsupportedOperationException("Method chooseClientAlias() not yet implemented."); 
    } 

    public String[] getServerAliases(String parm1, Principal[] parm2) { 
     return new String[] { alias }; 
    } 

    public String chooseServerAlias(String parm1, Principal[] parm2) { 
     return alias; 
    } 
} 
+0

आह। यह वही है जो मुझे संदेह था कि कल किया जा सकता था ... इसे मेरे समाधान के लिए पसंद किया होगा, क्योंकि यह तर्कसंगत रूप से क्लीनर है, मुझे लगता है। –

+0

मैंने जो भी टूल देखा है, उनमें से कोई भी वैकल्पिक विषय नाम सेट करने का कोई तरीका नहीं है - क्या आपके पास कोई अनुशंसाएं हैं? –

+0

ओपनएसएसएल SANs के साथ सीएसआर बना सकता है। आप कमांड लाइन का उपयोग नहीं कर सकते हैं।आपको कॉन्फ़िगरेशन फ़ाइल में [alt_names] अनुभाग में SAN जोड़ना होगा। Http://therowes.net/~greg/2008/01/08/creating-a-certificate-with-multiple-hostnames/ देखें –

5

आप डिफ़ॉल्ट SSLServerSocketFactory उपयोग करने में सक्षम नहीं होगा।

इसके बजाय

, initialize प्रत्येक साइट के लिए एक अलग SSLContext, प्रत्येक सही सर्वर प्रमाण पत्र के साथ एक प्रमुख प्रवेश युक्त एक कुंजी संग्रह के साथ एक KeyManagerFactoryconfigured का उपयोग कर।

, get its SSLServerSocketFactory (KeyManagerFactory आरंभ करने के बाद, अपने key managersSSLContext की init विधि से गुजरती हैं।) SSLContext बाद initalized है, और का उपयोग करें कि आपके श्रोता बनाने के लिए।

KeyStore identity = KeyStore.getInstance(KeyStore.getDefaultType()); 
/* Load the keystore (a different one for each site). */ 
... 
SSLContext ctx = SSLContext.getInstance("TLS"); 
KeyManagerFactory kmf = 
    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
kmf.init(identity, password); 
ctx.init(kmf.getKeyManagers(), null, null); 
SSLServerSocketFactory factory = ctx.getServerSocketFactory(); 
ServerSocket server = factory.createSocket(port); 
+1

और सुनो सॉकेट से जुड़े एक अलग SSLContext को कैसे मिलता है? –

+0

इनमें से कौन सी ऑब्जेक्ट्स के लिए कस्टम कार्यान्वयन प्रदान करने की आवश्यकता होगी, और जेएसएसई द्वारा प्रदान की गई स्टॉक ऑब्जेक्ट्स का उपयोग कौन करेगा? –

+0

वे सभी स्टॉक जेएसएसई हो सकते हैं। आपका मुख्य कार्य प्रत्येक साइट के लिए एक अलग कुंजी स्टोर बनाना है। उदाहरण के लिए मेरा अपडेट देखें। मैंने आईडीई का उपयोग नहीं किया है, इसलिए कुछ विधियों के नाम बंद हो सकते हैं। – erickson

2

मैं हाल ही में एक ऐसी ही स्थिति में भाग कुछ इस तरह कर सकते हैं। मेरे पास एक कस्टम एम्बेडेड जावा वेब सर्वर है जो किसी भी वेबसाइट की मेजबानी कर सकता है। प्रत्येक वेबसाइट का अपना डोमेन नाम होता है। प्रत्येक वेबसाइट/डोमेन को सर्वर पर एक अद्वितीय आईपी पता असाइन किया जाता है। एक सॉकेट श्रोता साइटों है कि SSL प्रमाणपत्र के लिए पोर्ट 80

प्रत्येक IP पते के लिए बनाई गई है, मैं एक ही KeyStore में कुंजी और प्रमाणपत्र आयात किया। मैंने डोमेन नाम से मेल खाने के लिए प्रत्येक डोमेन के एसएसएल प्रमाण पत्र के लिए एक प्रमाणपत्र उपनाम असाइन किया। प्रत्येक डोमेन/वेबसाइट जिसमें SSL प्रमाणपत्र है, पोर्ट 443 पर एक नया सॉकेट श्रोता असाइन किया गया है।

डिफ़ॉल्ट रूप से, मानक जावा X509KeyManager और SunX509 कार्यान्वयन will pick the first aliases it finds for which there is a private key and a key of the right type for the chosen cipher suite (typically RSA)। दुर्भाग्यवश, चयनित उपनाम अनुरोधित डोमेन से संबंधित नहीं है, इसलिए आप प्रमाणपत्र त्रुटियों के साथ समाप्त होते हैं।

इस मुद्दे को दरकिनार करने के लिए, मैं ZZ Coder's suggestion इस्तेमाल किया और एक कस्टम X509KeyManager कार्यान्वित किया। असल में, मेरे सर्वर के लिए, मुझे X509ExtendedKeyManager की आवश्यकता थी जिसमें अतिरिक्त चयन EngineServerAlias ​​() विधि है।

मेरे कस्टम KeyManager होस्ट नामों की एक hashmap कर उनके संबंधित IP पते पर निर्भर करता है। जब कोई नया SSL अनुरोध किया जाता है, तो यह आने वाले आईपी पते की जांच करता है और संबंधित होस्टनाम पाता है। फिर, यह होस्टस्टोन से संबंधित कीस्टोर में उपनाम ढूंढने का प्रयास करता है।

private class MyKeyManager extends X509ExtendedKeyManager implements X509KeyManager { 
    private KeyStore keyStore; 
    private char[] password; 
    private java.util.HashMap<InetAddress, String> hosts; 

    public MyKeyManager(KeyStore keystore, char[] password, java.util.HashMap<InetAddress, String> hosts) 
    throws IOException, GeneralSecurityException { 
     this.keyStore = keystore; 
     this.password = password; 
     this.hosts = hosts; 
    } 

    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { 
     try{ 
      return hosts.get(InetAddress.getByName(engine.getPeerHost())); 
     } 
     catch(Exception e){ 
      return null; 
     } 
    } 

    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { 
     return hosts.get(socket.getLocalAddress()); 
    } 

    public PrivateKey getPrivateKey(String alias) { 
     try { 
      return (PrivateKey) keyStore.getKey(alias, password); 
     } 
     catch (Exception e) { 
      return null; 
     } 
    } 

    public X509Certificate[] getCertificateChain(String alias) { 
     try { 
      java.security.cert.Certificate[] certs = keyStore.getCertificateChain(alias); 
      if (certs == null || certs.length == 0) return null; 
      X509Certificate[] x509 = new X509Certificate[certs.length]; 
      for (int i = 0; i < certs.length; i++){ 
       x509[i] = (X509Certificate)certs[i]; 
      } 
      return x509; 
     } 
     catch (Exception e) { 
      e.printStackTrace(); 
      return null; 
     }   
    } 

    public String[] getServerAliases(String keyType, Principal[] issuers) { 
     throw new UnsupportedOperationException("Method getServerAliases() not yet implemented."); 
    } 

    public String[] getClientAliases(String keyType, Principal[] issuers) { 
     throw new UnsupportedOperationException("Method getClientAliases() not yet implemented."); 
    } 

    public String chooseClientAlias(String keyTypes[], Principal[] issuers, Socket socket) { 
     throw new UnsupportedOperationException("Method chooseClientAlias() not yet implemented."); 
    } 

    public String chooseEngineClientAlias(String[] strings, Principal[] prncpls, SSLEngine ssle) { 
     throw new UnsupportedOperationException("Method chooseEngineClientAlias() not yet implemented."); 
    }   
} 

कस्टम कीमैनगर का उपयोग SSLContext को प्रारंभ करने के लिए किया जाता है। अच्छी बात यह है कि आपको केवल एक SSLContext प्रारंभ करने की आवश्यकता है।

javax.net.ssl.KeyManager[] kms = new javax.net.ssl.KeyManager[]{ 
    new MyKeyManager(keystore, keypass, hosts) 
}; 
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 
tmf.init(keystore); 
javax.net.ssl.TrustManager[] tms = tmf.getTrustManagers(); 
SSLContext sslContext = SSLContext.getInstance("TLS"); 
sslContext.init(kms, tms, null); 
संबंधित मुद्दे