mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-26 12:16:20 +08:00
add cfca sm2 key and certificate parse
This commit is contained in:
parent
e7f1b45acf
commit
5d46f47c15
124
cfca/pkcs12_sm2.go
Normal file
124
cfca/pkcs12_sm2.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Package cfca handles cfca issued key and certificate
|
||||||
|
package cfca
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/emmansun/gmsm/kdf"
|
||||||
|
"github.com/emmansun/gmsm/padding"
|
||||||
|
"github.com/emmansun/gmsm/pkcs"
|
||||||
|
"github.com/emmansun/gmsm/sm2"
|
||||||
|
"github.com/emmansun/gmsm/sm3"
|
||||||
|
"github.com/emmansun/gmsm/sm4"
|
||||||
|
"github.com/emmansun/gmsm/smx509"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CFCA私有格式,在SADK中把它定义为PKCS12_SM2
|
||||||
|
|
||||||
|
type cfcaKeyPairData struct {
|
||||||
|
Version int `asn1:"default:1"`
|
||||||
|
EncryptedKey keyData
|
||||||
|
Certificate certData
|
||||||
|
}
|
||||||
|
|
||||||
|
// 被加密的私钥数据
|
||||||
|
type keyData struct {
|
||||||
|
ContentType asn1.ObjectIdentifier
|
||||||
|
Algorithm asn1.ObjectIdentifier
|
||||||
|
EncryptedContent asn1.RawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对应的证书
|
||||||
|
type certData struct {
|
||||||
|
ContentType asn1.ObjectIdentifier
|
||||||
|
Content asn1.RawContent
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
oidSM2Data = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 2, 1}
|
||||||
|
oidSM4 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104} // SADK中认为这就是SM4_CBC,不知道是不是历史原因
|
||||||
|
oidSM4CBC = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 2}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseSM2 parses the der data, returns private key and related certificate, it's CFCA private structure.
|
||||||
|
func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, error) {
|
||||||
|
var keys cfcaKeyPairData
|
||||||
|
if _, err := asn1.Unmarshal(data, &keys); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if !keys.Certificate.ContentType.Equal(oidSM2Data) {
|
||||||
|
return nil, nil, fmt.Errorf("cfca: unsupported content type oid <%v>", keys.Certificate.ContentType)
|
||||||
|
}
|
||||||
|
if !keys.EncryptedKey.ContentType.Equal(oidSM2Data) {
|
||||||
|
return nil, nil, fmt.Errorf("cfca: unsupported content type oid <%v>", keys.EncryptedKey.ContentType)
|
||||||
|
}
|
||||||
|
if !keys.EncryptedKey.Algorithm.Equal(oidSM4) && !keys.EncryptedKey.Algorithm.Equal(oidSM4CBC) {
|
||||||
|
return nil, nil, fmt.Errorf("cfca: unsupported algorithm <%v>", keys.EncryptedKey.Algorithm)
|
||||||
|
}
|
||||||
|
ivkey := kdf.Kdf(sm3.New(), password, 32)
|
||||||
|
marshalledIV, err := asn1.Marshal(ivkey[:16])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
pk, err := pkcs.SM4CBC.Decrypt(ivkey[16:], &asn1.RawValue{FullBytes: marshalledIV}, keys.EncryptedKey.EncryptedContent.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
d := new(big.Int).SetBytes(pk) // here we do NOT check if the d is in (0, N) or not
|
||||||
|
// Create private key from *big.Int
|
||||||
|
prvKey := new(sm2.PrivateKey)
|
||||||
|
prvKey.Curve = sm2.P256()
|
||||||
|
prvKey.D = d
|
||||||
|
prvKey.PublicKey.X, prvKey.PublicKey.Y = prvKey.ScalarBaseMult(prvKey.D.Bytes())
|
||||||
|
|
||||||
|
cert, err := smx509.ParseCertificate(keys.Certificate.Content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !prvKey.PublicKey.Equal(cert.PublicKey) {
|
||||||
|
return nil, nil, errors.New("cfca: public key and private key do not match")
|
||||||
|
}
|
||||||
|
return prvKey, cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalSM2 encodes sm2 private key and related certificate to cfca defined format
|
||||||
|
func MarshalSM2(password []byte, key *sm2.PrivateKey, cert *smx509.Certificate) ([]byte, error) {
|
||||||
|
if len(password) == 0 {
|
||||||
|
return nil, errors.New("cfca: invalid password")
|
||||||
|
}
|
||||||
|
ivkey := kdf.Kdf(sm3.New(), password, 32)
|
||||||
|
block, err := sm4.NewCipher(ivkey[16:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mode := cipher.NewCBCEncrypter(block, ivkey[:16])
|
||||||
|
pkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize()))
|
||||||
|
plainText := pkcs7.Pad(key.D.Bytes())
|
||||||
|
ciphertext := make([]byte, len(plainText))
|
||||||
|
mode.CryptBlocks(ciphertext, plainText)
|
||||||
|
|
||||||
|
ciphertext, err = asn1.Marshal(ciphertext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := cfcaKeyPairData{
|
||||||
|
Version: 1,
|
||||||
|
EncryptedKey: keyData{
|
||||||
|
ContentType: oidSM2Data,
|
||||||
|
Algorithm: oidSM4,
|
||||||
|
EncryptedContent: asn1.RawValue{FullBytes: ciphertext},
|
||||||
|
},
|
||||||
|
Certificate: certData{
|
||||||
|
ContentType: oidSM2Data,
|
||||||
|
Content: cert.Raw,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return asn1.Marshal(keys)
|
||||||
|
}
|
94
cfca/pkcs12_sm2_test.go
Normal file
94
cfca/pkcs12_sm2_test.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package cfca
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/emmansun/gmsm/sm2"
|
||||||
|
"github.com/emmansun/gmsm/smx509"
|
||||||
|
)
|
||||||
|
|
||||||
|
var v2exKeyPem = `-----BEGIN CFCA KEY-----
|
||||||
|
MIIDSQIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDDkLvKllj9ZWhaKU6MSnxBBV5yaF3tEcOk1
|
||||||
|
vQniWyVzyaQA4F3j/YvDJwEoE8gOF/swggL5BgoqgRzPVQYBBAIBBIIC6TCCAuUwggKJoAMCAQIC
|
||||||
|
BRBAmQgJMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
|
||||||
|
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0Ex
|
||||||
|
MB4XDTIwMTExOTA4MzExOFoXDTI1MTExOTA4MzExOFowgYkxCzAJBgNVBAYTAkNOMRcwFQYDVQQK
|
||||||
|
DA5DRkNBIFRFU1QgT0NBMTENMAsGA1UECwwEUFNCQzEZMBcGA1UECwwQT3JnYW5pemF0aW9uYWwt
|
||||||
|
MjE3MDUGA1UEAwwuMDUxQOmCruWCqOe6v+S4iuaUtuWNleWVhuaIt0BONTEwMTEzMDAwMTg4NzhA
|
||||||
|
MTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABJVRC63OKfcL4H324rDOdb4SSlbAjoJDXnK0qmwX
|
||||||
|
Z59FWmiSqt3ipreljKew4QynjTgR/yfp9yjNgNU8G5pkYdujggEGMIIBAjAfBgNVHSMEGDAWgBRr
|
||||||
|
/hjaj0I6prhtsy6Igzo0osEw4TAMBgNVHRMBAf8EAjAAMEgGA1UdIARBMD8wPQYIYIEchu8qAQEw
|
||||||
|
MTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wOQYDVR0f
|
||||||
|
BDIwMDAuoCygKoYoaHR0cDovL3VjcmwuY2ZjYS5jb20uY24vU00yL2NybDE0MzU2LmNybDAOBgNV
|
||||||
|
HQ8BAf8EBAMCBsAwHQYDVR0OBBYEFPiGPZT0oTuRXvkyGoOgviNEWnc1MB0GA1UdJQQWMBQGCCsG
|
||||||
|
AQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0gAMEUCIQCJDSsVPfhr+gnDASMj5Syt+hxs
|
||||||
|
amHygPecjCLbcdFQQgIgSXC4musF5Fnj/CpNTqvk9+56FuINkATGS8xRh7kzKBE=
|
||||||
|
-----END CFCA KEY-----
|
||||||
|
`
|
||||||
|
|
||||||
|
var cfcasm2oca1 = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIICTTCCAfKgAwIBAgIKZCTXgL0MKPOtBzAMBggqgRzPVQGDdQUAMF0xCzAJBgNV
|
||||||
|
BAYTAkNOMTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBB
|
||||||
|
dXRob3JpdHkxHDAaBgNVBAMME0NGQ0EgVEVTVCBDUyBTTTIgQ0EwHhcNMTIxMjI1
|
||||||
|
MTIyNTA2WhcNMzIwNzIzMTIyNTA2WjBcMQswCQYDVQQGEwJDTjEwMC4GA1UECgwn
|
||||||
|
Q2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRswGQYDVQQD
|
||||||
|
DBJDRkNBIFRFU1QgU00yIE9DQTEwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAQz
|
||||||
|
uFgJbedY55u6NToJElGWzPT+9UF1dxcopnerNO3fqRd4C1lDzz9LJZSfmMyNYaky
|
||||||
|
YC+6zh9G6/aPXW1Od/RFo4GYMIGVMB8GA1UdIwQYMBaAFLXYkG9c8Ngz0mO9frLD
|
||||||
|
jcZPEnphMAwGA1UdEwQFMAMBAf8wOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovLzIx
|
||||||
|
MC43NC40Mi4zL3Rlc3RyY2EvU00yL2NybDEuY3JsMAsGA1UdDwQEAwIBBjAdBgNV
|
||||||
|
HQ4EFgQUa/4Y2o9COqa4bbMuiIM6NKLBMOEwDAYIKoEcz1UBg3UFAANHADBEAiAR
|
||||||
|
kDmkQ0Clio48994IUs63nA8k652O2C4+7EQs1SSbuAIgcwNUrHJyEYX8xT5BKl9T
|
||||||
|
lJOefzCNNJW5Z0f3Y/SjaG0=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
`
|
||||||
|
|
||||||
|
func parseTestKeyAndCert() (*sm2.PrivateKey, *smx509.Certificate, error) {
|
||||||
|
password := []byte("123456")
|
||||||
|
var block *pem.Block
|
||||||
|
block, rest := pem.Decode([]byte(v2exKeyPem))
|
||||||
|
if len(rest) != 0 {
|
||||||
|
return nil, nil, errors.New("unexpected remaining PEM block during decode")
|
||||||
|
}
|
||||||
|
return ParseSM2(password, block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseSM2(t *testing.T) {
|
||||||
|
_, _, err := parseTestKeyAndCert()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalSM2(t *testing.T) {
|
||||||
|
password := []byte("changeit")
|
||||||
|
priv, cert, err := parseTestKeyAndCert()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rootca1, err := smx509.ParseCertificatePEM([]byte(cfcasm2oca1))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = rootca1.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result, err := MarshalSM2(password, priv, cert)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
priv1, cert1, err := ParseSM2(password, result)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !priv.Equal(priv1) {
|
||||||
|
t.Fatal("not same private key")
|
||||||
|
}
|
||||||
|
if !cert.Equal(cert1) {
|
||||||
|
t.Fatal("not same certficate")
|
||||||
|
}
|
||||||
|
}
|
@ -190,7 +190,7 @@ func getOIDForEncryptionAlgorithm(pkey interface{}, OIDDigestAlg asn1.ObjectIden
|
|||||||
case crypto.Signer:
|
case crypto.Signer:
|
||||||
return getOIDForEncryptionAlgorithm(k.Public(), OIDDigestAlg)
|
return getOIDForEncryptionAlgorithm(k.Public(), OIDDigestAlg)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("pkcs7: cannot convert encryption algorithm to oid, unknown private key type %T", pkey)
|
return nil, fmt.Errorf("pkcs7: cannot convert encryption algorithm to oid, unknown or unsupported private key type %T", pkey)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
// since the signed-and-enveloped-data content type does not have authenticated or unauthenticated attributes,
|
// since the signed-and-enveloped-data content type does not have authenticated or unauthenticated attributes,
|
||||||
// and does not provide enveloping of signer information other than the signature.
|
// and does not provide enveloping of signer information other than the signature.
|
||||||
type signedEnvelopedData struct {
|
type signedEnvelopedData struct {
|
||||||
Version int
|
Version int `asn1:"default:1"`
|
||||||
RecipientInfos []recipientInfo `asn1:"set"`
|
RecipientInfos []recipientInfo `asn1:"set"`
|
||||||
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
|
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
|
||||||
EncryptedContentInfo encryptedContentInfo
|
EncryptedContentInfo encryptedContentInfo
|
||||||
|
@ -25,7 +25,7 @@ var (
|
|||||||
//
|
//
|
||||||
// SM2EnvelopedKey ::= SEQUENCE {
|
// SM2EnvelopedKey ::= SEQUENCE {
|
||||||
// symAlgID AlgorithmIdentifier,
|
// symAlgID AlgorithmIdentifier,
|
||||||
// sysmEncryptedKey SM2Cipher,
|
// symEncryptedKey SM2Cipher,
|
||||||
// sm2PublicKey SM2PublicKey,
|
// sm2PublicKey SM2PublicKey,
|
||||||
// sm2EncryptedPrivateKey BIT STRING,
|
// sm2EncryptedPrivateKey BIT STRING,
|
||||||
// }
|
// }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user