天天看點

【SM2證書】簽發SM2根證書 (精簡、直接)

0.程式員專用開頭:

1.項目結構:

【SM2證書】簽發SM2根證書 (精簡、直接)

2. 源碼:

AlgSm2Demo.java  (簽名驗簽)

package alg.sm2demo;


import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.ECGenParameterSpec;

/**
 * SM2 算法 Demo
 *
 * @author Cliven
 * @date 2018-12-20 10:42:22
 */
public class AlgSm2Demo {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, SignatureException {
        // 擷取SM2橢圓曲線的參數
        final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
        // 擷取一個橢圓曲線類型的密鑰對生成器
        final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
        // 使用SM2參數初始化生成器
        kpg.initialize(sm2Spec);

        // 擷取密鑰對
        KeyPair keyPair = kpg.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        // ------------------------ SM2未壓縮公鑰 ----------------------------
        // 橢圓曲線公鑰的點坐标
        ECPoint pubKeyPointQ = ((BCECPublicKey) publicKey).getQ();
        System.out.println("X: \n" + pubKeyPointQ.getXCoord());
        System.out.println("Y: \n" + pubKeyPointQ.getYCoord());
        // 将其表示為SM2未壓縮的公鑰為
        System.out.println("SM2 public key: \n"
                + "04"
                + pubKeyPointQ.getXCoord().toString()
                + pubKeyPointQ.getYCoord().toString()
        );
        // ------------------------ SM2未壓縮公鑰 -------------------------------

        System.out.println("Public key: \n" + Hex.toHexString(publicKey.getEncoded()));
        System.out.println("Private key: \n" + Hex.toHexString(privateKey.getEncoded()));

        // 生成SM2sign with sm3 簽名驗簽算法執行個體
        Signature signature = Signature.getInstance("SM3withSm2", new BouncyCastleProvider());

        /*
         * 簽名
         */
        // 簽名需要使用私鑰,使用私鑰 初始化簽名執行個體
        signature.initSign(privateKey);
        // 簽名原文
        byte[] plainText = "你好".getBytes(StandardCharsets.UTF_8);
        // 寫入簽名原文到算法中
        signature.update(plainText);
        // 計算簽名值
        byte[] signatureValue = signature.sign();
        System.out.println("signature: \n" + Hex.toHexString(signatureValue));

        /*
         * 驗簽
         */
        // 簽名需要使用公鑰,使用公鑰 初始化簽名執行個體
        signature.initVerify(publicKey);
        // 寫入待驗簽的簽名原文到算法中
        signature.update(plainText);
        // 驗簽
        System.out.println("Signature verify result: " + signature.verify(signatureValue));
    }
}
           

SM2CertDemo.java  (簽發證書)

package alg.sm2demo;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
import org.bouncycastle.asn1.misc.NetscapeCertType;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.X509KeyUsage;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.encoders.Base64;

/**
 * SM2 X.509簽名制作Demo
 *
 * @author jy
 * @date 2018-12-21 14:04
 */
public class SM2CertDemo {

	/**
	 * BouncyCastle算法提供者
	 */
	private static final Provider BC = new BouncyCastleProvider();

	/**
	 * 擷取擴充密鑰用途
	 *
	 * @return 增強密鑰用法ASN.1對象
	 * @author Cliven
	 * @date 2018-12-21 16:04:58
	 */
	public static DERSequence extendedKeyUsage() {
		// // 用戶端身份認證
		// ASN1ObjectIdentifier clientAuth = new
		// ASN1ObjectIdentifier("1.3.6.1.5.5.7.3.2");
		// // 安全電子郵件
		// ASN1ObjectIdentifier emailProtection = new
		// ASN1ObjectIdentifier("1.3.6.1.5.5.7.3.4");
		// 構造容器對象
		ASN1EncodableVector vector = new ASN1EncodableVector();
		// 用戶端身份認證
		vector.add(KeyPurposeId.id_kp_clientAuth);
		// 安全電子郵件
		vector.add(KeyPurposeId.id_kp_emailProtection);
		return new DERSequence(vector);
	}

	/**
	 * 生成證書檔案
	 *
	 * @param x509Certificate
	 *            X.509格式證書
	 * @param savePath
	 *            證書儲存路徑
	 * @throws CertificateEncodingException
	 * @throws IOException
	 * @author Cliven
	 * @date 2018-12-21 17:21:50
	 */
	public static void makeCertFile(X509Certificate x509Certificate, Path savePath)
			throws CertificateEncodingException, IOException {
		if (Files.exists(savePath)) {
			// 删除已存在檔案
			Files.deleteIfExists(savePath);
		}
		// 建立檔案
		Files.createFile(savePath);

		// 擷取ASN.1編碼的證書位元組碼
		byte[] asn1BinCert = x509Certificate.getEncoded();
		// 編碼為BASE64 便于傳輸
		byte[] base64EncodedCert = Base64.encode(asn1BinCert);
		// 寫入檔案
		Files.write(savePath, base64EncodedCert);
	}

