Java 使用BouncyCastle从PEM格式恢复EC私钥

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

我的应用程序以PEM格式存储私钥,现有代码适用于RSA密钥,但我尝试切换到EC密钥,出现了一个问题。密钥恢复似乎有效,恢复的密钥上的equals方法对原始密钥返回true,但原始密钥上的getAlgorithm()返回“EC”,对恢复的密钥返回“ECDSA”。算法中的差异稍后会导致问题,因为它与相应公钥的算法不匹配

我是否做错了什么,或者这是PEM解析器中的错误

下面是一个测试程序,它演示了问题:

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密钥选择正确的算法