diff --git a/docs/sm2.md b/docs/sm2.md index d9e584bb..eb827056 100644 --- a/docs/sm2.md +++ b/docs/sm2.md @@ -7,6 +7,7 @@ * 《GB/T 32918.4-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第4部分:公钥加密算法》 * 《GB/T 32918.5-2016 信息安全技术 SM2椭圆曲线公钥密码算法 第5部分:参数定义》 * 《GB/T 35276-2017 信息安全技术 SM2密码算法使用规范》 +* 《GB/T 33560-2017 信息安全技术 密码应用标识规范》 * 《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》(对应PKCS#7) 您可以从[国家标准全文公开系统](https://openstd.samr.gov.cn/)在线阅读这些标准。 diff --git a/smx509/parser.go b/smx509/parser.go index f5f81334..57e3b51d 100644 --- a/smx509/parser.go +++ b/smx509/parser.go @@ -21,6 +21,7 @@ import ( "unicode/utf16" "unicode/utf8" + "github.com/emmansun/gmsm/sm2" "golang.org/x/crypto/cryptobyte" cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" ) @@ -288,6 +289,27 @@ func parsePublicKey(keyData *publicKeyInfo) (any, error) { Y: y, } return pub, nil + case oid.Equal(oidPublicKeySM2): + paramsDer := cryptobyte.String(params.FullBytes) + namedCurveOID := new(asn1.ObjectIdentifier) + if !paramsDer.ReadASN1ObjectIdentifier(namedCurveOID) { + return nil, errors.New("x509: invalid SM2 parameters") + } + namedCurve := namedCurveFromOID(*namedCurveOID) + if namedCurve != sm2.P256() { + return nil, errors.New("x509: unsupported SM2 curve") + } + x, y := elliptic.Unmarshal(namedCurve, der) + if x == nil { + return nil, errors.New("x509: failed to unmarshal SM2 curve point") + } + pub := &ecdsa.PublicKey{ + Curve: namedCurve, + X: x, + Y: y, + } + return pub, nil + case oid.Equal(oidPublicKeyEd25519): // RFC 8410, Section 3 // > For all of the OIDs, the parameters MUST be absent. diff --git a/smx509/parser_test.go b/smx509/parser_test.go index 57666be9..be0e7071 100644 --- a/smx509/parser_test.go +++ b/smx509/parser_test.go @@ -2,6 +2,7 @@ package smx509 import ( "encoding/asn1" + "encoding/hex" "testing" cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" @@ -97,3 +98,31 @@ func TestParseASN1String(t *testing.T) { }) } } + +// The SM2 public key with alg = oidPublicKeySM2 and SM2 curve +var sm2PublicKeyHex = "305a301406082a811ccf5501822d06082a811ccf5501822d0342000409586fff35c1f805b5c74f7281c3ade8fe211ffa70bf0ddd1c7268f62ae664331410e3039eeb03209afdc7fa834235c7b3ef528d32bf8b401eb98d32f498b4b7" + +// The SM2 public key with alg = oidPublicKeySM2 and NIST P256 curve +var sm2NistP256PubulicKeyHex = "305a301406082a811ccf5501822d06082a8648ce3d0301070342000476110a45e7e86c1e96ba3c3300da61049a529c20a7ea7f026e50a2dbed60558087346bcb04cb0f0f8dcab8cca9967b8c7cc5aa0c874f024b73208b28f408bfca" + +func TestParseSM2PublicKey(t *testing.T) { + der, err := hex.DecodeString(sm2PublicKeyHex) + if err != nil { + t.Fatal(err) + } + _, err = ParsePKIXPublicKey(der) + if err != nil { + t.Fatal(err) + } +} + +func TestParseSM2PublicKeyWithNistP256(t *testing.T) { + der, err := hex.DecodeString(sm2NistP256PubulicKeyHex) + if err != nil { + t.Fatal(err) + } + _, err = ParsePKIXPublicKey(der) + if err == nil || err.Error() != "x509: unsupported SM2 curve" { + t.Fatal("should throw x509: unsupported SM2 curve") + } +} diff --git a/smx509/pkcs8.go b/smx509/pkcs8.go index ba236fd3..b2a121ca 100644 --- a/smx509/pkcs8.go +++ b/smx509/pkcs8.go @@ -46,27 +46,42 @@ func ParsePKCS8PrivateKey(der []byte) (key any, err error) { } return nil, err } - if privKey.Algo.Algorithm.Equal(oidSM9) || privKey.Algo.Algorithm.Equal(oidSM9Sign) || privKey.Algo.Algorithm.Equal(oidSM9Enc) { + switch { + case privKey.Algo.Algorithm.Equal(oidPublicKeySM2): + bytes := privKey.Algo.Parameters.FullBytes + namedCurveOID := new(asn1.ObjectIdentifier) + if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil { + namedCurveOID = nil + } + ecKey, err := parseECPrivateKey(namedCurveOID, privKey.PrivateKey) + if err != nil { + return nil, errors.New("x509: failed to parse SM2 private key embedded in PKCS#8: " + err.Error()) + } + if ecKey.Curve != sm2.P256() { + return nil, errors.New("x509: unsupported SM2 curve") + } + return new(sm2.PrivateKey).FromECPrivateKey(ecKey) + case privKey.Algo.Algorithm.Equal(oidSM9), privKey.Algo.Algorithm.Equal(oidSM9Sign), privKey.Algo.Algorithm.Equal(oidSM9Enc): return parseSM9PrivateKey(privKey) - } - if !privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA) && !privKey.Algo.Algorithm.Equal(oidNamedCurveP256SM2) { + case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA): + bytes := privKey.Algo.Parameters.FullBytes + namedCurveOID := new(asn1.ObjectIdentifier) + if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil { + namedCurveOID = nil + } + ecKey, err := parseECPrivateKey(namedCurveOID, privKey.PrivateKey) + if err != nil { + return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error()) + } + // convert *ecdsa.PrivateKey to *sm2.PrivateKey + if ecKey.Curve == sm2.P256() { + return new(sm2.PrivateKey).FromECPrivateKey(ecKey) + } + return ecKey, err + default: + // fallback to golang sdk return x509.ParsePKCS8PrivateKey(der) } - bytes := privKey.Algo.Parameters.FullBytes - namedCurveOID := new(asn1.ObjectIdentifier) - if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil { - namedCurveOID = nil - } - ecKey, err := parseECPrivateKey(namedCurveOID, privKey.PrivateKey) - if err != nil { - return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error()) - } - if namedCurveOID.Equal(oidNamedCurveP256SM2) { - key, err = new(sm2.PrivateKey).FromECPrivateKey(ecKey) - } else { - key = ecKey - } - return key, err } func parseSM9PrivateKey(privKey pkcs8) (key any, err error) { diff --git a/smx509/x509.go b/smx509/x509.go index ce46d570..456befbe 100644 --- a/smx509/x509.go +++ b/smx509/x509.go @@ -462,6 +462,9 @@ var ( // id-ecPublicKey OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} + // GB/T 33560-2017 信息安全技术 密码应用标识规范 + // 附录A(规范性附录)商用密码领域中的相关OID定义 + oidPublicKeySM2 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 301} // RFC 8410, Section 3 // // id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 }