Bouncy Castle - how to get Public Key Info from JceOpenSSLPKCS8DecryptorProviderBuilder

隐身守侯 提交于 2020-06-01 08:02:17

问题


I have the following code to extract Private Key

    PEMParser parser = new PEMParser(new InputStreamReader(new ByteArrayInputStream(decoded)));
    Object object = parser.readObject();
    PEMDecryptorProvider provider = new JcePEMDecryptorProviderBuilder()
            .build(props.getProperty(KeytoolFlags.KEYPASS.name()).toCharArray());

    JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME);
    if (object instanceof PEMEncryptedKeyPair) {
        KeyPair pair = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(provider));
        return loadPublic ? pair.getPublic() : pair.getPrivate();
    } else if (object instanceof PEMKeyPair) {
        return loadPublic ? converter.getPublicKey(((PEMKeyPair) (object)).getPublicKeyInfo())
                : converter.getPrivateKey(((PEMKeyPair) (object)).getPrivateKeyInfo());
    } else {
        InputDecryptorProvider p2 = new JceOpenSSLPKCS8DecryptorProviderBuilder()
                .setProvider(BouncyCastleProvider.PROVIDER_NAME)
                .build(props.getProperty(KeytoolFlags.KEYPASS.name()).toCharArray());
        return converter.getPrivateKey(((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(p2));
    }

I would like to get the Public Key from converter when it's JceOpenSSLPKCS8DecryptorProviderBuilder. Is there any way?

Thanks,


回答1:


The simplest way, although it feels rather ugly to me, is to convert the private key 'back' to one of OpenSSL's 'legacy' forms, which PEMParser is then able to turn into a PEMKeyPair with both halves, from which the public can be selected. Otherwise, the method must be tailored depending on the key algorithm aka type, but can be more efficient which I like better. Here are both options for your consideration:

public static void SO57043669PKCS8_Public_BC (String[] args) throws Exception {
    Object p8e = new PEMParser (new FileReader (args[0])).readObject();
    // for PKCS8-encrypted result is org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo 
    PrivateKeyInfo p8i = ((PKCS8EncryptedPrivateKeyInfo)p8e).decryptPrivateKeyInfo(
            new JceOpenSSLPKCS8DecryptorProviderBuilder().build(args[1].toCharArray()) );
    // or get org.bouncycastle.asn1.pkcs.PrivateKeyInfo directly from PEMParser for PKCS8-clear
    PublicKey pub = null;
    if( args.length>=3 ){ // the simple way:
        PrivateKey prv = new JcaPEMKeyConverter().getPrivateKey(p8i);
        PemObject old = new JcaMiscPEMGenerator (prv,null).generate();
        StringWriter w1 = new StringWriter(); 
        PemWriter w2 = new PemWriter(w1);
        w2.writeObject(old); w2.close();
        Object pair = new PEMParser(new StringReader(w1.toString())).readObject();
        pub = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair)pair).getPublic();
    }else{
        ASN1ObjectIdentifier id = p8i.getPrivateKeyAlgorithm().getAlgorithm();
        PKCS8EncodedKeySpec p8s = new PKCS8EncodedKeySpec (p8i.getEncoded());
        if( id.equals(PKCSObjectIdentifiers.rsaEncryption) ){
            // the standard PKCS1 private key format for RSA redundantly includes e
            KeyFactory rfact = KeyFactory.getInstance("RSA");
            RSAPrivateCrtKey rprv = (RSAPrivateCrtKey) rfact.generatePrivate(p8s);
            // or JcaPEMKeyConverter.getPrivateKey does the same thing
            pub = /*(RSAPublicKey)*/ rfact.generatePublic(
                    new RSAPublicKeySpec (rprv.getModulus(), rprv.getPublicExponent()));
        }else if( id.equals(X9ObjectIdentifiers.id_dsa) ){
            // the apparently ad-hoc format OpenSSL uses for DSA does not include y but it can be computed
            KeyFactory dfact = KeyFactory.getInstance("DSA");
            DSAPrivateKey dprv = (DSAPrivateKey) dfact.generatePrivate(p8s);
            // or JcaPEMKeyConverter.getPrivateKey does the same thing
            BigInteger p = dprv.getParams().getP(), q = dprv.getParams().getQ(), g = dprv.getParams().getG();
            pub = /*(DSAPublicKey)*/ dfact.generatePublic (
                    new DSAPublicKeySpec(g.modPow(dprv.getX(),p), p, q, g) );
            // warning: naive computation probably vulnerable to sidechannel attack if any  
        }else if( id.equals(X9ObjectIdentifiers.id_ecPublicKey) ){
            // the SECG SEC1 format for EC private key _in PKCS8 by OpenSSL_ 
            // includes []] BITSTR(Q) (but not [0] params which is already in the PKCS8 algid)
            org.bouncycastle.asn1.sec.ECPrivateKey eprv = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(p8i.parsePrivateKey());
            byte[] eenc = new SubjectPublicKeyInfo (p8i.getPrivateKeyAlgorithm(), eprv.getPublicKey().getOctets()).getEncoded(); 
            KeyFactory efact = KeyFactory.getInstance("EC");
            pub = /*(ECPublicKey)*/ KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(eenc));
            //}else if maybe others ...
        }else throw new Exception ("unknown private key OID " + id);
    }
    System.out.println (pub.getAlgorithm() + " " + pub.getClass().getName());
}


来源:https://stackoverflow.com/questions/57043669/bouncy-castle-how-to-get-public-key-info-from-jceopensslpkcs8decryptorprovider

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!