provide convient methods to parse pkcs8 sm9 keys

This commit is contained in:
Sun Yimin 2022-10-24 16:09:12 +08:00 committed by GitHub
parent a7c36d7f30
commit 5db7e63360
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 205 additions and 28 deletions

View File

@ -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.
//

View File

@ -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(),
} {

View File

@ -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)

View File

@ -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)
}
}