使用Java生成PKCS#1格式的RSA密钥
当我使用Java API生成RSA密钥对时,公钥以X.509格式编码,私钥以PKCS#8格式编码。 我希望将它们编码为PKCS#1。 这可能吗? 我花了相当多的时间浏览Java文档,但还没有找到解决方案。 当我使用Java和Bouncy Castle提供程序时,结果是一样的。
以下是代码片段:
KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA","BC"); keygen.initialize(1024); KeyPair pair = keygen.generateKeyPair(); PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); byte[] privBytes = priv.getEncoded(); byte[] pubBytes = pub.getEncoded();
两个结果字节数组的格式为X.509(公共)和PKCS#8(私有)。
任何帮助将非常感激。 有一些类似的post,但没有一个真正回答我的问题。
谢谢
你需要BouncyCastle:
import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemWriter;
下面的代码片段已经过检查,发现它与Bouncy Castle 1.52一起使用。
私钥
将私钥从PKCS8转换为PKCS1:
PrivateKey priv = pair.getPrivate(); byte[] privBytes = priv.getEncoded(); PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privBytes); ASN1Encodable encodable = pkInfo.parsePrivateKey(); ASN1Primitive primitive = encodable.toASN1Primitive(); byte[] privateKeyPKCS1 = primitive.getEncoded();
将PKCS1中的私钥转换为PEM:
PemObject pemObject = new PemObject("RSA PRIVATE KEY", privateKeyPKCS1); StringWriter stringWriter = new StringWriter(); PemWriter pemWriter = new PemWriter(stringWriter); pemWriter.writeObject(pemObject); pemWriter.close(); String pemString = stringWriter.toString();
使用命令行OpenSSL检查密钥格式是否符合预期:
openssl rsa -in rsa_private_key.pem -noout -text
公钥
将公钥从X.509 SubjectPublicKeyInfo转换为PKCS1:
PublicKey pub = pair.getPublic(); byte[] pubBytes = pub.getEncoded(); SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(pubBytes); ASN1Primitive primitive = spkInfo.parsePublicKey(); byte[] publicKeyPKCS1 = primitive.getEncoded();
将PKCS1中的公钥转换为PEM:
PemObject pemObject = new PemObject("RSA PUBLIC KEY", publicKeyPKCS1); StringWriter stringWriter = new StringWriter(); PemWriter pemWriter = new PemWriter(stringWriter); pemWriter.writeObject(pemObject); pemWriter.close(); String pemString = stringWriter.toString();
使用命令行OpenSSL检查密钥格式是否符合预期:
openssl rsa -in rsa_public_key.pem -RSAPublicKey_in -noout -text
谢谢
非常感谢以下post的作者:
- https://stackoverflow.com/a/8713518/1016580
- https://stackoverflow.com/a/14052651/1016580
- https://stackoverflow.com/a/14068057/1016580
这些post包含有用但有时过时的信息(即旧版本的BouncyCastle),这有助于我构建这篇文章。
从RFC5208开始 ,PKCS#8未加密格式由PrivateKeyInfo
结构组成:
PrivateKeyInfo :: = SEQUENCE { 版本版本, privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, privateKey PrivateKey, attributes [0] IMPLICIT属性可选}
privateKey
是:
“…一个八位字符串,其内容是私钥的值。内容的解释在私钥算法的注册中定义。例如,对于RSA私钥,内容是BER编码值类型为RSAPrivateKey。“
这个RSAPrivateKey
结构只是密钥的PKCS#1编码,我们可以使用BouncyCastle提取:
// pkcs8Bytes contains PKCS#8 DER-encoded key as a byte[] PrivateKeyInfo pki = PrivateKeyInfo.getInstance(pkcs8Bytes); RSAPrivateKeyStructure pkcs1Key = RSAPrivateKeyStructure.getInstance( pki.getPrivateKey()); byte[] pkcs1Bytes = pkcs1Key.getEncoded(); // etc.
我写了一个C程序,将pkcs8私钥转换为pkcs1。 有用!
/***************************************** convert pkcs8 private key file to pkcs1 2013-1-25 Larry Wu created ****************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include
BouncyCastle框架有一个PKCS1编码器来解决这个问题: http : //www.bouncycastle.org/docs/docs1.6/index.html
我试图使用移植到BlackBerry的BountyCastle J2ME库以DER格式生成OpenSSL友好的RSA公钥,我的代码:
public void testMe() throws Exception { RSAKeyPairGenerator generator = new RSAKeyPairGenerator(); generator.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001), new SecureRandom(), 512, 80)); AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); RSAKeyParameters params = (RSAKeyParameters) keyPair.getPublic(); RSAPublicKeyStructure struct = new RSAPublicKeyStructure(params.getModulus(), params.getExponent()); SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(new AlgorithmIdentifier("1.2.840.113549.1.1.1"), struct); byte[] bytes = info.getDEREncoded(); FileOutputStream out = new FileOutputStream("/tmp/test.der"); out.write(bytes); out.flush(); out.close(); }
密钥仍然不正确:
$ openssl asn1parse -in test.der -inform DER -i 0:d=0 hl=2 l= 90 cons: SEQUENCE 2:d=1 hl=2 l= 11 cons: SEQUENCE 4:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 15:d=1 hl=2 l= 75 prim: BIT STRING
我更改了org.bouncycastle.asn1.x509.AlgorithmIdentifier
public AlgorithmIdentifier( String objectId) { this.objectId = new DERObjectIdentifier(objectId); // This line has been added this.parametersDefined = true; }
现在有了很好的关键:
$ openssl asn1parse -in test.der -inform DER -i 0:d=0 hl=2 l= 92 cons: SEQUENCE 2:d=1 hl=2 l= 13 cons: SEQUENCE 4:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 15:d=2 hl=2 l= 0 prim: NULL 17:d=1 hl=2 l= 75 prim: BIT STRING
哪个可用于加密:
$ echo "123" | openssl rsautl -pubin -inkey test.der -encrypt -keyform DER -out y $ wc -cy 64 y
我知道这是老post。 但我花了两天时间来解决这个问题,最后发现BouncyCastle可以做到这一点
ASN1Encodable
http://www.bouncycastle.org/docs/docs1.5on/org/bouncycastle/asn1/ASN1Encodable.html