PKCS12 Java Keystore от CA и пользовательский сертификат в Java - PullRequest
13 голосов
/ 01 сентября 2010

Я недавно был назначен ответственным за макетирование продукта Apple (утилиты конфигурирования iPhone) на Java.Один из разделов, на котором я немного застрял, - это часть об Exchange ActiveSync.Там он позволяет вам выбрать сертификат из цепочки для ключей для использования в качестве учетных данных для вашей учетной записи EAS.После некоторых исследований я обнаружил, что на самом деле он создает хранилище ключей PKCS12, вставляет закрытый ключ выбранного сертификата и кодирует его в XML.Пока что ничего страшного.Если я создаю файл .p12 с помощью Keychain Access, он загружается без проблем.Но я сталкиваюсь с проблемой, когда пытаюсь перенести это на Java.

Скажем, я экспортирую один из тех сертификатов, которые я использовал ранее, с файлом .p12 как файл .cerожидая попасть в окружающую среду).Теперь, когда я загружаю его в Java, я получаю объект Certificate следующим образом ...

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

Но когда я пытаюсь ...

ks.setCertificateEntry("SomeAlias", userCert);

, я получаю исключение ...

java.security.KeyStoreException: TrustedCertEntry not supported

Итак, из сертификатов я перехожу на ключи.Но с этими сертификатами (я также получил сертификат CA) я могу получить доступ только к открытому ключу, а не к личному.И если я попытаюсь добавить открытый ключ следующим образом ...

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

Я получу ...

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

Так что теперь я здесь.У кого-нибудь есть идеи, как получить закрытый ключ из файла .cer в хранилище ключей PKCS12 на Java?Я даже на правильном пути?

Заранее спасибо!

Ответы [ 3 ]

17 голосов
/ 01 сентября 2010

Формат PKCS # 12 предназначен для хранения закрытого ключа, связанного с цепочкой сертификатов, и оба требуются (хотя вам может не понадобиться вся цепочка). Хотя тип хранилища ключей PKCS12 хорошо подходит для преобразования этого формата в Java KeyStore, по этой причине поддерживается не все.

То, что вы пытаетесь сделать с первой попытки, - это сохранить сертификат самостоятельно, что не сработает.

То, что вы пытаетесь сделать во второй попытке (ks.setKeyEntry("SomeAlias", userCert.getPublicKey().getEncoded(), chain)), - это использование открытого ключа вместо того, что должно быть закрытым ключом (см. KeyStore#setKeyEntry).

Файл

.cer, как правило, предназначен только для сертификатов, а не закрытых ключей (хотя, конечно, расширение в конечном итоге является лишь указанием). Если вы экспортируете файл .cer из Keychain Access.app , вы не получите с ним закрытый ключ (для этого нужен формат экспорта .p12).

РЕДАКТИРОВАТЬ о KeychainStore:

Если причина, по которой вы пытаетесь выполнить это преобразование, заключается в конечном счете для доступа к закрытым ключам и сертификатам, которые уже находятся в цепочке для ключей, вы можете загрузить их непосредственно из KeychainStore:

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

Пара замечаний по этому поводу:

  • Любой непустой, непустой пароль подходит для использования закрытого ключа (например, "-".toCharArray()), так как доступ будет запрашиваться службой безопасности ОС (как в других приложениях).
  • Насколько я знаю, все еще есть ошибка, и он разрешает доступ только к одной паре секретный ключ / сертификат (даже если в паре имеется несколько пар секретный ключ / сертификат). брелок)
3 голосов
/ 30 ноября 2010

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

Это способ добавления сертификата с ассоциированным закрытым ключом в хранилище ключей PKCS12. Когда вы используете аутентификацию клиента, хранилище ключей должно содержать также закрытый ключ, в этом случае вы используете KeyStore.getInstance ("PKCS12").

Когда вы не используете аутентификацию клиента, а только аутентификацию сервера (и закрытый ключ не будет добавлен в хранилище ключей, поскольку он принадлежит серверу), его лучше использовать KeyStore.getInstance ("JKS"), затем к этому одному хранилищу ключей можно добавить несколько сертификатов с псевдонимом.

Когда вы используете PKCS12, насколько я знаю, вы можете добавить только 1 сертификат (вам нужно добавить всю цепочку сертификатов), связанный с закрытым ключом, который вы хотите использовать для этого сертификата.

0 голосов
/ 14 сентября 2016

Я на пару лет опоздал на вечеринку, но мне потребовалось несколько часов, чтобы правильно работать, поэтому я подумал, что стоит опубликовать рабочее решение . В этом решении используется 1) сертификат .p12 / PKCS12. и 2) ЦС, отсутствующий в TrustManager по умолчанию (и вы хотите добавить его программно, а не в TrustManager по умолчанию). 3) Никаких сторонних библиотек криптографии, просто HttpClient, чтобы собрать все вместе.

Я также добавил несколько полезных команд keytool и openssl в JavaDoc для работы с сертификатами, поскольку это само по себе искусство.

// 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...