小白一枚,搞这个弄得心力交瘁,希望对你们有用…
参考了大牛的实现,https://github.com/ZZMarquis/gmhelper
感谢大牛的贡献,不然我等小白真的…
pom.xml
4.0.0
com.jinhongjian.testBCgm
testBCgm-test
1.0-SNAPSHOT
org.apache.maven.plugins
maven-compiler-plugin
6
6
org.bouncycastle
bcprov-jdk15on
1.60
org.bouncycastle
bcpkix-jdk15on
1.60
java程序
程序里面包含的功能有:sm2 公私钥对的生成,x509证书的生成,私钥以及证书转成pem格式保存到文件中
这里面是生成的自签发的CA证书。我也是新手,代码不规范请见谅…
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x9.X9ECParameters;
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.*;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.io.pem.PemObject;
import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.*;
public class x509Cert {
private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
private static ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
public static final String SIGN_ALGO_SM3WITHSM2 = "SM3withSM2";
static {
if (Security.getProvider("BC") == null) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
}
public static KeyPair generateKeyPair(){
try {
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
kpGen.initialize(ecParameterSpec, new SecureRandom());
KeyPair kp = kpGen.generateKeyPair();
return kp;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static PKCS10CertificationRequest createCSR(X500Name subject, SM2PublicKey pubKey, PrivateKey priKey,String signAlgo)
throws OperatorCreationException {
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(subject, pubKey);
ContentSigner signerBuilder = new JcaContentSignerBuilder(signAlgo)
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build(priKey);
return csrBuilder.build(signerBuilder);
}
private static JcaContentSignerBuilder makeContentSignerBuilder(PublicKey issPub) throws Exception {
if (issPub.getAlgorithm().equals("EC")) {
JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(SIGN_ALGO_SM3WITHSM2);
contentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME);
return contentSignerBuilder;
}
throw new Exception("Unsupported PublicKey Algorithm:" + issPub.getAlgorithm());
}
//生成自签发ca证书
public static X509Certificate caCertGen(KeyPair keypair)throws Exception {
X500Name issuerDN = new X500Name("CN=My Application,O=My Organisation,L=My City,C=DE");
SM2PublicKey sm2SubPub = new SM2PublicKey(keypair.getPublic().getAlgorithm(),
(BCECPublicKey) keypair.getPublic());
byte[] csr = createCSR(issuerDN, sm2SubPub, keypair.getPrivate(), "SM3withSM2").getEncoded();
PKCS10CertificationRequest request = new PKCS10CertificationRequest(csr);
PublicKey subPub = BCECUtil.createPublicKeyFromSubjectPublicKeyInfo(request.getSubjectPublicKeyInfo());
PrivateKey issPriv = keypair.getPrivate();
PublicKey issPub = keypair.getPublic();
Calendar c = Calendar.getInstance();
c.add(Calendar.YEAR,1);//日期加1年
Date startDate = new Date();
Date endDate = c.getTime();
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(issuerDN, BigInteger.valueOf(System.currentTimeMillis()),
startDate, endDate, request.getSubject(), subPub);
v3CertGen.addExtension(Extension.subjectKeyIdentifier, false,
extUtils.createSubjectKeyIdentifier(SubjectPublicKeyInfo.getInstance(subPub.getEncoded())));
v3CertGen.addExtension(Extension.authorityKeyIdentifier, false,
extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(issPub.getEncoded())));
v3CertGen.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
v3CertGen.addExtension(Extension.keyUsage, false, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.dataEncipherment
| KeyUsage.keyCertSign | KeyUsage.cRLSign));
JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issPub);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME)
.getCertificate(v3CertGen.build(contentSignerBuilder.build(issPriv)));
cert.checkValidity(new Date());
cert.verify(issPub);
return cert;
}
public static String saveX509ToPemFile(X509Certificate x509Cert, String path) throws Exception {
PemObject pemCSR = new PemObject("CERTIFICATE REQUEST", x509Cert.getEncoded());
StringWriter str = new StringWriter();
JcaPEMWriter pemWriter = new JcaPEMWriter(str);
pemWriter.writeObject(pemCSR);
pemWriter.close();
str.close();
FileOutputStream certOut = new FileOutputStream(path);
certOut.write(str.toString().getBytes());
return str.toString();
}
public static void savePrivateKey(PrivateKey priv, String keyFileName) throws IOException {
// 保存private key
try {
FileOutputStream keyOut = new FileOutputStream(keyFileName);
StringBuilder sb = new StringBuilder(300);
sb.append("-----BEGIN PRIVATE KEY-----\n");
String priKey = DatatypeConverter.printBase64Binary(priv.getEncoded());
// 每64个字符输出一个换行
int LEN = priKey.length();
for (int ix = 0; ix < LEN; ++ix) {
sb.append(priKey.charAt(ix));
if ((ix + 1) % 64 == 0) {
sb.append('\n');
}
}
sb.append("\n-----END PRIVATE KEY-----\n");
keyOut.write(sb.toString().getBytes());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
// 生成公私钥对 ---------------------
KeyPair kp = generateKeyPair();
X509Certificate cert = caCertGen(kp);
System.out.println(cert);
savePrivateKey(kp.getPrivate(),"cert/privSm2.pri");
String pemCertString = saveX509ToPemFile(cert,"cert/certSm2.crt");
System.out.println(pemCertString);
}
}
生成的证书在Windows下查看是这样的:
使用golang的 tjfoc 国密实现对证书及密钥进行验证
package main
import (
"github.com/tjfoc/gmsm/sm2"
"fmt"
"log"
"reflect"
"encoding/asn1"
"math/big"
"crypto/ecdsa"
)
type dsaSignature struct {
R, S *big.Int
}
type ecdsaSignature dsaSignature
func main(){
privKey, err := sm2.ReadPrivateKeyFromPem("cert/privSm2.pri", nil) // 读取密钥
if err != nil {
log.Fatal(err)
}
pubkey1 := privKey.Public()
fmt.Println(reflect.TypeOf(pubkey1))
r,s,err :=sm2.Sm2Sign(privKey,[]byte("qwe"),nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(pubkey1)
cert, err := sm2.ReadCertificateFromPem("cert/certSm2.crt")
if err != nil {
fmt.Printf("failed to read cert file")
}
pubkey2 := cert.PublicKey.(*ecdsa.PublicKey)
fmt.Println(reflect.TypeOf(pubkey2))
sm2pubkey := sm2.PublicKey{
pubkey2.Curve,
pubkey2.X,
pubkey2.Y,
}
fmt.Println("###########################")
fmt.Println(sm2.Sm2Verify(&sm2pubkey,[]byte("qwe"),nil,r,s))
fmt.Println(pubkey2)
ecdsaSig := new(ecdsaSignature)
if _, err := asn1.Unmarshal(cert.Signature, ecdsaSig); err != nil {
log.Fatal(err)
}
//直接验证证书里的签名
fmt.Println(sm2.Sm2Verify(&sm2pubkey,cert.RawTBSCertificate,nil,ecdsaSig.R,ecdsaSig.S))
err = cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature)
if err != nil {
log.Fatal(err)
} else {
fmt.Printf("CheckSignature ok\n")
}
}
这一步的目的就是看看这证书到底生成的对不对了…目前验证的是私钥中提出的公钥可以验证证书里的签名成功,证书自己验证也可以成功