From 5db7e633605f34dd176d5f8c2e5ac789b0749399 Mon Sep 17 00:00:00 2001 From: Sun Yimin Date: Mon, 24 Oct 2022 16:09:12 +0800 Subject: [PATCH] provide convient methods to parse pkcs8 sm9 keys --- pkcs8/pkcs8.go | 61 ++++++++++++++++++++++++++++++-- pkcs8/pkcs8_test.go | 85 +++++++++++++++++++++++++++++++++++++++++++++ sm9/sm9_key.go | 46 +++++++++++------------- sm9/sm9_key_test.go | 41 ++++++++++++++++++++++ 4 files changed, 205 insertions(+), 28 deletions(-) diff --git a/pkcs8/pkcs8.go b/pkcs8/pkcs8.go index 8a936b5..4dce5ad 100644 --- a/pkcs8/pkcs8.go +++ b/pkcs8/pkcs8.go @@ -18,6 +18,7 @@ import ( "github.com/emmansun/gmsm/sm2" "github.com/emmansun/gmsm/sm3" + "github.com/emmansun/gmsm/sm9" "github.com/emmansun/gmsm/smx509" ) @@ -219,7 +220,7 @@ func ParsePrivateKey(der []byte, password []byte) (interface{}, KDFParameters, e key, err := smx509.ParsePKCS8PrivateKey(decryptedKey) if err != nil { - return nil, nil, errors.New("pkcs8: failed to parse private key while ParsePKCS8PrivateKey: " + err.Error()) + return nil, nil, errors.New("pkcs8: incorrect password? failed to parse private key while ParsePKCS8PrivateKey: " + err.Error()) } return key, kdfParams, nil } @@ -327,7 +328,7 @@ func ParsePKCS8PrivateKeyECDSA(der []byte, v ...[]byte) (*ecdsa.PrivateKey, erro return typedKey, nil } -// ParsePKCS8PrivateKeySM2 parses encrypted/unencrypted private keys in PKCS#8 format. +// ParsePKCS8PrivateKeySM2 parses encrypted/unencrypted SM2 private key in PKCS#8 format. // To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter. func ParsePKCS8PrivateKeySM2(der []byte, v ...[]byte) (*sm2.PrivateKey, error) { key, err := ParsePKCS8PrivateKey(der, v...) @@ -341,6 +342,62 @@ func ParsePKCS8PrivateKeySM2(der []byte, v ...[]byte) (*sm2.PrivateKey, error) { return typedKey, nil } +// ParseSM9SignMasterPrivateKey parses encrypted/unencrypted SM9 sign master private key in PKCS#8 format. +// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter. +func ParseSM9SignMasterPrivateKey(der []byte, v ...[]byte) (*sm9.SignMasterPrivateKey, error) { + key, err := ParsePKCS8PrivateKey(der, v...) + if err != nil { + return nil, err + } + typedKey, ok := key.(*sm9.SignMasterPrivateKey) + if !ok { + return nil, errors.New("pkcs8: key block is not of type SM9 sign master private key") + } + return typedKey, nil +} + +// ParseSM9SignPrivateKey parses encrypted/unencrypted SM9 sign private key in PKCS#8 format. +// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter. +func ParseSM9SignPrivateKey(der []byte, v ...[]byte) (*sm9.SignPrivateKey, error) { + key, err := ParsePKCS8PrivateKey(der, v...) + if err != nil { + return nil, err + } + typedKey, ok := key.(*sm9.SignPrivateKey) + if !ok { + return nil, errors.New("pkcs8: key block is not of type SM9 sign user private key") + } + return typedKey, nil +} + +// ParseSM9EncryptMasterPrivateKey parses encrypted/unencrypted SM9 encrypt master private key in PKCS#8 format. +// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter. +func ParseSM9EncryptMasterPrivateKey(der []byte, v ...[]byte) (*sm9.EncryptMasterPrivateKey, error) { + key, err := ParsePKCS8PrivateKey(der, v...) + if err != nil { + return nil, err + } + typedKey, ok := key.(*sm9.EncryptMasterPrivateKey) + if !ok { + return nil, errors.New("pkcs8: key block is not of type SM9 encrypt master private key") + } + return typedKey, nil +} + +// ParseSM9EncryptPrivateKey parses encrypted/unencrypted SM9 encrypt private key in PKCS#8 format. +// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter. +func ParseSM9EncryptPrivateKey(der []byte, v ...[]byte) (*sm9.EncryptPrivateKey, error) { + key, err := ParsePKCS8PrivateKey(der, v...) + if err != nil { + return nil, err + } + typedKey, ok := key.(*sm9.EncryptPrivateKey) + if !ok { + return nil, errors.New("pkcs8: key block is not of type SM9 encrypt user private key") + } + return typedKey, nil +} + // ConvertPrivateKeyToPKCS8 converts the private key into PKCS#8 format. // To encrypt the private key, the password of []byte type should be provided as the second parameter. // diff --git a/pkcs8/pkcs8_test.go b/pkcs8/pkcs8_test.go index f256edf..4888f46 100644 --- a/pkcs8/pkcs8_test.go +++ b/pkcs8/pkcs8_test.go @@ -12,6 +12,7 @@ import ( "github.com/emmansun/gmsm/pkcs8" "github.com/emmansun/gmsm/sm2" + "github.com/emmansun/gmsm/sm9" ) const rsa2048 = `-----BEGIN PRIVATE KEY----- @@ -569,6 +570,90 @@ func TestMarshalPrivateKey(t *testing.T) { t.Fatalf("%d: Decoded key does not match original key", i) } + sm9SignMasterPrivateKey, err := sm9.GenerateSignMasterKey(rand.Reader) + if err != nil { + t.Fatalf("%d: GenerateKey returned: %s", i, err) + } + + der, err = pkcs8.MarshalPrivateKey(sm9SignMasterPrivateKey, tt.password, tt.opts) + if err != nil { + t.Fatalf("%d: MarshalPrivateKey returned: %s", i, err) + } + decodedSM9SignMasterPrivateKey, err := pkcs8.ParseSM9SignMasterPrivateKey(der, tt.password) + if err != nil { + t.Fatalf("%d: ParseSM9SignMasterPrivateKey returned: %s", i, err) + } + + _, err = pkcs8.ParseSM9SignPrivateKey(der, tt.password) + if err == nil { + t.Fatalf("%d: ParseSM9SignPrivateKey should return error", i) + } + + if sm9SignMasterPrivateKey.D.Cmp(decodedSM9SignMasterPrivateKey.D) != 0 { + t.Fatalf("%d: Decoded key does not match original key", i) + } + + sm9SignPrivateKey, err := sm9SignMasterPrivateKey.GenerateUserKey([]byte("emmansun"), 0x01) + if err != nil { + t.Fatalf("%d: GenerateUserKey returned: %s", i, err) + } + der, err = pkcs8.MarshalPrivateKey(sm9SignPrivateKey, tt.password, tt.opts) + if err != nil { + t.Fatalf("%d: MarshalPrivateKey returned: %s", i, err) + } + decodedSM9SignPrivateKey, err := pkcs8.ParseSM9SignPrivateKey(der, tt.password) + if err != nil { + t.Fatalf("%d: ParseSM9SignPrivateKey returned: %s", i, err) + } + _, err = pkcs8.ParseSM9SignMasterPrivateKey(der, tt.password) + if err == nil { + t.Fatalf("%d: ParseSM9SignMasterPrivateKey should return error", i) + } + if !sm9SignPrivateKey.PrivateKey.Equal(decodedSM9SignPrivateKey.PrivateKey) { + t.Fatalf("%d: Decoded key does not match original key", i) + } + + sm9EncMasterPrivateKey, err := sm9.GenerateEncryptMasterKey(rand.Reader) + if err != nil { + t.Fatalf("%d: GenerateKey returned: %s", i, err) + } + + der, err = pkcs8.MarshalPrivateKey(sm9EncMasterPrivateKey, tt.password, tt.opts) + if err != nil { + t.Fatalf("%d: MarshalPrivateKey returned: %s", i, err) + } + decodedSM9EncMasterPrivateKey, err := pkcs8.ParseSM9EncryptMasterPrivateKey(der, tt.password) + if err != nil { + t.Fatalf("%d: ParseSM9EncryptMasterPrivateKey returned: %s", i, err) + } + _, err = pkcs8.ParseSM9EncryptPrivateKey(der, tt.password) + if err == nil { + t.Fatalf("%d: ParseSM9EncryptPrivateKey should return error", i) + } + if sm9EncMasterPrivateKey.D.Cmp(decodedSM9EncMasterPrivateKey.D) != 0 { + t.Fatalf("%d: Decoded key does not match original key", i) + } + + sm9EncPrivateKey, err := sm9EncMasterPrivateKey.GenerateUserKey([]byte("emmansun"), 0x02) + if err != nil { + t.Fatalf("%d: GenerateUserKey returned: %s", i, err) + } + der, err = pkcs8.MarshalPrivateKey(sm9EncPrivateKey, tt.password, tt.opts) + if err != nil { + t.Fatalf("%d: MarshalPrivateKey returned: %s", i, err) + } + decodedSM9EncPrivateKey, err := pkcs8.ParseSM9EncryptPrivateKey(der, tt.password) + if err != nil { + t.Fatalf("%d: ParseSM9EncryptPrivateKey returned: %s", i, err) + } + _, err = pkcs8.ParseSM9EncryptMasterPrivateKey(der, tt.password) + if err == nil { + t.Fatalf("%d: ParseSM9EncryptMasterPrivateKey should return error", i) + } + if !sm9EncPrivateKey.PrivateKey.Equal(decodedSM9EncPrivateKey.PrivateKey) { + t.Fatalf("%d: Decoded key does not match original key", i) + } + for _, curve := range []elliptic.Curve{ elliptic.P224(), elliptic.P256(), elliptic.P384(), elliptic.P521(), } { diff --git a/sm9/sm9_key.go b/sm9/sm9_key.go index ab3e741..33ce490 100644 --- a/sm9/sm9_key.go +++ b/sm9/sm9_key.go @@ -1,7 +1,6 @@ package sm9 import ( - "encoding/asn1" "encoding/pem" "errors" @@ -209,33 +208,28 @@ func (pub *SignMasterPublicKey) UnmarshalRaw(bytes []byte) error { // UnmarshalASN1 unmarsal der data to sign master public key func (pub *SignMasterPublicKey) UnmarshalASN1(der []byte) error { var bytes []byte + var inner cryptobyte.String input := cryptobyte.String(der) - if !input.ReadASN1BitStringAsBytes(&bytes) || !input.Empty() { + if der[0] == 0x30 { + if !input.ReadASN1(&inner, cryptobyte_asn1.SEQUENCE) || + !input.Empty() || + !inner.ReadASN1BitStringAsBytes(&bytes) || + !inner.Empty() { + return errors.New("sm9: invalid sign master public key asn1 data") + } + } else if !input.ReadASN1BitStringAsBytes(&bytes) || !input.Empty() { return errors.New("sm9: invalid sign master public key asn1 data") } return pub.UnmarshalRaw(bytes) } -type publicKeyInfo struct { - Raw asn1.RawContent - PublicKey asn1.BitString -} - // ParseFromPEM just for GMSSL, there are no Algorithm pkix.AlgorithmIdentifier func (pub *SignMasterPublicKey) ParseFromPEM(data []byte) error { block, _ := pem.Decode([]byte(data)) if block == nil { return errors.New("failed to parse PEM block") } - - var pki publicKeyInfo - if rest, err := asn1.Unmarshal(block.Bytes, &pki); err != nil { - return err - } else if len(rest) != 0 { - return errors.New("trailing data after ASN.1 of public-key") - } - der := cryptobyte.String(pki.PublicKey.RightAlign()) - return pub.UnmarshalRaw(der) + return pub.UnmarshalASN1(block.Bytes) } // MasterPublic returns the master public key corresponding to priv. @@ -467,22 +461,22 @@ func (pub *EncryptMasterPublicKey) ParseFromPEM(data []byte) error { if block == nil { return errors.New("failed to parse PEM block") } - - var pki publicKeyInfo - if rest, err := asn1.Unmarshal(block.Bytes, &pki); err != nil { - return err - } else if len(rest) != 0 { - return errors.New("trailing data after ASN.1 of public-key") - } - der := cryptobyte.String(pki.PublicKey.RightAlign()) - return pub.UnmarshalRaw(der) + return pub.UnmarshalASN1(block.Bytes) } // UnmarshalASN1 unmarsal der data to encrypt master public key func (pub *EncryptMasterPublicKey) UnmarshalASN1(der []byte) error { var bytes []byte + var inner cryptobyte.String input := cryptobyte.String(der) - if !input.ReadASN1BitStringAsBytes(&bytes) || !input.Empty() { + if der[0] == 0x30 { + if !input.ReadASN1(&inner, cryptobyte_asn1.SEQUENCE) || + !input.Empty() || + !inner.ReadASN1BitStringAsBytes(&bytes) || + !inner.Empty() { + return errors.New("sm9: invalid encrypt master public key asn1 data") + } + } else if !input.ReadASN1BitStringAsBytes(&bytes) || !input.Empty() { return errors.New("sm9: invalid encrypt master public key asn1 data") } return pub.UnmarshalRaw(bytes) diff --git a/sm9/sm9_key_test.go b/sm9/sm9_key_test.go index 982d283..a57edf6 100644 --- a/sm9/sm9_key_test.go +++ b/sm9/sm9_key_test.go @@ -3,7 +3,11 @@ package sm9 import ( "crypto/rand" "encoding/hex" + "encoding/pem" "testing" + + "golang.org/x/crypto/cryptobyte" + cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" ) func TestSignMasterPrivateKeyMarshalASN1(t *testing.T) { @@ -270,6 +274,23 @@ func TestParseSM9SignMasterPublicKey(t *testing.T) { if key.MasterPublicKey == nil { t.Errorf("not expected nil") } + + // create sign master public key PEM with cryptobyte + var b cryptobyte.Builder + bytes, _ := key.MarshalASN1() + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddBytes(bytes) + }) + data, err := b.Bytes() + if err != nil { + t.Fatal(err) + } + block := &pem.Block{Bytes: data, Type: "SM9 SIGN MASTER PUBLIC KEY"} + pemContent := string(pem.EncodeToMemory(block)) + + if pemContent != sm9SignMasterPublicKeyFromGMSSL { + t.Fatalf("failed %s\n", pemContent) + } } const sm9EncMasterPublicKeyFromGMSSL = `-----BEGIN SM9 ENC MASTER PUBLIC KEY----- @@ -287,4 +308,24 @@ func TestParseSM9EncryptMasterPublicKey(t *testing.T) { if key.MasterPublicKey == nil { t.Errorf("not expected nil") } + + // create encrypt master public key PEM with asn1 + var b cryptobyte.Builder + + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddASN1BitString(key.MasterPublicKey.MarshalUncompressed()) + }) + data, err := b.Bytes() + if err != nil { + t.Fatal(err) + } + if err != nil { + t.Fatal(err) + } + block := &pem.Block{Bytes: data, Type: "SM9 ENC MASTER PUBLIC KEY"} + pemContent := string(pem.EncodeToMemory(block)) + + if pemContent != sm9EncMasterPublicKeyFromGMSSL { + t.Fatalf("failed %s\n", pemContent) + } }