基于BouncyCastle的ECDSA Key, CSR, Certificate Demo

直接代码示例:

import com.google.common.base.Splitter;
import org.ethereum.crypto.jce.SpongyCastleProvider;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
import org.spongycastle.asn1.x509.*;
import org.spongycastle.cert.CertException;
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.X509v3CertificateBuilder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.spongycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.spongycastle.jce.ECNamedCurveTable;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.jce.spec.ECParameterSpec;
import org.spongycastle.openssl.PEMKeyPair;
import org.spongycastle.openssl.PEMParser;
import org.spongycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.spongycastle.openssl.jcajce.JcaPEMWriter;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.ContentVerifierProvider;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import org.spongycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.spongycastle.pkcs.PKCS10CertificationRequest;
import org.spongycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.spongycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.spongycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.spongycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.spongycastle.util.encoders.Base64;
import org.spongycastle.util.io.pem.PemObject;
import org.spongycastle.util.io.pem.PemWriter;

import javax.crypto.*;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.security.auth.x500.X500Principal;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Date;
import java.util.Enumeration;

public class ECDSATest {
    private static final String TEST_PWD = "1234";

    public static void main(String[] args) throws Exception {
        BouncyCastleProvider prov = new org.spongycastle.jce.provider.BouncyCastleProvider();
        Security.addProvider(prov);
        // 查看支持的Provider和Algorithm
        for (Provider provider : Security.getProviders()) {
            System.out.println("Provider: " + provider.getName() + " version: " + provider.getVersion());
            for (Provider.Service service : provider.getServices()) {
                System.out.printf("  Type : %-30s  Algorithm: %-30s\n", service.getType(), service.getAlgorithm());
            }
        }
        System.out.println("------------------------------------------------------------------------------------------\n\n");

        //生成ECDSA 公私钥
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
        KeyPairGenerator generator = KeyPairGenerator.getInstance("ECDSA", prov.getName());
        generator.initialize(ecSpec, new SecureRandom());
        KeyPair pair = generator.generateKeyPair();
        PublicKey publicKey = pair.getPublic();
        PrivateKey privateKey = pair.getPrivate();

        // extract the encoded private key, this is an unencrypted PKCS#8 private key
        byte[] encodedPrivateKey = privateKey.getEncoded();

        //输出private key(非BouncyCastle API)
        String pem = generatePem("PRIVATE KEY", encodedPrivateKey);
        System.out.println("pem:\n" + pem);
        checkRecover(privateKey, pem);

        //输出private key(BouncyCastle API)
        pem = privateKeyToPEM(privateKey);
        System.out.println("pem:\n" + pem);
        checkRecover(privateKey, pem);

        // 输出加密private key(BouncyCastle API)
        // We must use a PasswordBasedEncryption algorithm in order to encrypt the private key, you may use any common algorithm supported by openssl,
        // you can check them in the openssl documentation http://www.openssl.org/docs/apps/pkcs8.html
        String pbeAlg = "PBEWITHSHA1ANDDESEDE";
        int count = 1 << 10;// hash iteration count
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[128];
        random.nextBytes(salt);
        // Create PBE parameter set
        PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
        PBEKeySpec pbeKeySpec = new PBEKeySpec(TEST_PWD.toCharArray());
        SecretKeyFactory keyFac = SecretKeyFactory.getInstance(pbeAlg);
        SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
        Cipher pbeCipher = Cipher.getInstance(pbeAlg);
        pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
        // Encrypt the encoded Private Key with the PBE key
        byte[] ciphertext = pbeCipher.doFinal(encodedPrivateKey);
        // Now construct PKCS #8 EncryptedPrivateKeyInfo object
        AlgorithmParameters algparms = AlgorithmParameters.getInstance(pbeAlg);
        algparms.init(pbeParamSpec);
        EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(algparms, ciphertext);
        // and here we have it! a DER encoded PKCS#8 encrypted key!
        byte[] encryptedPkcs8 = encinfo.getEncoded();
        pem = generatePem("ENCRYPTED PRIVATE KEY", encryptedPkcs8);
        System.out.println("pem encrypted:\n" + pem);
        checkRecover(privateKey, pem);

        System.out.println("------------------------------------------------------------------------------------------");
        //生成ECDSA csr,并签名
        generatePKCS10CSRAndSign(publicKey, privateKey);
    }

