gmsm/cfca/pkcs12_sm2.go
2025-01-23 16:35:32 +08:00

106 lines
3.2 KiB
Go

// Package cfca supports part of CFCA SADK's functions, provides interoperability with CFCA SADK.
package cfca
import (
"encoding/asn1"
"errors"
"fmt"
"math/big"
"github.com/emmansun/gmsm/pkcs"
"github.com/emmansun/gmsm/pkcs7"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509"
)
// cfcaKeyPairData represents a key pair data structure used
// in CFCA (China Financial Certification Authority)
// for both parsing and marshaling SM2 keys and certificates.
type cfcaKeyPairData struct {
Version int `asn1:"default:1"`
EncryptedKey keyData
Certificate certData
}
// Encrypted private key data
type keyData struct {
ContentType asn1.ObjectIdentifier
Algorithm asn1.ObjectIdentifier
EncryptedContent asn1.RawValue
}
// Corresponding certificate
type certData struct {
ContentType asn1.ObjectIdentifier
Content asn1.RawContent
}
var (
oidSM2Data = pkcs7.SM2OIDData
oidSM4 = pkcs.SM4.OID()
oidSM4CBC = pkcs.SM4CBC.OID()
)
// ParseSM2 parses the der data, returns private key and related certificate, it's CFCA private structure.
// This method is corresponding to CFCA SADK's cfca.sadk.asn1.pkcs.load.
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)
}
pk, err := DecryptBySM4CBC(keys.EncryptedKey.EncryptedContent.Bytes, password)
if err != nil {
return nil, nil, fmt.Errorf("cfca: failed to decrypt by SM4-CBC, please ensure the password is correct: %v", err)
}
prvKey, err := sm2.NewPrivateKeyFromInt(new(big.Int).SetBytes(pk))
if err != nil {
return nil, nil, err
}
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.
// This method is corresponding to CFCA SADK's cfca.sadk.asn1.pkcs.CombineSM2Data.
func MarshalSM2(password []byte, key *sm2.PrivateKey, cert *smx509.Certificate) ([]byte, error) {
var err error
var ciphertext []byte
if ciphertext, err = EncryptBySM4CBC(key.D.Bytes(), password); err != nil {
return nil, err
}
if ciphertext, err = asn1.Marshal(ciphertext); 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)
}