开放签电子签章系统已经上线发布了,为了让开源版能够更好的服务广大研发工程师,接下来会详细的说说开源版从代码层面的具体实现,以便大家在工作过程中更好使用开放签电子签章系统。本文主要讲开放签开源电子签章系统Java后端代码中关于数字证书生成的Java代码。

一、需要引用的jar包:

bouncycastle

java.security

二、核心代码:

(1)生成数字证书主题信息

import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Enumeration;

public class CertificateUtils {

    /**
     * 生成证书主题信息
     *
     * @param C  Country Name (国家代号),eg: CN
     * @param ST State or Province Name (洲或者省份),eg: Beijing
     * @param L  Locality Name (城市名),eg: Beijing
     * @param O  Organization Name (可以是公司名称),eg: xxxxxx网络技术有限公司
     * @param OU Organizational Unit Name (可以是单位部门名称)
     * @param CN Common Name (服务器ip或者域名或者公司名称),eg: 192.168.30.71 or www.baidu.com or xxxxxx网络技术有限公司
     * @return X500Name Subject
     */
    public static String buildSubject(String C, String ST, String L,
                                        String O, String OU, String CN) {
        X500NameBuilder x500NameBuilder = new X500NameBuilder();
        x500NameBuilder.addRDN(BCStyle.C, C);
        x500NameBuilder.addRDN(BCStyle.ST, ST);
        x500NameBuilder.addRDN(BCStyle.L, L);
        if(O != null)
            x500NameBuilder.addRDN(BCStyle.O, O);
        if(OU != null)
            x500NameBuilder.addRDN(BCStyle.OU, OU);
        x500NameBuilder.addRDN(BCStyle.CN, CN);
        System.out.println(x500NameBuilder.build().toASN1Primitive().toString());
        return x500NameBuilder.build().toString();
    }

