我的应用程序以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
解决方法
问题不是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作为最合适的选择.您可以使用自己的KeyFactory执行类似JcaPEMKeyConverter的操作,但为EC键选择正确的算法.