diff --git a/docs/sm2.md b/docs/sm2.md index ad70c26..bae737c 100644 --- a/docs/sm2.md +++ b/docs/sm2.md @@ -320,9 +320,9 @@ func calculateSM2Hash(pub *ecdsa.PublicKey, data, uid []byte) ([]byte, error) { 第一个返回公钥的方法是必须要实现的,后面的方法取决于这个KEY的用途。 **注意**: -`Sign(rand io.Reader, digest []byte, opts SignerOpts) (signature []byte, err error)`方法通常用于对哈希值作签名,最好遵从以下实现逻辑:检查`opts`是否是`*sm2.SM2SignerOption`类型,如果是,则把传入的`digest`作为原始数据进行处理,具体实现可以参考`sm2.SignASN1`函数。当然,目前不管三七二十一,简单当作原始数据大多数情况下都没有问题。实现者可以根据实际应用情况酌情处理。 - -如果密码硬件有自己的随机数源,可以忽略传入的`rand`。 +1. `Sign(rand io.Reader, digest []byte, opts SignerOpts) (signature []byte, err error)`方法通常用于对哈希值作签名,最好遵从以下实现逻辑:检查`opts`是否是`*sm2.SM2SignerOption`类型,如果是,则把传入的`digest`作为原始数据进行处理,具体实现可以参考`sm2.SignASN1`函数。当然,目前不管三七二十一,简单当作原始数据大多数情况下都没有问题。实现者可以根据实际应用情况酌情处理。 +2. 如果密码硬件有自己的随机数源,可以忽略传入的`rand`。 +3. 很多设备签名函数通常只接收哈希值,需要调用```sm2.CalculateSM2Hash```自行计算哈希值。 ## SM2扩展应用 SM2的一些扩展应用,譬如从签名中恢复公钥、半同态加密、环签名等,大多尚处于POC状态,也无相关标准。其它扩展应用(但凡椭圆曲线公钥密码算法能用到的场合),包括但不限于: diff --git a/pkcs7/sign.go b/pkcs7/sign.go index 6d0a8aa..53feeda 100644 --- a/pkcs7/sign.go +++ b/pkcs7/sign.go @@ -3,6 +3,7 @@ package pkcs7 import ( "bytes" "crypto" + "crypto/ecdsa" "crypto/rand" "crypto/x509/pkix" "encoding/asn1" @@ -390,10 +391,16 @@ func signData(data []byte, pkey crypto.PrivateKey, hasher crypto.Hash) ([]byte, var opts crypto.SignerOpts = hasher if !hasher.Available() { - // if you pass a private key with type *ecdsa.PrivateKey and the curve is SM2, - // you will get unexpected signature. if sm2.IsSM2PublicKey(key.Public()) { opts = sm2.DefaultSM2SignerOpts + switch realKey := key.(type) { + case *ecdsa.PrivateKey: + { + sm2Key := new(sm2.PrivateKey) + sm2Key.PrivateKey = *realKey + key = sm2Key + } + } } else { return nil, fmt.Errorf("pkcs7: unsupported hash function %s", hasher) } diff --git a/pkcs7/sign_enveloped.go b/pkcs7/sign_enveloped.go index dba76c1..877836f 100644 --- a/pkcs7/sign_enveloped.go +++ b/pkcs7/sign_enveloped.go @@ -8,7 +8,6 @@ import ( "errors" "github.com/emmansun/gmsm/pkcs" - "github.com/emmansun/gmsm/sm2" "github.com/emmansun/gmsm/smx509" ) @@ -235,24 +234,7 @@ func (saed *SignedAndEnvelopedData) AddSignerChain(ee *smx509.Certificate, pkey if err != nil { return err } - key, ok := pkey.(crypto.Signer) - if !ok { - return errors.New("pkcs7: private key does not implement crypto.Signer") - } - - var signOpt crypto.SignerOpts - var tobeSigned []byte - - if _, isSM2 := pkey.(sm2.Signer); isSM2 { - signOpt = sm2.DefaultSM2SignerOpts - tobeSigned = saed.data - } else { - signOpt = hasher - h := newHash(hasher, saed.digestOid) - h.Write(saed.data) - tobeSigned = h.Sum(nil) - } - signature, err := key.Sign(rand.Reader, tobeSigned, signOpt) + signature, err := signData(saed.data, pkey, hasher) if err != nil { return err } diff --git a/pkcs7/sign_enveloped_test.go b/pkcs7/sign_enveloped_test.go index 47c4734..ff5a454 100644 --- a/pkcs7/sign_enveloped_test.go +++ b/pkcs7/sign_enveloped_test.go @@ -2,6 +2,7 @@ package pkcs7 import ( "bytes" + "crypto" "crypto/ecdsa" "encoding/pem" "math/big" @@ -177,40 +178,47 @@ func TestCreateSignedEvnvelopedDataSM(t *testing.T) { } sm2Key.D.FillBytes(privKey) + rootSM2Priv, ok := (*rootCert.PrivateKey).(*sm2.PrivateKey) + if !ok { + t.Fatal("should be sm2 private key") + } + signKeys := []crypto.PrivateKey{rootSM2Priv, &rootSM2Priv.PrivateKey} testCipers := []pkcs.Cipher{pkcs.SM4ECB, pkcs.SM4CBC, pkcs.SM4GCM} - for _, cipher := range testCipers { - saed, err := NewSMSignedAndEnvelopedData(privKey, cipher) - if err != nil { - t.Fatal(err) - } - err = saed.AddSigner(rootCert.Certificate, *rootCert.PrivateKey) - if err != nil { - t.Fatal(err) - } - err = saed.AddRecipient(recipient.Certificate) - if err != nil { - t.Fatal(err) - } - result, err := saed.Finish() - if err != nil { - t.Fatal(err) - } + for _, key := range signKeys { + for _, cipher := range testCipers { + saed, err := NewSMSignedAndEnvelopedData(privKey, cipher) + if err != nil { + t.Fatal(err) + } + err = saed.AddSigner(rootCert.Certificate, key) + if err != nil { + t.Fatal(err) + } + err = saed.AddRecipient(recipient.Certificate) + if err != nil { + t.Fatal(err) + } + result, err := saed.Finish() + if err != nil { + t.Fatal(err) + } - // fmt.Printf("%x\n", result) + // fmt.Printf("%x\n", result) - // parse, decrypt, verify - p7Data, err := Parse(result) - if err != nil { - t.Fatal(err) - } - encKeyBytes, err := p7Data.DecryptAndVerify(recipient.Certificate, *recipient.PrivateKey, func() error { - return p7Data.Verify() - }) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(encKeyBytes, privKey) { - t.Fatal("not same private key") + // parse, decrypt, verify + p7Data, err := Parse(result) + if err != nil { + t.Fatal(err) + } + encKeyBytes, err := p7Data.DecryptAndVerify(recipient.Certificate, *recipient.PrivateKey, func() error { + return p7Data.Verify() + }) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(encKeyBytes, privKey) { + t.Fatal("not same private key") + } } } } @@ -283,7 +291,7 @@ func TestCreateSignedEvnvelopedData(t *testing.T) { if recipients[0].SerialNumber.Cmp(recipient.Certificate.SerialNumber) != 0 { t.Errorf("Recipient serial number does not match.\n\tExpected:%s\n\tActual:%s", recipient.Certificate.SerialNumber, recipients[0].SerialNumber) } - + if !bytes.Equal(recipients[0].RawIssuer, recipient.Certificate.RawIssuer) { t.Errorf("Recipient issuer name does not match.\n\tExpected:%x\n\tActual:%x", recipient.Certificate.RawIssuer, recipients[0].RawIssuer) } diff --git a/sm2/sm2_dsa.go b/sm2/sm2_dsa.go index 6e473a4..dc1ed16 100644 --- a/sm2/sm2_dsa.go +++ b/sm2/sm2_dsa.go @@ -214,6 +214,7 @@ var defaultUID = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x // Compliance with GB/T 32918.2-2016 5.5. // // This function will not use default UID even the uid argument is empty. +// Reference: GM/T 0009-2023 Chapter 8.1. func CalculateZA(pub *ecdsa.PublicKey, uid []byte) ([]byte, error) { uidLen := len(uid) if uidLen > 0x1fff { @@ -263,6 +264,7 @@ func bigIntToBytes(curve elliptic.Curve, value *big.Int) []byte { // If the UID is not provided, a default UID (1234567812345678) is used. // The public key must be valid, otherwise will be panic. // This function is used to calculate the hash value for SM2 signature. +// Reference: GM/T 0009-2023 Chapter 8.1 and 8.2. func CalculateSM2Hash(pub *ecdsa.PublicKey, data, uid []byte) ([]byte, error) { if len(uid) == 0 { uid = defaultUID