12

好的,这是另一个“我不知道从哪里开始”的问题,所以希望答案很简单。但是,我真的不知道要搜索什么,而且到目前为止我的尝试并没有多大用处。

我想从(当前在磁盘上的)文件中读取私钥。最终,密钥将驻留在数据库中,但目前这已经足够了,而且这种差异对解析密钥材料没有真正的影响。我已经能够创建一个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实例对数据进行签名?

4

2 回答 2

21

BasicX509Credential不是标准 Java 的一部分;我想您是在谈论org.opensaml.xml.security.x509.BasicX509CredentialOpenSAML。

你想要一个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位弱;1999 年,数百台计算机破解了 512 位 RSA 密钥。2011 年,经过 12 年的技术进步,人们应该假设 512 位 RSA 密钥几乎可以被任何人破解。因此,至少使用 1024 位 RSA 密钥(最好是 2048 位;使用密钥时的计算开销还不错,您仍然可以每秒执行数百个签名)。

于 2011-03-08T14:19:26.073 回答
1

这个问题与 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。

谢谢,约格什

于 2012-12-14T17:16:38.177 回答