2017-03-08 280 views
1

我收到一份自簽名客戶端證書工具包,用於通過HTTPS訪問服務器。該套件包括以下PEM文件:如何從PEM證書和密鑰構建SSLSocketFactory而不轉換爲密鑰庫?

  1. client.crt(客戶端證書)
  2. client.key(客戶端私鑰)
  3. ca.crt(CA證書)

一的方式來解決的任務是生成Java密鑰:

  1. 使用OpenSSL客戶端證書和密鑰轉換爲PKCS12密鑰庫
  2. 使用密鑰工具導入CA證書到店

...然後使用如下代碼來構建SSLSocketFactory的例如:

InputStream stream = new ByteArrayInputStream(pksData);   
KeyStore keyStore = KeyStore.getInstance("PKCS12"); 
keyStore.load(stream, password); 

KeyManagerFactory kmf = KeyManagerFactory.getInstance(
    KeyManagerFactory.getDefaultAlgorithm()); 
kmf.init(keyStore, password.toCharArray()); 
KeyManager[] keyManagers = kmf.getKeyManagers(); 

TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
    TrustManagerFactory.getDefaultAlgorithm()); 
tmfactory.init(keyStore); 
TrustManager[] trustManagers = tmfactory.getTrustManagers(); 

SSLContext sslContext = SSLContext.getInstance("TLS"); 
sslContext.init(keyManagers, trustManagers, null); 
sslSocketFactory = sslContext.getSocketFactory(); 

...這是後來用於初始化http庫。

所以我們獲得一個KeyStore,然後在它的幫助下初始化KeyManagers和TrustManagers,最後我們用它們構建SSLSocketFactory實例。

的問題是:有沒有辦法避免的密鑰存儲文件創建並以某種方式建立的SSLSocketFactory開始以公鑰證書和實例(其中,例如,可以從PEM文件使用BouncyCastle的的PemReader獲得)?

+0

號採取你必須建立一個PKCS#12或JKS密鑰庫。但你應該給予一個私鑰。你應該自己生成它。這裏存在嚴重的安全問題。你的私鑰不是私人的,所以無論誰給你的私鑰都可以在法律意義上冒充你。不要這樣做。 – EJP

+0

是的,謝謝,我們知道我們應該自己生成私鑰。但我們在這裏工作的機構規定其規則,不聽任何人:他們只是自己生成所有密鑰。不過這不是技術問題。 –

回答

2

事實證明,KeyStore實例仍然需要構建,但可以在內存中完成(以PEM文件作爲輸入開始),而無需使用keytool構建中間密鑰庫文件。

要生成內存密鑰庫,如下面的代碼可以被使用:

private static final String TEMPORARY_KEY_PASSWORD = "changeit"; 

private KeyStore getKeyStore() throws ConfigurationException { 
    try { 
     Certificate clientCertificate = loadCertificate(certificatePem); 
     PrivateKey privateKey = loadPrivateKey(privateKeyPem); 
     Certificate caCertificate = loadCertificate(caPem); 

     KeyStore keyStore = KeyStore.getInstance("JKS"); 
     keyStore.load(null, null); 
     keyStore.setCertificateEntry("ca-cert", caCertificate); 
     keyStore.setCertificateEntry("client-cert", clientCertificate); 
     keyStore.setKeyEntry("client-key", privateKey, TEMPORARY_KEY_PASSWORD.toCharArray(), new Certificate[]{clientCertificate}); 
     return keyStore; 
    } catch (GeneralSecurityException | IOException e) { 
     throw new ConfigurationException("Cannot build keystore", e); 
    } 
} 

private Certificate loadCertificate(String certificatePem) throws IOException, GeneralSecurityException { 
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X509"); 
    final byte[] content = readPemContent(certificatePem); 
    return certificateFactory.generateCertificate(new ByteArrayInputStream(content)); 
} 

private PrivateKey loadPrivateKey(String privateKeyPem) throws IOException, GeneralSecurityException { 
    return pemLoadPrivateKeyPkcs1OrPkcs8Encoded(privateKeyPem); 
} 

private byte[] readPemContent(String pem) throws IOException { 
    final byte[] content; 
    try (PemReader pemReader = new PemReader(new StringReader(pem))) { 
     final PemObject pemObject = pemReader.readPemObject(); 
     content = pemObject.getContent(); 
    } 
    return content; 
} 

private static PrivateKey pemLoadPrivateKeyPkcs1OrPkcs8Encoded(
     String privateKeyPem) throws GeneralSecurityException, IOException { 
    // PKCS#8 format 
    final String PEM_PRIVATE_START = "-----BEGIN PRIVATE KEY-----"; 
    final String PEM_PRIVATE_END = "-----END PRIVATE KEY-----"; 

    // PKCS#1 format 
    final String PEM_RSA_PRIVATE_START = "-----BEGIN RSA PRIVATE KEY-----"; 
    final String PEM_RSA_PRIVATE_END = "-----END RSA PRIVATE KEY-----"; 

    if (privateKeyPem.contains(PEM_PRIVATE_START)) { // PKCS#8 format 
     privateKeyPem = privateKeyPem.replace(PEM_PRIVATE_START, "").replace(PEM_PRIVATE_END, ""); 
     privateKeyPem = privateKeyPem.replaceAll("\\s", ""); 

     byte[] pkcs8EncodedKey = Base64.getDecoder().decode(privateKeyPem); 

     KeyFactory factory = KeyFactory.getInstance("RSA"); 
     return factory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey)); 

    } else if (privateKeyPem.contains(PEM_RSA_PRIVATE_START)) { // PKCS#1 format 

     privateKeyPem = privateKeyPem.replace(PEM_RSA_PRIVATE_START, "").replace(PEM_RSA_PRIVATE_END, ""); 
     privateKeyPem = privateKeyPem.replaceAll("\\s", ""); 

     DerInputStream derReader = new DerInputStream(Base64.getDecoder().decode(privateKeyPem)); 

     DerValue[] seq = derReader.getSequence(0); 

     if (seq.length < 9) { 
      throw new GeneralSecurityException("Could not parse a PKCS1 private key."); 
     } 

     // skip version seq[0]; 
     BigInteger modulus = seq[1].getBigInteger(); 
     BigInteger publicExp = seq[2].getBigInteger(); 
     BigInteger privateExp = seq[3].getBigInteger(); 
     BigInteger prime1 = seq[4].getBigInteger(); 
     BigInteger prime2 = seq[5].getBigInteger(); 
     BigInteger exp1 = seq[6].getBigInteger(); 
     BigInteger exp2 = seq[7].getBigInteger(); 
     BigInteger crtCoef = seq[8].getBigInteger(); 

     RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(modulus, publicExp, privateExp, prime1, prime2, 
       exp1, exp2, crtCoef); 

     KeyFactory factory = KeyFactory.getInstance("RSA"); 

     return factory.generatePrivate(keySpec); 
    } 

    throw new GeneralSecurityException("Not supported format of a private key"); 
} 

的想法是從Programmatically Obtain KeyStore from PEM

相關問題