基于BouncyCastle的ECDSA Key, CSR, Certificate Demo

本文提供了一段基于BouncyCastle库的代码示例,详细演示了如何在Android和Java平台上使用ECDSA算法生成密钥对、创建证书签名请求(CSR)以及处理证书。
摘要由CSDN通过智能技术生成
直接代码示例:

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, emailAddress=xx@xx.com"), 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;
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值