Java tutorial
/* * Copyright 2013-2023 Peng Li <madding.lip@gmail.com> * Licensed under the AQNote License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.aqnote.com/licenses/LICENSE-1.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.aqnote.shared.encrypt.cert.gen; import static com.aqnote.shared.encrypt.cert.bc.constant.CertConstant.MAD_CRL_URL; import static com.aqnote.shared.encrypt.cert.bc.constant.DateConstant.FIVE_YEAR; import static com.aqnote.shared.encrypt.cert.bc.constant.DateConstant.HALF_DAY; import static com.aqnote.shared.encrypt.cert.bc.constant.DateConstant.ONE_DAY; import static com.aqnote.shared.encrypt.cert.bc.constant.DateConstant.TWENTY_YEAR; import java.math.BigInteger; import java.security.KeyFactory; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.security.spec.RSAPublicKeySpec; import java.util.Date; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERIA5String; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AccessDescription; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.CRLDistPoint; import org.bouncycastle.asn1.x509.DistributionPoint; import org.bouncycastle.asn1.x509.DistributionPointName; import org.bouncycastle.asn1.x509.ExtendedKeyUsage; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.KeyPurposeId; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.CertIOException; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.crypto.params.RSAKeyParameters; import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import com.aqnote.shared.encrypt.ProviderUtil; import com.aqnote.shared.encrypt.cert.bc.constant.BCConstant; import com.aqnote.shared.encrypt.cert.bc.constant.CertConstant; import com.aqnote.shared.encrypt.cert.bc.constant.DateConstant; import com.aqnote.shared.encrypt.cert.bc.util.CertificateUtil; import com.aqnote.shared.encrypt.cert.bc.util.X500NameUtil; /** * ?,?? * * @author madding.lip */ public class BCCertGenerator implements BCConstant { private static ThreadLocal<BCCertGenerator> threadlocal = new ThreadLocal<BCCertGenerator>(); protected static final KeyPurposeId[] BASE_EKU = new KeyPurposeId[2]; protected static final KeyPurposeId[] MOST_EKU = new KeyPurposeId[5]; protected static int WHOLE_KEY_USAGE = KeyUsage.digitalSignature | KeyUsage.nonRepudiation | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment | KeyUsage.keyAgreement | KeyUsage.keyCertSign | KeyUsage.cRLSign | KeyUsage.encipherOnly | KeyUsage.decipherOnly; protected static int END_KEY_USAGE = KeyUsage.digitalSignature | KeyUsage.keyEncipherment; static { ProviderUtil.addBCProvider(); BASE_EKU[0] = KeyPurposeId.id_kp_clientAuth; BASE_EKU[1] = KeyPurposeId.id_kp_serverAuth; MOST_EKU[0] = KeyPurposeId.id_kp_clientAuth; MOST_EKU[1] = KeyPurposeId.id_kp_serverAuth; MOST_EKU[2] = KeyPurposeId.id_kp_eapOverPPP; MOST_EKU[3] = KeyPurposeId.id_kp_eapOverLAN; MOST_EKU[4] = KeyPurposeId.id_kp_ipsecIKE; // MOST_EKU[5] = KeyPurposeId.id_kp_ipsecUser; // MOST_EKU[6] = KeyPurposeId.id_kp_ipsecTunnel; // MOST_EKU[7] = KeyPurposeId.id_kp_ipsecEndSystem; } public static BCCertGenerator getIns() { if (threadlocal.get() == null) { threadlocal.set(new BCCertGenerator()); } return threadlocal.get(); } public X509Certificate createRootCaCert(final KeyPair keyPair) throws Exception { PublicKey pubKey = keyPair.getPublic(); PrivateKey privKey = keyPair.getPrivate(); X500Name idn = X500NameUtil.createRootPrincipal(); BigInteger sno = BigInteger.valueOf(1); Date nb = new Date(System.currentTimeMillis() - ONE_DAY); Date na = new Date(nb.getTime() + TWENTY_YEAR); X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(idn, sno, nb, na, idn, pubKey); addSubjectKID(certBuilder, pubKey); addAuthorityKID(certBuilder, pubKey); addCRLDistributionPoints(certBuilder); addAuthorityInfoAccess(certBuilder); certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(Boolean.TRUE)); X509Certificate certificate = signCert(certBuilder, privKey); certificate.checkValidity(new Date()); certificate.verify(pubKey); setPKCS9Info(certificate); return certificate; } public X509Certificate createClass3RootCert(KeyPair keyPair, PrivateKey ppk, X509Certificate caCert) throws Exception { X500Name idn = CertificateUtil.getSubject(caCert); BigInteger sno = BigInteger.valueOf(5); Date nb = new Date(System.currentTimeMillis() - HALF_DAY); Date na = new Date(nb.getTime() + TWENTY_YEAR); X500Name sdn = X500NameUtil.createClass3RootPrincipal(); PublicKey pubKey = keyPair.getPublic(); X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(idn, sno, nb, na, sdn, pubKey); addSubjectKID(certBuilder, pubKey); addAuthorityKID(certBuilder, caCert.getPublicKey()); certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(Boolean.TRUE)); X509Certificate certificate = signCert(certBuilder, ppk); certificate.checkValidity(new Date()); certificate.verify(caCert.getPublicKey()); setPKCS9Info(certificate); return certificate; } public X509Certificate createClass1CaCert(KeyPair keyPair, PrivateKey ppk, X509Certificate caCert) throws Exception { X500Name idn = CertificateUtil.getSubject(caCert); BigInteger sno = BigInteger.valueOf(3); Date nb = new Date(System.currentTimeMillis() - HALF_DAY); Date na = new Date(nb.getTime() + TWENTY_YEAR); X500Name sdn = X500NameUtil.createClass1RootPrincipal(); PublicKey pubKey = keyPair.getPublic(); X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(idn, sno, nb, na, sdn, pubKey); addSubjectKID(certBuilder, pubKey); addAuthorityKID(certBuilder, caCert.getPublicKey()); certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(3)); certBuilder.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(BASE_EKU)); X509Certificate certificate = signCert(certBuilder, ppk); certificate.checkValidity(new Date()); certificate.verify(caCert.getPublicKey()); setPKCS9Info(certificate); return certificate; } public X509Certificate createClass1EndCert(X500Name sdn, PublicKey pubKey, KeyPair pKeyPair) throws Exception { PublicKey pPubKey = pKeyPair.getPublic(); PrivateKey pPrivKey = pKeyPair.getPrivate(); X500Name issuer = X500NameUtil.createClass1RootPrincipal(); BigInteger sno = BigInteger.valueOf(System.currentTimeMillis()); Date nb = new Date(System.currentTimeMillis() - HALF_DAY); Date na = new Date(nb.getTime() + FIVE_YEAR); X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(issuer, sno, nb, na, sdn, pubKey); addSubjectKID(certBuilder, pubKey); addAuthorityKID(certBuilder, pPubKey); certBuilder.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(BASE_EKU)); certBuilder.addExtension(Extension.keyUsage, false, new KeyUsage(END_KEY_USAGE)); X509Certificate certificate = signCert(certBuilder, pPrivKey); certificate.checkValidity(new Date()); certificate.verify(pPubKey); setPKCS9Info(certificate); return certificate; } public X509Certificate createClass3EndCert(long sno, X500Name sdn, Map<String, String> exts, KeyPair keyPair, KeyPair pKeyPair) throws Exception { PublicKey pPubKey = pKeyPair.getPublic(); PrivateKey pPrivKey = pKeyPair.getPrivate(); X500Name idn = X500NameUtil.createClass3RootPrincipal(); BigInteger _sno = BigInteger.valueOf(sno <= 0 ? System.currentTimeMillis() : sno); Date nb = new Date(System.currentTimeMillis() - HALF_DAY); Date na = new Date(nb.getTime() + FIVE_YEAR); PublicKey pubKey = keyPair.getPublic(); X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(idn, _sno, nb, na, sdn, pubKey); addSubjectKID(certBuilder, pubKey); addAuthorityKID(certBuilder, pPubKey); certBuilder.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(MOST_EKU)); certBuilder.addExtension(Extension.keyUsage, false, new KeyUsage(END_KEY_USAGE)); if (exts != null) { Set<String> key = exts.keySet(); for (Iterator<String> it = key.iterator(); it.hasNext();) { String oid = it.next(); String value = exts.get(oid); if (!StringUtils.isBlank(value)) { certBuilder.addExtension(new ASN1ObjectIdentifier(oid), false, new DEROctetString(value.getBytes())); } } } X509Certificate certificate = signCert(certBuilder, pPrivKey); certificate.checkValidity(new Date()); certificate.verify(pPubKey); setPKCS9Info(certificate); return certificate; } public PKCS10CertificationRequest createCSR(X500Name x500Name, KeyPair keyPair) throws Exception { PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(x500Name, publicKey); JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(ALG_SIG_SHA256_RSA); ContentSigner signer = csBuilder.build(privateKey); PKCS10CertificationRequest csr = p10Builder.build(signer); return csr; } public X509Certificate signCert(PKCS10CertificationRequest pkcs10CSR, X500Name issuer, KeyPair pKeyPair) throws Exception { SubjectPublicKeyInfo pkInfo = pkcs10CSR.getSubjectPublicKeyInfo(); RSAKeyParameters rsa = (RSAKeyParameters) PublicKeyFactory.createKey(pkInfo); RSAPublicKeySpec rsaSpec = new RSAPublicKeySpec(rsa.getModulus(), rsa.getExponent()); KeyFactory kf = KeyFactory.getInstance(ALG_RSA); PublicKey publicKey = kf.generatePublic(rsaSpec); SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo(ASN1Sequence.getInstance(publicKey.getEncoded())); X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(issuer, BigInteger.valueOf(System.currentTimeMillis()), new Date(System.currentTimeMillis() - DateConstant.ONE_DAY), new Date(System.currentTimeMillis() + DateConstant.ONE_YEAR), pkcs10CSR.getSubject(), keyInfo); ContentSigner signer = new JcaContentSignerBuilder(ALG_SIG_SHA256_RSA).setProvider(JCE_PROVIDER) .build(pKeyPair.getPrivate()); X509Certificate signedCert = new JcaX509CertificateConverter().setProvider(JCE_PROVIDER) .getCertificate(certBuilder.build(signer)); signedCert.verify(pKeyPair.getPublic()); return signedCert; } private static void setPKCS9Info(X509Certificate certificate) throws Exception { // X500Name subject = CertificateUtil.getSubject(certificate); // PKCS12BagAttributeCarrier attrCarrier = (PKCS12BagAttributeCarrier) certificate; // String friendlyName = CertificateUtil.getValue(subject.getRDNs(BCStyle.CN)[0]); // attrCarrier.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString(friendlyName)); // SubjectKeyIdentifier pubKeyId = new // JcaX509ExtensionUtils().createSubjectKeyIdentifier(certificate.getPublicKey()); // attrCarrier.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, pubKeyId); } private static void addSubjectKID(X509v3CertificateBuilder certBuilder, PublicKey pubKey) throws Exception { JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pubKey)); } private static void addAuthorityKID(X509v3CertificateBuilder certBuilder, PublicKey pubKey) throws Exception { JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(pubKey)); } private static void addCRLDistributionPoints(X509v3CertificateBuilder certBuilder) throws CertIOException { DistributionPoint[] distPoints = new DistributionPoint[1]; GeneralName generalName = new GeneralName(GeneralName.uniformResourceIdentifier, MAD_CRL_URL); GeneralNames generalNames = new GeneralNames(generalName); DistributionPointName distPointOne = new DistributionPointName(generalNames); distPoints[0] = new DistributionPoint(distPointOne, null, null); certBuilder.addExtension(Extension.cRLDistributionPoints, false, new CRLDistPoint(distPoints)); } private static void addAuthorityInfoAccess(X509v3CertificateBuilder certBuilder) throws CertIOException { ASN1EncodableVector aia_ASN = new ASN1EncodableVector(); GeneralName crlName = new GeneralName(GeneralName.uniformResourceIdentifier, new DERIA5String(CertConstant.MAD_CA_URL)); AccessDescription caIssuers = new AccessDescription(AccessDescription.id_ad_caIssuers, crlName); GeneralName ocspName = new GeneralName(GeneralName.uniformResourceIdentifier, new DERIA5String(CertConstant.MAD_OCSP_URL)); AccessDescription ocsp = new AccessDescription(AccessDescription.id_ad_ocsp, ocspName); aia_ASN.add(caIssuers); aia_ASN.add(ocsp); certBuilder.addExtension(Extension.authorityInfoAccess, false, new DERSequence(aia_ASN)); } private static X509Certificate signCert(X509v3CertificateBuilder certBuilder, PrivateKey pPrivKey) throws Exception { ContentSigner signer = new JcaContentSignerBuilder(ALG_SIG_SHA256_RSA).setProvider(JCE_PROVIDER) .build(pPrivKey); return new JcaX509CertificateConverter().setProvider(JCE_PROVIDER) .getCertificate(certBuilder.build(signer)); } }