[英]EC private key recovery from PEM format with BouncyCastle
My application stores private keys in PEM format, the existing code works for RSA keys but I am trying to switch to EC keys and there is a problem. 我的应用程序以PEM格式存储私钥,现有代码适用于RSA密钥但我试图切换到EC密钥并且存在问题。 The key recovery seems to work, and the equals method on the recovered key returns true for the original key, but getAlgorithm() on the original key returns "EC" and on the recovered key "ECDSA".
密钥恢复似乎有效,并且恢复密钥上的equals方法对原始密钥返回true,但原始密钥上的getAlgorithm()返回“EC”并返回恢复密钥“ECDSA”。 The discrepancy in the algorithm later causes problems because it does not match the algorithm for the corresponding public key.
算法中的差异会导致问题,因为它与相应公钥的算法不匹配。
Am I doing something wrong or is this a bug in the PEM parser? 我做错了什么或者这是PEM解析器中的错误?
Here is a test program which demonstrates the problem: 这是一个演示问题的测试程序:
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();
}
}
}
The output from the test program is: 测试程序的输出是:
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
The problem is not the PEMParser
but JcaPEMKeyConverter
which treats EC keys as keys for 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);
...
The algorithm identifier is id-ecPublicKey, which is also used for ECDSA keys, so the algorithm selection is not unique here and probably the BC devs have chosen ECDSA as the most suitable choice. 算法标识符是id-ecPublicKey,它也用于ECDSA密钥,因此算法选择在这里并不是唯一的,并且BC devs可能选择ECDSA作为最合适的选择。 You could do something similar like
JcaPEMKeyConverter
with you own KeyFactory
but choose your correct algorithm for EC keys. 您可以使用自己的
KeyFactory
执行类似JcaPEMKeyConverter
,但为EC键选择正确的算法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.