我的应用程序以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
答案 0 :(得分:5)
问题不在于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 devs可能选择ECDSA作为最合适的选择。您可以使用JcaPEMKeyConverter
执行与KeyFactory
类似的操作,但为EC键选择正确的算法。