    private static void checkRecover(PrivateKey privateKey, String pem) throws IOException {
        PrivateKey recoveredKey = privateKeyFromPEM(pem);
        System.out.println("privateKey=" + privateKey);
        System.out.println("privateKey.getAlgorithm()=" + privateKey.getAlgorithm());
        System.out.println("\nrecoveredKey=" + 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");
        }
        System.out.println("\n\n\n");
    }

    private static String privateKeyToPEM(PrivateKey privateKey) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        JcaPEMWriter pemWriter = new JcaPEMWriter(new OutputStreamWriter(bos));
        pemWriter.writeObject(privateKey);
        pemWriter.close();
        return new String(bos.toByteArray());
    }

    private static PrivateKey privateKeyFromPEM(String der) throws IOException {
        StringReader reader = new StringReader(der);
        PEMParser pemParser = new PEMParser(reader);
        try {
            Object o = pemParser.readObject();
            if (o == null) {
                throw new IOException("Not an OpenSSL key");
            }
            if (o instanceof PEMKeyPair) {
                KeyPair kp = new JcaPEMKeyConverter().setProvider("SC").getKeyPair((PEMKeyPair) o);
                return kp.getPrivate();
            } else if (o instanceof PrivateKeyInfo) {
                JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(SpongyCastleProvider.getInstance().getName());
                return converter.getPrivateKey((PrivateKeyInfo) o);
            } else if (o instanceof PKCS8EncryptedPrivateKeyInfo) {
                PKCS8EncryptedPrivateKeyInfo eki = (PKCS8EncryptedPrivateKeyInfo) o;
                return privateKeyFromEncryptedPEM(eki.getEncoded());
            }
            throw new IOException("Not an OpenSSL key" + o);
        } finally {
            pemParser.close();
        }
    }

    private static PrivateKey privateKeyFromEncryptedPEM(byte[] keyBytes) throws IOException {
        EncryptedPrivateKeyInfo encryptPKInfo = new EncryptedPrivateKeyInfo(keyBytes);
        Cipher cipher;
        try {
            cipher = Cipher.getInstance(encryptPKInfo.getAlgName());
            PBEKeySpec pbeKeySpec = new PBEKeySpec(TEST_PWD.toCharArray());
            SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptPKInfo.getAlgName());
            Key pbeKey = secFac.generateSecret(pbeKeySpec);
            AlgorithmParameters algParams = encryptPKInfo.getAlgParameters();
            cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
            KeySpec pkcs8KeySpec = encryptPKInfo.getKeySpec(cipher);
            KeyFactory kf = KeyFactory.getInstance("ECDSA");
            return kf.generatePrivate(pkcs8KeySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String generatePem(String type, byte[] encoded) {
        String code = "-----BEGIN " + type + "-----\n";
        String codenew = new String(Base64.encode((encoded)));
        String myOutput = "";
        for (String substring : Splitter.fixedLength(64).split(codenew)) {
            myOutput += substring + "\n";
        }
        code += myOutput.substring(0, myOutput.length() - 1);
        code += "\n-----END " + type + "-----";
        return new String(code.getBytes());
    }

    private static String csrToPEM(PKCS10CertificationRequest csr) throws IOException {
        PemObject pemObject = new PemObject("NEW CERTIFICATE REQUEST", csr.getEncoded());
        StringWriter stringWriter = new StringWriter();
        PemWriter pemWriter = new PemWriter(stringWriter);
        pemWriter.writeObject(pemObject);
        pemWriter.close();
        return stringWriter.toString();
    }

    private static byte[] generatePKCS10CSRAndSign(PublicKey publicKey, PrivateKey privateKey) throws Exception {
        // CSR
        PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
                new X500Principal("C=CN, ST=SC, O=CD, OU=MC, CN=test, [email protected]"), publicKey);
        JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withECDSA");
        ContentSigner signer = csBuilder.build(privateKey);
        PKCS10CertificationRequest csr = p10Builder.build(signer);

        String pem = csrToPEM(csr);
        System.out.println("csr:\n" + pem);
        System.out.println(csr.getSubjectPublicKeyInfo().getPublicKeyData());

        //加载ca公私钥
        PEMParser pemParser = new PEMParser(new StringReader("-----BEGIN EC PRIVATE KEY-----\n" +
                "MHQCAQEEIFLDMLtsZrm1WlqMo/VZ0mWM/hX+rYpQaYdeMvkJA4F1oAcGBSuBBAAK\n" +
                "oUQDQgAEVVrLKeTmtkXqDK1lrRi2/AngLSHSERQFscZzOED09W9zYxA/vqx5uxI8\n" +
                "XpN5PQ/ekvAPJAqkbfN7msw4f5U5KQ==\n" +
                "-----END EC PRIVATE KEY-----\n"));
        Object object = pemParser.readObject();
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(SpongyCastleProvider.getInstance().getName());
        PrivateKey caPrivateKey = null;
        if (object instanceof PrivateKeyInfo) {
            System.out.println("ca privateKey=\n" + converter.getPrivateKey((PrivateKeyInfo) object));
            caPrivateKey = converter.getPrivateKey((PrivateKeyInfo) object);
        } else if (object instanceof org.spongycastle.openssl.PEMKeyPair) {
            PEMKeyPair pemKeyPair = (PEMKeyPair) object;
            PrivateKeyInfo pki = pemKeyPair.getPrivateKeyInfo();
            System.out.println("ca privateKey=\n" + converter.getPrivateKey(pki));
            caPrivateKey = converter.getPrivateKey(pki);
        }
        pemParser = new PEMParser(new StringReader("-----BEGIN CERTIFICATE-----\n" +
                "MIICYzCCAgigAwIBAgIJALVT13T2E0X2MAoGCCqGSM49BAMCMIGNMQswCQYDVQQG\n" +
                "EwJDTjEQMA4GA1UECBMHQmVpSmluZzEQMA4GA1UEBxMHQmVpSmluZzETMBEGA1UE\n" +
                "ChMKY2xvdWRtaW5kczESMBAGA1UECxMJYXV0aGNoYWluMQ0wCwYDVQQDEwRkYXRh\n" +
                "MSIwIAYJKoZIhvcNAQkBFhNkYXRhQGNsb3VkbWluZHMuY29tMCAXDTE3MDIxMDAz\n" +
                "MjU1NFoYDzMwMTYwNjEzMDMyNTU0WjCBjTELMAkGA1UEBhMCQ04xEDAOBgNVBAgT\n" +
                "B0JlaUppbmcxEDAOBgNVBAcTB0JlaUppbmcxEzARBgNVBAoTCmNsb3VkbWluZHMx\n" +
                "EjAQBgNVBAsTCWF1dGhjaGFpbjENMAsGA1UEAxMEZGF0YTEiMCAGCSqGSIb3DQEJ\n" +
                "ARYTZGF0YUBjbG91ZG1pbmRzLmNvbTBWMBAGByqGSM49AgEGBSuBBAAKA0IABFVa\n" +
                "yynk5rZF6gytZa0YtvwJ4C0h0hEUBbHGczhA9PVvc2MQP76sebsSPF6TeT0P3pLw\n" +
                "DyQKpG3ze5rMOH+VOSmjUDBOMB0GA1UdDgQWBBQgbt/oOfrxgKp91uCWDLrOi5qb\n" +
                "TzAfBgNVHSMEGDAWgBQgbt/oOfrxgKp91uCWDLrOi5qbTzAMBgNVHRMEBTADAQH/\n" +
                "MAoGCCqGSM49BAMCA0kAMEYCIQDqeOeAbg5mvB7kl483aiRSlVvEUokaaZllnKKQ\n" +
                "auXZewIhAKtE1BtMJZzPjDrQE3+tJI69eSE+nTTH7cUvvIJFTvCd\n" +
                "-----END CERTIFICATE-----"));
        object = pemParser.readObject();
        X509CertificateHolder xh = (X509CertificateHolder) object;
        X509Certificate caCert = new JcaX509CertificateConverter().setProvider("SC") .getCertificate(xh);
        PublicKey caPublicKey = converter.getPublicKey(xh.getSubjectPublicKeyInfo());
        System.out.println("ca publicKey=\n" + caPublicKey);

        Date startDate = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000);
        Date endDate = new Date(System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000);
        signCertificateRequest(caCert, caPublicKey, caPrivateKey, csr, startDate, endDate, publicKey);
        return csr.getEncoded();
    }

    private static String certificateToPEM(X509Certificate certificate) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        JcaPEMWriter pemWriter = new JcaPEMWriter(new OutputStreamWriter(bos));
        pemWriter.writeObject(certificate);
        pemWriter.close();
        return new String(bos.toByteArray());
    }

    private static X509Certificate signCertificateRequest(X509Certificate caCert, PublicKey caPublicKey, PrivateKey caPrivateKey, PKCS10CertificationRequest csr,
                                                          Date notBefore, Date notAfter, PublicKey csrPublicKey)
            throws NoSuchAlgorithmException, InvalidKeyException, CertificateException, IOException, OperatorCreationException {
        JcaPKCS10CertificationRequest jcaRequest = new JcaPKCS10CertificationRequest(csr);
        X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(caCert,
                BigInteger.valueOf(System.currentTimeMillis()), notBefore, notAfter, jcaRequest.getSubject(), jcaRequest.getPublicKey());

        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
        certificateBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert))
                .addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(jcaRequest.getPublicKey()))
                .addExtension(Extension.basicConstraints, true, new BasicConstraints(false))
                .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature))
                .addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_clientAuth));

        // add pkcs extensions
        org.spongycastle.asn1.pkcs.Attribute[] attributes = csr.getAttributes();
        for (org.spongycastle.asn1.pkcs.Attribute attr : attributes) {
            // process extension request
            if (attr.getAttrType().equals(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest)) {
                Extensions extensions = Extensions.getInstance(attr.getAttrValues().getObjectAt(0));
                Enumeration e = extensions.oids();
                while (e.hasMoreElements()) {
                    ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement();
                    Extension ext = extensions.getExtension(oid);
                    certificateBuilder.addExtension(oid, ext.isCritical(), ext.getParsedValue());
                }
            }
        }

        ContentSigner signer = null;
        if (caPrivateKey instanceof org.spongycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey) {
            signer = new JcaContentSignerBuilder("SHA256withECDSA").setProvider("SC").build(caPrivateKey);
        } else {
            signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("SC").build(caPrivateKey);
        }
        X509CertificateHolder certHolder = certificateBuilder.build(signer);
        X509Certificate c = new JcaX509CertificateConverter().setProvider("SC").getCertificate(certHolder);

        System.out.println("\n" + certificateToPEM(c));
        System.out.println("-----------------------\n" + c.getPublicKey());
        System.out.println(csrPublicKey);
        System.out.println(isEqual(c.getPublicKey(), csrPublicKey));

        ContentVerifierProvider contentVerifierProvider = new JcaContentVerifierProviderBuilder()
                .setProvider("SC").build(caPublicKey);
        try {
            if (!certHolder.isSignatureValid(contentVerifierProvider)) {
                System.err.println("signature invalid");
            } else {
                System.out.println("isSignatureValid");
            }
        } catch (CertException e) {
            System.err.println("CertException");
            e.printStackTrace();
        }
        return c;
    }

    private static boolean isEqual(PublicKey publicKey1, PublicKey publicKey2) {
        byte[] pub1 = publicKey1.getEncoded();
        byte[] pub2 = publicKey2.getEncoded();
        if (pub1.length != pub2.length) {
            return false;
        }
        for (int i = 0; i < pub1.length; i++) {
            if (pub1[i] != pub2[i]) {
                return false;
            }
        }
        return true;
    }

    //签名demo
    private static X509Certificate sign(X500Principal subject, PublicKey pubKey,
                                        X500Principal issuer, PrivateKey caKey,
                                        Date begin, Date ends)
            throws GeneralSecurityException, OperatorCreationException {
        X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(issuer,
                BigInteger.valueOf(System.currentTimeMillis()), begin, ends, subject, pubKey);
        ContentSigner signer = new JcaContentSignerBuilder("SHA256withECDSA").build(caKey);//.setProvider("SC").build(caKey);
        X509CertificateHolder certHolder = certificateBuilder.build(signer);
        X509Certificate c = new JcaX509CertificateConverter().getCertificate(certHolder);//.setProvider("SC").getCertificate(certHolder);
        return c;
    }
}

猜你喜欢

转载自enuocm.iteye.com/blog/2359347