2025-01-14 16:41:03 +08:00
|
|
|
|
// Package cfca supports part of CFCA SADK's functions, provides interoperability with CFCA SADK.
|
2024-08-02 13:02:25 +08:00
|
|
|
|
package cfca
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/asn1"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"math/big"
|
|
|
|
|
|
|
|
|
|
"github.com/emmansun/gmsm/pkcs"
|
2024-12-03 17:53:25 +08:00
|
|
|
|
"github.com/emmansun/gmsm/pkcs7"
|
2024-08-02 13:02:25 +08:00
|
|
|
|
"github.com/emmansun/gmsm/sm2"
|
|
|
|
|
"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 (
|
2024-12-03 17:53:25 +08:00
|
|
|
|
oidSM2Data = pkcs7.SM2OIDData
|
|
|
|
|
oidSM4 = pkcs.SM4.OID()
|
|
|
|
|
oidSM4CBC = pkcs.SM4CBC.OID()
|
2024-08-02 13:02:25 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// ParseSM2 parses the der data, returns private key and related certificate, it's CFCA private structure.
|
2024-12-16 12:54:36 +08:00
|
|
|
|
// This methed is coresponding to CFCA SADK's cfca.sadk.asn1.pkcs.load.
|
2024-08-02 13:02:25 +08:00
|
|
|
|
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)
|
|
|
|
|
}
|
2024-12-11 17:51:31 +08:00
|
|
|
|
pk, err := DecryptBySM4CBC(keys.EncryptedKey.EncryptedContent.Bytes, password)
|
2024-08-02 13:02:25 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
2024-12-11 17:51:31 +08:00
|
|
|
|
prvKey, err := sm2.NewPrivateKeyFromInt(new(big.Int).SetBytes(pk))
|
2024-12-11 08:36:55 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
2024-08-02 13:02:25 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 12:54:36 +08:00
|
|
|
|
// MarshalSM2 encodes sm2 private key and related certificate to cfca defined format.
|
|
|
|
|
// This methed is coresponding to CFCA SADK's cfca.sadk.asn1.pkcs.CombineSM2Data.
|
2024-08-02 13:02:25 +08:00
|
|
|
|
func MarshalSM2(password []byte, key *sm2.PrivateKey, cert *smx509.Certificate) ([]byte, error) {
|
2024-12-11 17:51:31 +08:00
|
|
|
|
var err error
|
|
|
|
|
var ciphertext []byte
|
|
|
|
|
if ciphertext, err = EncryptBySM4CBC(key.D.Bytes(), password); err != nil {
|
2024-08-02 13:02:25 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-12-11 17:51:31 +08:00
|
|
|
|
if ciphertext, err = asn1.Marshal(ciphertext); err != nil {
|
2024-08-02 13:02:25 +08:00
|
|
|
|
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)
|
|
|
|
|
}
|