1、背景
go的官方库已经支持标密rsa证书相关的功能,对于国密证书也是能在github上找到对应的库:https://github.com/tjfoc/gmsm,下面会给出一个如何解析国密pfx文件的例子。
2、代码示例
【1】解析国密pfx
import (
"encoding/pem"
"GoTest/comm/logger"
gmpkcs12 "github.com/tjfoc/gmsm/pkcs12"
"go.uber.org/zap"
)
// Sm2PfxToPem
//
// @Description: 将国密SM2算法的pfx文件解析成pem格式的证书和私钥
// @param pfxFile 要解析的国密pfx文件
// @param pwd pfx文件密码
// @return certPem pem格式的证书
// @return priKeyPem pem格式的私钥
// @return err
func Sm2PfxToPem(pfxFile, pwd string) (certPem, priKeyPem string, err error) {
cert, priKey, err := gmpkcs12.SM2P12Decrypt(pfxFile, pwd)
if err != nil {
logger.Error("sm2 p12 decrypt error", zap.Error(err))
return
}
//证书转换成PEM
certPem = string(pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
}))
//私钥转换为Pem
PriKeyBytes, err := gmpkcs12.MarshalECPrivateKey(priKey)
if err != nil {
logger.Error("marshal ec private key error", zap.Error(err))
return
}
priKeyPem = string(pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: PriKeyBytes,
}))
return
}
【2】单元测试
import "testing"
var pfxFile = "D:\\GoTest\\certs_demo\\gm_cert\\gm_test.pfx"
func TestSm2PfxToPem(t *testing.T) {
certPem, priKeyPem, err := Sm2PfxToPem(pfxFile, "123456")
if err != nil {
t.Error("sm2 pfx to pem error", err)
return
}
t.Log("cert:", certPem, "\nkey:", priKeyPem)
}
【3】执行结果
$ go test -v -run TestSm2PfxToPem
=== RUN TestSm2PfxToPem
gm_test.go:14: cert: -----BEGIN CERTIFICATE-----
MIICoDCCAkSgAwIBAgIQahCmCRXqRbvlycjsKT90hzAMBggqgRzPVQGDdQUAMHMx
CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdKaWFuZ1hpMREwDwYDVQQHDAhOYW5DaGFu
ZzENMAsGA1UECgwESlhDQTEcMBoGA1UECwwTSmlhbmdYaSBST09UIENBIFNNMjES
MBAGA1UEAwwJSlhDQV9URVNUMB4XDTIxMTEyMjA5MjQxM1oXDTIyMTEyMjA5MjQx
M1owgZExCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlKaW5nMRAwDgYDVQQHDAdC
ZWlKaW5nMRAwDgYDVQQKDAdxaWFueGluMRAwDgYDVQQLDAdxaWFueGluMSIwIAYJ
KoZIhvcNAQkBFhNxaWFueGluQFFJQU5YSU4uY29tMRYwFAYDVQQDDA1sZWdlbmRz
ZWMuY29tMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyAV//1n86fjrh/KaYTqM
c3DzBXZvpczXlfFXQAypONe1NZ58+Fla543r1aw/f4MQU4uQvuPSYdvVehB4Eeqf
36OBmDCBlTAfBgNVHSMEGDAWgBQIaDOhbxNSRPXR6AR5mzFzpUBCRDARBglghkgB
hvhCAQEEBAMCBkAwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovLzExMS43NS4xOTgu
MjQzL2NybDMwLmNybDAOBgNVHQ8BAf8EBAMCBsAwHQYDVR0OBBYEFLsQOV9YrYxc
YgbFspkIQkBo2o0qMAwGCCqBHM9VAYN1BQADSAAwRQIhAOMIVDyXYBuu5pocBBWr
czZMBskLU4Lf4qsYvmhY/La7AiB+CSGkZsgS9GMySU5Kn1E+KD114rBquYJ7EgBX
cZAugg==
-----END CERTIFICATE-----
key: -----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJp5iN8Jb1PiUQC98+Ffp5on2LdeX4yl128YctbFfofxoAoGCCqBHM9V
AYItoUQDQgAEyAV//1n86fjrh/KaYTqMc3DzBXZvpczXlfFXQAypONe1NZ58+Fla
543r1aw/f4MQU4uQvuPSYdvVehB4Eeqf3w==
-----END EC PRIVATE KEY-----
--- PASS: TestSm2PfxToPem (0.00s)
PASS
ok GoTest/certs_demo/gm_cert/gm 1.709s
3、注意事项
对于包含多个证书和一个私钥的国密pfx文件,SM2P12Decrypt函数返回的证书是取解析到的第一个,可能并不是我们想要的证书,源码如下:
func SM2P12Decrypt(fileName string, pwd string) (*x.Certificate, *sm2.PrivateKey, error) {
pfxData, _ := ioutil.ReadFile(fileName)
pv, cer, err := DecodeAll(pfxData, pwd) //直接使用这个函数获取所有证书
if err != nil {
return nil, nil, err
}
switch k := pv.(type) {
case *ecdsa.PrivateKey:
switch k.Curve {
case sm2.P256Sm2():
sm2pub := &sm2.PublicKey{
Curve: k.Curve,
X: k.X,
Y: k.Y,
}
sm2Pri := &sm2.PrivateKey{
PublicKey: *sm2pub,
D: k.D,
}
if !k.IsOnCurve(k.X,k.Y) {
return nil, nil, errors.New("error while validating SM2 private key: %v")
}
return cer[0], sm2Pri, nil //返回第一个证书
}
default:
return nil, nil, errors.New("unexpected type for p12 private key")
}
return nil,nil,nil
}
因此我们也可以直接使用DecodeAll函数来获取私钥和所有的证书,再通过证书里的颁发者和主题名等其它字段来获取我们想要的业务证书和ca证书。