0.程式員專用開頭:
1.項目結構:
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. 效果: