Java 使用BouncyCastle从PEM格式恢复EC私钥
我的应用程序以PEM格式存储私钥,现有代码适用于RSA密钥,但我尝试切换到EC密钥,出现了一个问题。密钥恢复似乎有效,恢复的密钥上的equals方法对原始密钥返回true,但原始密钥上的getAlgorithm()返回“EC”,对恢复的密钥返回“ECDSA”。算法中的差异稍后会导致问题,因为它与相应公钥的算法不匹配 我是否做错了什么,或者这是PEM解析器中的错误 下面是一个测试程序,它演示了问题:Java 使用BouncyCastle从PEM格式恢复EC私钥,java,bouncycastle,private-key,pem,elliptic-curve,Java,Bouncycastle,Private Key,Pem,Elliptic Curve,我的应用程序以PEM格式存储私钥,现有代码适用于RSA密钥,但我尝试切换到EC密钥,出现了一个问题。密钥恢复似乎有效,恢复的密钥上的equals方法对原始密钥返回true,但原始密钥上的getAlgorithm()返回“EC”,对恢复的密钥返回“ECDSA”。算法中的差异稍后会导致问题,因为它与相应公钥的算法不匹配 我是否做错了什么,或者这是PEM解析器中的错误 下面是一个测试程序,它演示了问题: import java.io.ByteArrayOutputStream; import java
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.immutify.janus.keytool.KeyToolUtils;
public class TestPrivateKeyRecovery
{
private static final String KEY_ALGORITHM = "EC";
private static final String SIGNATURE_ALGORITHM = "SHA512withECDSA";
private static final String PROVIDER = "BC";
private static final String CURVE_NAME = "secp521r1";
private static final String WRAPPING_CIPHER_SPEC = "ECIESwithAES";
private ECGenParameterSpec ecGenSpec;
private KeyPairGenerator keyGen_;
private SecureRandom rand_;
public void run()
{
try
{
rand_ = new SecureRandom();
ecGenSpec = new ECGenParameterSpec(CURVE_NAME);
keyGen_ = KeyPairGenerator.getInstance(KEY_ALGORITHM, PROVIDER);
keyGen_.initialize(ecGenSpec, rand_);
PrivateKey privateKey = keyGen_.generateKeyPair().getPrivate();
String der = privateKeyToDER(privateKey);
PrivateKey recoveredKey = privateKeyFromDER(der);
System.out.println("privateKey=" + privateKey);
System.out.println("privateKey.getAlgorithm()=" + privateKey.getAlgorithm());
System.out.println("der=" + der);
System.out.println("recoveredKey=" + privateKey);
System.out.println("recoveredKey.getAlgorithm()=" + recoveredKey.getAlgorithm());
System.out.println();
if(privateKey.equals(recoveredKey))
System.out.println("Key recovery ok");
else
System.err.println("Private key recovery failed");
if(privateKey.getAlgorithm().equals(recoveredKey.getAlgorithm()))
System.out.println("Key algorithm ok");
else
System.err.println("Key algorithms do not match");
}
catch(Exception e)
{
e.printStackTrace();
}
}
public static String privateKeyToDER(PrivateKey key) throws IOException
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PEMWriter pemWriter = new PEMWriter(new OutputStreamWriter(bos));
pemWriter.writeObject(key);
pemWriter.close();
return new String(bos.toByteArray());
}
public static PrivateKey privateKeyFromDER(String der) throws IOException
{
StringReader reader = new StringReader(der);
PEMParser pemParser = new PEMParser(reader);
try
{
Object o = pemParser.readObject();
if (o == null || !(o instanceof PEMKeyPair))
{
throw new IOException("Not an OpenSSL key");
}
KeyPair kp = new JcaPEMKeyConverter().setProvider("BC").getKeyPair((PEMKeyPair)o);
return kp.getPrivate();
}
finally
{
pemParser.close();
}
}
}
测试程序的输出为:
privateKey=EC Private Key
S: 13d19928468d14fabb9235a81fc1ec706ff5413a70a760b63e07d45a5d04a2f18425ef735500190291aacaf58c92306acd87fa01a47d907d5d3fc01531180353146
privateKey.getAlgorithm()=EC
der=-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBPRmShGjRT6u5I1qB/B7HBv9UE6cKdgtj4H1FpdBKLxhCXvc1UAGQ
KRqsr1jJIwas2H+gGkfZB9XT/AFTEYA1MUagBwYFK4EEACOhgYkDgYYABAFN5ZcE
zg9fV13u57ffwyN9bm9Wa9Pe0MtL2cd5CW2ku4mWzgS5m8IfNMAw2QMah5Z9fuXW
1fGJgUx1RsC09R6legFTgymlbqt+CaPhNsJkr12cjyzhT1NxR6uEzMUtBcYxqLHy
ANkhHmvAk221//YIRIWix7ZlRsRrs+iYrpWw4bMt9A==
-----END EC PRIVATE KEY-----
recoveredKey=EC Private Key
S: 13d19928468d14fabb9235a81fc1ec706ff5413a70a760b63e07d45a5d04a2f18425ef735500190291aacaf58c92306acd87fa01a47d907d5d3fc01531180353146
recoveredKey.getAlgorithm()=ECDSA
Key recovery ok
Key algorithms do not match
私钥=EC私钥
S:13D19928468D14B9235A81FC1EC706FF5413A70A760B63E07D45A5D04A2F18425EF735500190291ACACF58C92306ACD87FA01A47D907D5D3FC01531180353146
privateKey.getAlgorithm()=EC
der=----开始EC私钥-----
MIHCAGEBEBEBPRMSHGJRT6U5I1QB/B7HBv9UE6cKdgtj4H1FpdBKLxhCXvc1UAGQ
KRqsr1jJIwas2H+gGkfZB9XT/AFTEYA1MUAGBWYFK4EEACHOGYKDGYYABFN5ZCE
zg9fV13u57ffwyN9bm9Wa9Pe0MtL2cd5CW2ku4mWzgS5m8IfNMAw2QMah5Z9fuXW
1FGGUX1RSC09R6EGFTGYMLBQT+CaPhNsJkr12cjyzhT1NxR6uEzMUtBcYxqLHy
ANkhHmvAk221//YIRIWix7ZlRsRrs+iYrpWw4bMt9A==
-----结束EC私钥-----
recoveredKey=EC私钥
S:13D19928468D14B9235A81FC1EC706FF5413A70A760B63E07D45A5D04A2F18425EF735500190291ACACF58C92306ACD87FA01A47D907D5D3FC01531180353146
recoveredKey.getAlgorithm()=ECDSA
密钥恢复正常
关键算法不匹配
问题不在于
PEMParser
而是JcaPEMKeyConverter
,它将EC密钥视为ECDSA的密钥:
algorithms.put(X9ObjectIdentifiers.id_ecPublicKey, "ECDSA");
...
private KeyFactory getKeyFactory(AlgorithmIdentifier algId)
throws NoSuchAlgorithmException, NoSuchProviderException
{
ASN1ObjectIdentifier algorithm = algId.getAlgorithm();
String algName = (String)algorithms.get(algorithm);
...
算法标识符是id ecPublicKey,它也用于ECDSA密钥,因此这里的算法选择不是唯一的,可能BC开发人员已经选择ECDSA作为最合适的选择。您可以使用自己的KeyFactory
执行类似的操作,如JcaPEMKeyConverter
,但要为EC密钥选择正确的算法