	public static void main(String[] args) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException,
			OperatorCreationException, IOException, CertificateException {
		// 生成密鑰生成器, 産生密鑰對
		KeyPairGenerator keyPairGenerator = SM2KeyGenerateFactory.generator();
		KeyPair keyPair = keyPairGenerator.generateKeyPair();

		// 證書簽名實作類 附加了 SM3WITHSM2 和 PrivateKey
		JcaContentSignerBuilder jcaContentSB = new JcaContentSignerBuilder("SM3withSM2");
		jcaContentSB.setProvider(new BouncyCastleProvider());
		ContentSigner sigGen = jcaContentSB.build(keyPair.getPrivate());

		// 準備證書資訊
		BigInteger sn = BigInteger.valueOf(System.currentTimeMillis());
		X500Name issuer = createX500Name("yan");
		X500Name subject = createX500Name("yan");
		Date notBefore = new Date();
		Date notAfter = new Date(System.currentTimeMillis() + 50 * 1000);
		PublicKey publickey = keyPair.getPublic();

		// 構造證書資訊
		JcaX509v3CertificateBuilder jcaX509v3Cert = new JcaX509v3CertificateBuilder(issuer, sn, notBefore, notAfter,
				subject, publickey);
		jcaX509v3Cert.addExtension(Extension.keyUsage, false,
				new X509KeyUsage(X509KeyUsage.digitalSignature | X509KeyUsage.nonRepudiation));
		jcaX509v3Cert.addExtension(Extension.extendedKeyUsage, false, extendedKeyUsage());
		jcaX509v3Cert.addExtension(MiscObjectIdentifiers.netscapeCertType, false,
				new NetscapeCertType(NetscapeCertType.sslClient));

		// 構造X.509 第3版的證書建構者
		X509v3CertificateBuilder x509v3Cert = jcaX509v3Cert;

		// 将證書構造參數裝換為X.509證書對象
		X509Certificate certificate = new JcaX509CertificateConverter().setProvider(BC)
				.getCertificate(x509v3Cert.build(sigGen));

		// 儲存為證書檔案
		makeCertFile(certificate, Paths.get("sm2_jy.cer"));
		System.out.println("gen cert succ!");
	}

	// 構造 主題名稱
	public static X500Name createX500Name(String cn) {
		X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
		// 國家代碼
		builder.addRDN(BCStyle.C, "CN");
		// 組織
		builder.addRDN(BCStyle.O, "info");
		// 省份
		builder.addRDN(BCStyle.ST, "beijing");
		// 地區
		builder.addRDN(BCStyle.L, "beijing");
		// 身份
		builder.addRDN(BCStyle.CN, cn);

		X500Name subject = builder.build();

		return subject;
	}

}
           

SM2KeyGenerateFactory.java (産生SM2 密鑰對)

package alg.sm2demo;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.spec.ECGenParameterSpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * SM2密鑰對生成器
 *
 * @author Cliven
 * @date 2018-12-21 14:05
 */
public class SM2KeyGenerateFactory {
    /**
     * 擷取SM2密鑰對生成器
     *
     * @return SM2密鑰對生成器
     * @throws NoSuchAlgorithmException
     * @author Cliven
     * @date 2019-6-10 15:56:36
     */
    public static KeyPairGenerator generator() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
/*
        // 擷取SM2 橢圓曲線推薦參數
        X9ECParameters ecParameters = GMNamedCurves.getByName("sm2p256v1");
       // 構造EC 算法參數
        ECNamedCurveParameterSpec sm2Spec = new ECNamedCurveParameterSpec(
                // 設定SM2 算法的 OID
                GMObjectIdentifiers.sm2p256v1.toString()
                // 設定曲線方程
                , ecParameters.getCurve()
                // 橢圓曲線G點
                , ecParameters.getG()
                // 大整數N
                , ecParameters.getN());
        // 建立 密鑰對生成器
        final KeyPairGenerator gen = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
        // 使用SM2的算法區域初始化密鑰生成器
        kpg.initialize(sm2Spec, new SecureRandom());
        return gen;
*/
        /*
         * 上面過程為原始構造過程參考,BC已經為我們封裝好了
         * 隻需要下面三行代碼即可
         */
        // 擷取SM2橢圓曲線的參數
        final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
        // 擷取一個橢圓曲線類型的密鑰對生成器
        final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
        // 使用SM2參數初始化生成器
        kpg.initialize(sm2Spec);
        return kpg;

    }
}
           

執行 SM2CertDemo 的 main 方法即可,産生 SM2 證書。

3. 效果:

【SM2證書】簽發SM2根證書 (精簡、直接)
【SM2證書】簽發SM2根證書 (精簡、直接)

繼續閱讀