    public static byte [] coverToPfx(byte [] jks, String password) {
        try {

            KeyStore inputKeyStore = KeyStore.getInstance("JKS");
            ByteArrayInputStream inputStream = new ByteArrayInputStream(jks);
            inputKeyStore.load(inputStream, password.toCharArray());

            KeyStore outputKeyStore = KeyStore.getInstance("PKCS12");

            outputKeyStore.load(null, password.toCharArray());

            Enumeration enums = inputKeyStore.aliases();

            while (enums.hasMoreElements()) { //   we   are   readin   just   one   certificate.

                String keyAlias = (String) enums.nextElement();

                if (inputKeyStore.isKeyEntry(keyAlias)) {
                    Key key = inputKeyStore.getKey(keyAlias, password.toCharArray());
                    Certificate[] certChain = inputKeyStore
                            .getCertificateChain(keyAlias);

                    outputKeyStore.setKeyEntry("", key, password
                            .toCharArray(), certChain);
                }
            }

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            outputKeyStore.store(out, password.toCharArray());
            out.close();
            return out.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


}

(2)数字证书生成过程信息

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;


@Data
@AllArgsConstructor
@NoArgsConstructor
public class GenerateCertificateInfo {

    /**
     * 证书密码
     */
    private String password;

    /**
     * 证书类型  JKS  PFX
     */
    private String certFileType;

    /**
     * 证书库
     */
    private byte [] jks;


    /**
     * 签名证书
     */
    private byte [] pfx;

    /**
     * 证书序列号
     */
    private String serial;
    /**
     * 证书签名算法     SHA1withRSA  SHA256withRSA
     */
    private String algorithmSignature;

    /**
     *  证书算法类型: RSA、SM2
     */
    private String algorithm;

    /**
     * 证书有效期起始时间
     */
    private Date termOfValidityStartTime;

    /**
     * 证书有效期结束时间
     */
    private Date termOfValidityEndTime;



}

java后端工具类之从0到1生成数字证书(RSA)_List(3)根证书类

import java.security.KeyStore;

public class BaseCertificateInfo {

    /**
     * 根证书
     */
    private KeyStore cert;

    /**
     * 证书别名
     */
    private String alias;

    /**
     * 证书密码
     */
    private String password;

    public KeyStore getCert() {
        return cert;
    }

    public void setCert(KeyStore cert) {
        this.cert = cert;
    }

    public String getAlias() {
        return alias;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

java后端工具类之从0到1生成数字证书(RSA)_数字证书_02(4)数字证书类型枚举

public enum CertificateType {
    RSA,SM2
}

java后端工具类之从0到1生成数字证书(RSA)_数字证书_03(5)生成数字证书

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

public class GenerateRootCertificate {

    //SHA256WITHRSA


    private String subject;

    private String algorithmSignature = "SHA256withRSA";

    private CertificateType certificateType;

    private GenerateRootCertificate(String subject,CertificateType certificateType){
        this.subject = subject;
        this.certificateType = certificateType;
    }


    /**
     * @param subject 证书主题
     *                可通过 CertificateUtils.buildSubject进行生成
     */
    public static GenerateRootCertificate instance(String subject,CertificateType certificateType){
        return new GenerateRootCertificate(subject,certificateType);
    }


    /**
     * 设置证书签名算法  SHA1withRSA  SHA256withRSA
     * @param algorithmSignature
     */
    public void setAlgorithmSignature(String algorithmSignature){
        this.algorithmSignature = algorithmSignature;
    }


    /**
     * 证书类型
     * @param password
     * @param lifespan 单位年
     *
     * @return
     */
    public GenerateCertificateInfo generateCertificate(BaseCertificateInfo root, String password, int lifespan) throws Exception {
        X500Name issuer = new X500Name(subject);
        KeyStore.PrivateKeyEntry rootPK = null;
        PrivateKey signPrivateKey = null;
        List<Certificate> chains = new ArrayList<>(5);
        if(root != null){
            rootPK = (KeyStore.PrivateKeyEntry) root.getCert().getEntry(root.getAlias(),
                    new KeyStore.PasswordProtection(root.getPassword().toCharArray()));
            X509Certificate rootCert = (X509Certificate) rootPK.getCertificate();

            issuer = X500Name.getInstance(rootCert.getSubjectX500Principal().getEncoded());

            signPrivateKey = rootPK.getPrivateKey();

            Certificate [] rootChains = rootPK.getCertificateChain();
            for (int i=0;i<rootChains.length;i++){
                chains.add(rootChains[i]);
            }
        }

        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(2048);
        KeyPair keyPair = kpg.generateKeyPair();

        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(null, null);

        Date startDate = new Date(System.currentTimeMillis());
        Date expireDate = new Date(System.currentTimeMillis() + (86400000L * 365 * lifespan));

        String serial = UUID.randomUUID().toString().replaceAll("-","");
        BigInteger serialBig = str2BigInteger(serial);

        if(signPrivateKey == null){
            signPrivateKey = keyPair.getPrivate();
        }
        Certificate cert = generateV3(issuer, new X500Name(subject),serialBig, startDate,expireDate, keyPair.getPublic(),
                signPrivateKey
                , null);

        //用户证书要放在证书链的最前面
        chains.add(0,cert);

        keyStore.setKeyEntry("root", keyPair.getPrivate(), password.toCharArray(), chains.toArray(new Certificate[chains.size()]));

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        keyStore.store(outputStream,password.toCharArray());


        GenerateCertificateInfo certificateInfo = new GenerateCertificateInfo();
        certificateInfo.setAlgorithm(certificateType.name());
        certificateInfo.setAlgorithmSignature(algorithmSignature);
        certificateInfo.setPassword(password);
        certificateInfo.setCertFileType("jks");
        certificateInfo.setSerial(serial);
        certificateInfo.setJks(outputStream.toByteArray());
        certificateInfo.setPfx(CertificateUtils.coverToPfx(certificateInfo.getJks(),certificateInfo.getPassword()));

        certificateInfo.setTermOfValidityStartTime(startDate);
        certificateInfo.setTermOfValidityEndTime(expireDate);

        return certificateInfo;
    }

    public Certificate generateV3(X500Name issuer, X500Name subject,
                                         BigInteger serial, Date startDate, Date expireDate,
                                         PublicKey publicKey, PrivateKey privKey, List<Extension> extensions) throws IOException, CertificateException, OperatorCreationException {

        X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
                issuer, serial, startDate, expireDate,
                subject, publicKey);
        ContentSigner sigGen = new JcaContentSignerBuilder(algorithmSignature).build(privKey);
        //privKey是CA的私钥,publicKey是待签名的公钥,那么生成的证书就是被CA签名的证书。
        if (extensions != null){
            for (Extension ext : extensions) {
                builder.addExtension(ext.getExtnId(), ext.isCritical(),ext.getExtnValue());
            }
        }
        X509CertificateHolder holder = builder.build(sigGen);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream is1 = new ByteArrayInputStream(holder.toASN1Structure()
                .getEncoded());
        X509Certificate theCert = (X509Certificate) cf.generateCertificate(is1);
        is1.close();
        return theCert;
    }


    public static void main(String[] args) throws Exception {
//
        KeyStore store = loadJKS("Ting111");
        BaseCertificateInfo baseCertificateInfo = new BaseCertificateInfo();
        baseCertificateInfo.setAlias("root");
        baseCertificateInfo.setCert(store);
        baseCertificateInfo.setPassword("123456");
        String x500Name = "CN=Ting222,OU=研发部,O=资源律动,L=BeiJing,ST=BeiJing,C=CN";

        GenerateCertificateInfo certificateInfo = GenerateRootCertificate.instance(x500Name,CertificateType.RSA).generateCertificate(baseCertificateInfo,"123456",10);
        FileUtils.writeByteArrayToFile(new File("C:\\Users\\Administrator\\Desktop\\tem\\cert\\Ting222.jks"), certificateInfo.getJks());

    }

    private static BigInteger str2BigInteger(String str){
        StringBuffer sb = new StringBuffer();
        //将字符串转换为字符数组
        char ch[] = str.toCharArray();
        for(int i = 0; i < ch.length; i++) {
            String hexString = Integer.toHexString(ch[i]);
            sb.append(hexString);
        }
        return new BigInteger(sb.toString());
    }

    private static String big2String(String str){
        String result = new String();
        char[] charArray = str.toString().toCharArray();
        for(int i = 0; i < charArray.length; i=i+2) {
            String st = ""+charArray[i]+""+charArray[i+1];
            char ch1 = (char)Integer.parseInt(st, 16);
            result = result + ch1;
        }
        return result;
    }



    public static KeyStore loadJKS(String name) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
        KeyStore store = KeyStore.getInstance("JKS");
        File file = new File(""+name+".jks");
        store.load(new FileInputStream(file), "123456".toCharArray());
        return store;
    }

}


java后端工具类之从0到1生成数字证书(RSA)_java_04