Как прочитать закрытый ключ для использования с OpenSAML? - PullRequest
11 голосов
/ 08 марта 2011

ОК, это еще один из тех вопросов, "у меня нет реальной идеи, с чего начать", так что, надеюсь, ответ прост. Тем не менее, я не знаю, что искать, и мои попытки пока не принесли много пользы.

Я хочу прочитать закрытый ключ из файла (который сейчас находится на диске). В конечном итоге ключ будет находиться в базе данных, но на данный момент этого будет достаточно, и это различие не должно иметь никакого отношения к анализу материала ключа. Мне удалось создать экземпляр Credential, который содержит открытую часть ключа (подтверждено отладчиком), но я не могу понять, как читать закрытую часть. Пара ключей была сгенерирована как:

openssl genrsa 512 > d:\host.key
openssl req -new -x509 -nodes -sha1 -days 365 -key d:\host.key > d:\host.cert

( Да , я знаю, что 512-битные ключи RSA давно были взломаны. Однако для того, чтобы попытаться заставить работать API, я не вижу причин бесполезно истощать запас энтропии системы.) *

Код на данный момент:

import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.x509.BasicX509Credential;

private Credential getSigningCredential()
throws java.security.cert.CertificateException, IOException {
    BasicX509Credential credential = new BasicX509Credential();

    credential.setUsageType(UsageType.SIGNING);

    // read public key
    InputStream inStream = new FileInputStream("d:\\host.cert");
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
    inStream.close();
    credential.setEntityCertificate(cert);

    // TODO: read private key

    // done.
    return credential;
}

Но как мне прочитать файл host.key в части закрытого ключа credential, чтобы я мог использовать сгенерированный экземпляр Credential для подписи данных?

Ответы [ 2 ]

19 голосов
/ 08 марта 2011

BasicX509Credential не является частью стандартной Java;Я полагаю, вы говорите о org.opensaml.xml.security.x509.BasicX509Credential из OpenSAML.

Вы хотите PrivateKey, который вы установите с credential.setPrivateKey().Чтобы получить PrivateKey, вы должны сначала преобразовать закрытый ключ в формат, который может прочитать Java, а именно PKCS # 8:

openssl pkcs8 -topk8 -nocrypt -outform DER < D:\host.key > D:\host.pk8

Затем из Java:

RandomAccessFile raf = new RandomAccessFile("d:\\host.pk8", "r");
byte[] buf = new byte[(int)raf.length()];
raf.readFully(buf);
raf.close();
PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(buf);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(kspec);

и вуаля!у вас есть PrivateKey.

По умолчанию openssl записывает ключ в своем собственном формате (для ключей RSA PKCS # 8 оказывается оберткой вокруг этого формата), и он кодирует их в PEM, который Base64 с верхним и нижним колонтитулом.Обе характеристики не поддерживаются простой Java, следовательно, преобразование в PKCS # 8.Параметр -nocrypt обусловлен тем, что PKCS # 8 поддерживает необязательное шифрование секретного ключа на основе пароля.

Предупреждение: вы действительно действительно хотите использовать более длинный ключ RSA,512 бит слабые;512-битный ключ RSA был взломан в 1999 году с несколькими сотнями компьютеров.В 2011 году, с 12-летним технологическим прогрессом, следует предположить, что 512-битный ключ RSA может быть взломан почти любым.Поэтому используйте 1024-битные ключи RSA как минимум (предпочтительно, 2048-битные; вычислительные затраты при использовании ключа не так уж и плохи, вы все равно сможете выполнять сотни подписей в секунду).

1 голос
/ 14 декабря 2012

Этот вопрос относится к SAML, а также к тем, кто хочет получить закрытый ключ для подписи объекта XMLObject.Приведенный выше ответ прекрасно работает, и также возможно получить закрытый ключ из хранилища ключей:

        Properties sigProperties = new Properties();

    sigProperties.put("org.apache.ws.security.crypto.provider","org.apache.ws.security.components.crypto.Merlin");
    sigProperties.put("org.apache.ws.security.crypto.merlin.keystore.type","jks");
    sigProperties.put("org.apache.ws.security.crypto.merlin.keystore.password","keypass");
    sigProperties.put("org.apache.ws.security.crypto.merlin.keystore.alias","keyalias");
    sigProperties.put("org.apache.ws.security.crypto.merlin.keystore.file","keystore.jks");

    Crypto issuerCrypto = CryptoFactory.getInstance(sigProperties);

    String issuerKeyName = (String) sigProperties.get("org.apache.ws.security.crypto.merlin.keystore.alias");

    //See http://ws.apache.org/wss4j/xref/org/apache/ws/security/saml/ext/AssertionWrapper.html 'signAssertion' method
    // prepare to sign the SAML token
    CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
    cryptoType.setAlias(issuerKeyName);
    X509Certificate[] issuerCerts = issuerCrypto.getX509Certificates(cryptoType);
    if (issuerCerts == null) {
        throw new WSSecurityException(
                "No issuer certs were found to sign the SAML Assertion using issuer name: "
                        + issuerKeyName);
    }

    String password = ADSUnitTestUtils.getPrivateKeyPasswordFromAlias(issuerKeyName);

    PrivateKey privateKey = null;
    try {
        privateKey = issuerCrypto.getPrivateKey(issuerKeyName, password);
    } catch (Exception ex) {
        throw new WSSecurityException(ex.getMessage(), ex);
    }


    BasicX509Credential signingCredential = new BasicX509Credential();
    signingCredential.setEntityCertificate(issuerCerts[0]);
    signingCredential.setPrivateKey(privateKey);

    signature.setSigningCredential(signingCredential);

Это больше кода, чем запрошенный исходный запрос, но похоже, что они пытаются получить BasicX509Credential.

Спасибо, Йогеш

...