2010-09-01 83 views
12

Recientemente me han encargado de simular un producto de Apple (Utilidad de configuración de iPhone) en Java. Una de las secciones en las que he estado estancado es parte de Exchange ActiveSync. Allí, le permite seleccionar un certificado de su Llavero para usar como credenciales para su cuenta EAS. Después de algunas investigaciones, descubrí que en realidad está creando un almacén de claves PKCS12, insertando la clave privada del certificado que seleccioné y codificándola en XML. Hasta ahora no es gran cosa. Si creo un archivo .p12 con Keychain Access, se carga sin problemas. Pero me encuentro con un problema cuando trato de llevar eso a Java.PKCS12 Java Keystore de CA y certificado de usuario en java

Digamos que exporto uno de esos certs que había usado anteriormente con el archivo .p12 como archivo .cer (esto es lo que esperamos obtener en el entorno). Ahora, cuando subo en Java consigo un objeto Certificado de la siguiente manera ...

KeyStore ks = java.security.KeyStore.getInstance("PKCS12"); 
ks.load(null, "somePassword".toCharArray()); 

CertificateFactory cf = CertificateFactory.getInstance("X.509", new BouncyCastleProvider()); 
java.security.cert.Certificate userCert = cf.generateCertificate(new FileInputStream("/Users/me/Desktop/RecentlyExportedCert.cer")); 

Pero cuando intento ...

ks.setCertificateEntry("SomeAlias", userCert); 

consigo la excepción ...

java.security.KeyStoreException: TrustedCertEntry not supported 

Por lo tanto, desde los certs me muevo hacia las teclas. Pero con esos certificados (también obtuve el certificado de CA), solo puedo acceder a la clave pública, no a la privada. Y si intento agregar la clave pública como tal ...

java.security.cert.Certificate[] chain = {CACert}; 
ks.setKeyEntry("SomeAlias", userCert.getPublicKey().getEncoded(), chain); 

consigo ...

java.security.KeyStoreException: Private key is not stored as PKCS#8 EncryptedPrivateKeyInfo: java.io.IOException: DerValue.getOctetString, not an Octet String: 3 

Así que ahora estoy aquí. ¿Alguien tiene alguna idea de cómo obtener una clave privada de un archivo .cer en un almacén de claves PKCS12 en Java? ¿Estoy incluso en el camino correcto?

¡Gracias de antemano!

Respuesta

16

El formato PKCS # 12 está destinado a almacenar una clave privada asociada a una cadena de certificados, y ambos son obligatorios (aunque es posible que no necesite toda la cadena). Aunque el tipo de almacén de claves PKCS12 hace un buen trabajo para asignar este formato a Java KeyStore, no todo es compatible por este motivo.

Lo que intenta hacer en su primer intento es almacenar un certificado por sí mismo, lo cual no funcionará.

