sm2,smx509: add encoding paths for SM2 ecdh keys

This commit is contained in:
Sun Yimin 2022-11-21 09:31:30 +08:00 committed by GitHub
parent 9805aa448a
commit 984913e228
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 127 additions and 4 deletions

View File

@ -66,6 +66,10 @@ type Curve interface {
}
// PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire.
//
// These keys can be parsed with [smx509.ParsePKIXPublicKey] and encoded
// with [smx509.MarshalPKIXPublicKey]. For SM2 curve, it then needs to
// be converted with [sm2.PublicKeyToECDH] after parsing.
type PublicKey struct {
curve Curve
publicKey []byte
@ -129,6 +133,10 @@ func (uv *PublicKey) SM2SharedKey(isResponder bool, kenLen int, sPub, sRemote *P
}
// PrivateKey is an ECDH private key, usually kept secret.
//
// These keys can be parsed with [smx509.ParsePKCS8PrivateKey] and encoded
// with [smx509.MarshalPKCS8PrivateKey]. For SM2 curve, it then needs to
// be converted with [sm2.PrivateKey.ECDH] after parsing.
type PrivateKey struct {
curve Curve
privateKey []byte

View File

@ -22,6 +22,7 @@ import (
"math/big"
"strings"
"github.com/emmansun/gmsm/ecdh"
"github.com/emmansun/gmsm/internal/randutil"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/kdf"
@ -869,3 +870,41 @@ func IsSM2PublicKey(publicKey interface{}) bool {
func P256() elliptic.Curve {
return sm2ec.P256()
}
// PublicKeyToECDH returns k as a [ecdh.PublicKey]. It returns an error if the key is
// invalid according to the definition of [ecdh.Curve.NewPublicKey], or if the
// Curve is not supported by ecdh.
func PublicKeyToECDH(k *ecdsa.PublicKey) (*ecdh.PublicKey, error) {
c := curveToECDH(k.Curve)
if c == nil {
return nil, errors.New("sm2: unsupported curve by ecdh")
}
if !k.Curve.IsOnCurve(k.X, k.Y) {
return nil, errors.New("sm2: invalid public key")
}
return c.NewPublicKey(elliptic.Marshal(k.Curve, k.X, k.Y))
}
// ECDH returns k as a [ecdh.PrivateKey]. It returns an error if the key is
// invalid according to the definition of [ecdh.Curve.NewPrivateKey], or if the
// Curve is not supported by ecdh.
func (k *PrivateKey) ECDH() (*ecdh.PrivateKey, error) {
c := curveToECDH(k.Curve)
if c == nil {
return nil, errors.New("sm2: unsupported curve by ecdh")
}
size := (k.Curve.Params().N.BitLen() + 7) / 8
if k.D.BitLen() > size*8 {
return nil, errors.New("sm2: invalid private key")
}
return c.NewPrivateKey(k.D.FillBytes(make([]byte, size)))
}
func curveToECDH(c elliptic.Curve) ecdh.Curve {
switch c {
case sm2ec.P256():
return ecdh.P256()
default:
return nil
}
}

View File

@ -7,6 +7,7 @@ import (
"encoding/asn1"
"errors"
"github.com/emmansun/gmsm/ecdh"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/sm9"
)
@ -117,7 +118,7 @@ func parseSM9PrivateKey(privKey pkcs8) (key interface{}, err error) {
// MarshalPKCS8PrivateKey converts a private key to PKCS #8, ASN.1 DER form.
//
// The following key types are currently supported: *rsa.PrivateKey, *ecdsa.PrivateKey, a *sm2.PrivateKey,
// The following key types are currently supported: *rsa.PrivateKey, *ecdsa.PrivateKey, a *sm2.PrivateKey, a *ecdh.PrivateKey
// a *sm9.SignMasterPrivateKey, a *sm9.SignPrivateKey, a *sm9.EncryptMasterPrivateKey, a *sm9.EncryptPrivateKey
// and ed25519.PrivateKey. Unsupported key types result in an error.
//
@ -126,6 +127,8 @@ func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) {
switch k := key.(type) {
case *sm2.PrivateKey:
return marshalPKCS8ECPrivateKey(&k.PrivateKey)
case *ecdh.PrivateKey:
return marshalPKCS8ECDHPrivateKey(k)
case *sm9.SignPrivateKey:
return marshalPKCS8SM9SignPrivateKey(k)
case *sm9.EncryptPrivateKey:
@ -280,3 +283,25 @@ func marshalPKCS8ECPrivateKey(k *ecdsa.PrivateKey) ([]byte, error) {
}
return asn1.Marshal(privKey)
}
func marshalPKCS8ECDHPrivateKey(k *ecdh.PrivateKey) ([]byte, error) {
var privKey pkcs8
oid, ok := oidFromECDHCurve(k.Curve())
if !ok {
return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
}
oidBytes, err := asn1.Marshal(oid)
if err != nil {
return nil, errors.New("x509: failed to marshal curve OID: " + err.Error())
}
privKey.Algo = pkix.AlgorithmIdentifier{
Algorithm: oidPublicKeyECDSA,
Parameters: asn1.RawValue{
FullBytes: oidBytes,
},
}
if privKey.PrivateKey, err = marshalECDHPrivateKey(k); err != nil {
return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error())
}
return asn1.Marshal(privKey)
}

View File

@ -124,6 +124,22 @@ func TestPKCS8(t *testing.T) {
t.Errorf("%s: marshaled PKCS#8 didn't match original: got %x, want %x", test.name, reserialised, derBytes)
continue
}
if ecKey, isEC := privKey.(*sm2.PrivateKey); isEC {
ecdhKey, err := ecKey.ECDH()
if err != nil {
t.Errorf("%s: failed to convert to ecdh: %s", test.name, err)
continue
}
reserialised, err := MarshalPKCS8PrivateKey(ecdhKey)
if err != nil {
t.Errorf("%s: failed to marshal into PKCS#8: %s", test.name, err)
continue
}
if !bytes.Equal(derBytes, reserialised) {
t.Errorf("%s: marshaled PKCS#8 didn't match original: got %x, want %x", test.name, reserialised, derBytes)
continue
}
}
}
}

View File

@ -8,6 +8,7 @@ import (
"fmt"
"math/big"
"github.com/emmansun/gmsm/ecdh"
"github.com/emmansun/gmsm/sm2"
)
@ -133,3 +134,13 @@ func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *e
return priv, nil
}
// marshalECDHPrivateKey marshals an EC private key into ASN.1, DER format
// suitable for SM2 curve.
func marshalECDHPrivateKey(key *ecdh.PrivateKey) ([]byte, error) {
return asn1.Marshal(ecPrivateKey{
Version: 1,
PrivateKey: key.Bytes(),
PublicKey: asn1.BitString{Bytes: key.PublicKey().Bytes()},
})
}

View File

@ -637,7 +637,7 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
for i := 0; i < opts.Intermediates.len(); i++ {
c, err := opts.Intermediates.cert(i)
if err != nil {
return nil, fmt.Errorf("crypto/x509: error fetching intermediate: %w", err)
return nil, fmt.Errorf("x509: error fetching intermediate: %w", err)
}
if len(c.Raw) == 0 {
return nil, errNotParsed

View File

@ -49,6 +49,7 @@ import (
"golang.org/x/crypto/cryptobyte"
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
"github.com/emmansun/gmsm/ecdh"
"github.com/emmansun/gmsm/internal/godebug"
"github.com/emmansun/gmsm/sm2"
)
@ -114,6 +115,20 @@ func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorith
case ed25519.PublicKey:
publicKeyBytes = pub
publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
case *ecdh.PublicKey: //TODO:will add SDK ECDH public key support from golang 1.19 later.
publicKeyBytes = pub.Bytes()
oid, ok := oidFromECDHCurve(pub.Curve())
if !ok {
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve")
}
publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
var paramBytes []byte
paramBytes, err = asn1.Marshal(oid)
if err != nil {
return
}
publicKeyAlgorithm.Parameters.FullBytes = paramBytes
default:
return nil, pkix.AlgorithmIdentifier{}, fmt.Errorf("x509: unsupported public key type: %T", pub)
}
@ -519,6 +534,15 @@ func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
return nil, false
}
func oidFromECDHCurve(curve ecdh.Curve) (asn1.ObjectIdentifier, bool) {
switch curve {
case ecdh.P256():
return oidNamedCurveP256SM2, true
}
return nil, false
}
// KeyUsage represents the set of actions that are valid for a given key. It's
// a bitmap of the KeyUsage* constants.
type KeyUsage = x509.KeyUsage