Lo que intenta hacer en su segundo intento (ks.setKeyEntry("SomeAlias", userCert.getPublicKey().getEncoded(), chain)) es para una clave pública en lugar de lo que debería ser una clave privada (consulte KeyStore#setKeyEntry).

.cer El archivo suele ser solo para certificados y no para claves privadas (aunque, por supuesto, la extensión es solo una indicación). Si exporta su archivo .cer desde Keychain Access.app, no obtendrá la clave privada (para eso está el formato de exportación .p12).

EDITAR sobre KeychainStore:

Si la razón por la que estamos tratando de hacer esta conversión es en última instancia, para acceder a las claves privadas y certificados que ya están en el llavero que puede mostrarse desde el KeychainStore directamente:

KeyStore ks = KeyStore.getInstance("KeychainStore", "Apple"); 
ks.load(null, "-".toCharArray()); 

Un par de notas para esto:

  • Cualquier no nulo, no vacía contraseña wi Hago para usar la clave privada (p. "-".toCharArray()), ya que el servicio de seguridad del SO lo solicitará (como lo haría en otras aplicaciones).
  • Por lo que yo sé, todavía hay un error y it only allows access to one private key/certificate pair (incluso si un número de pares de pares de claves privadas/certificados están presentes en el llavero)
+0

+1, muy concienzudos! –

+0

Spot on. Gracias por la info! – Staros

3

http://www.docjar.com/html/api/org/bouncycastle/jce/examples/PKCS12Example.java.html

Este es cómo agregar un certificado con una clave privada de asociación a un almacén de claves PKCS12. Cuando usa autenticación de cliente, el almacén de claves necesita contener también la clave privada, en ese caso usa KeyStore.getInstance ("PKCS12").

Cuando no está utilizando la autenticación del cliente pero solo la autenticación del servidor (y la clave privada no se agregará al almacén de claves, ya que pertenece al servidor), es mejor usar KeyStore.getInstance ("JKS"), que usted agregue varios certificados con un alias a ese almacén de claves.

Cuando está utilizando PKCS12, hasta donde yo sé, solo puede agregar 1 certificado (debe agregar toda la cadena de certificados) asociado con la clave privada, que desea usar para ese certificado.

-1

Soy un par de años tarde a la fiesta, pero esto me tomó un par de horas para llegar a trabajar correctamente, así que pensé que valía la pena publicar una solución trabajo. Esta solución utiliza 1) un certificado .p12/PKCS12 y 2) una CA que no está en el TrustManager predeterminado (y desea agregarla programáticamente en lugar de agregarla al TrustManager predeterminado). 3) No hay bibliotecas de criptografía de terceros, solo HttpClient para unir todo.

También he agregado unos pocos y útiles comandos keytool y openssl en JavaDoc para trabajar con certificados, ya que es un arte en sí mismo.

// Stitch it all together with HttpClient 
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(getSSLSocketFactory()).build(); 


private SSLConnectionSocketFactory getSSLSocketFactory() { 
    try { 
     SSLContext sslContext = SSLContext.getInstance("TLS"); 

     KeyManager[] keyManager = getKeyManager("pkcs12", "path/to/cert.p12"), "p12_password")); 
     TrustManager[] trustManager = getTrustManager("jks", "path/to/CA.truststore", "trust_store_password")); 
     sslContext.init(keyManager, trustManager, new SecureRandom()); 

     return new SSLConnectionSocketFactory(sslContext); 
    } catch (Exception e) { 
     throw new RuntimeException("Unable to setup keystore and truststore", e); 
    } 
} 

/** 
* Some useful commands for looking at the client certificate and private key: 
* keytool -keystore certificate.p12 -list -storetype pkcs12 -v 
* openssl pkcs12 -info -in certificate.p12 
*/ 
private KeyManager[] getKeyManager(String keyStoreType, String keyStoreFile, String keyStorePassword) throws Exception { 
    KeyStore keyStore = KeyStore.getInstance(keyStoreType); 
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
    keyStore.load(this.getClass().getClassLoader().getResourceAsStream(keyStoreFile), keyStorePassword.toCharArray()); 
    kmf.init(keyStore, keyStorePassword.toCharArray()); 

    return kmf.getKeyManagers(); 
} 

/** 
* Depending on what format (pem/cer/p12) you have received the CA in, you will need to use a combination of openssl and keytool 
* to convert it to JKS format in order to be loaded into the truststore using the method below. 
* 
* You could of course use keytool to import this into the JREs TrustStore - my situation mandated I create it on the fly. 
* 
* Useful command to look at the CA certificate: 
* keytool -keystore root_ca.truststore -list -storetype jks -v 
* 
*/ 
private TrustManager[] getTrustManager(String trustStoreType, String trustStoreFile, String trustStorePassword) throws Exception { 
    KeyStore trustStore = KeyStore.getInstance(trustStoreType); 
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
    trustStore.load(this.getClass().getClassLoader().getResourceAsStream(trustStoreFile), trustStorePassword.toCharArray()); 
    tmf.init(trustStore); 

    return tmf.getTrustManagers(); 
} 
+1

Esta respuesta ni siquiera es relevante aquí. La pregunta es, en esencia, por qué no puede crear una entrada confiable en un PKCS12 'KeyStore'. Ni siquiera muestra una tienda de confianza PKCS # 12 o explica por qué eso no funciona. – erickson

Cuestiones relacionadas