mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-27 12:46:18 +08:00
sm2: SM2EnvelopedKey support
This commit is contained in:
parent
35a2ebe48f
commit
a47ae96293
127
sm2/sm2_envelopedkey.go
Normal file
127
sm2/sm2_envelopedkey.go
Normal file
@ -0,0 +1,127 @@
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/emmansun/gmsm/cipher"
|
||||
"github.com/emmansun/gmsm/sm4"
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
|
||||
)
|
||||
|
||||
var (
|
||||
oidSM4 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104}
|
||||
oidSM4ECB = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 1}
|
||||
)
|
||||
|
||||
// MarshalEnvelopedPrivateKey, returns sm2 key pair protected data with ASN.1 format:
|
||||
//
|
||||
// SM2EnvelopedKey ::= SEQUENCE {
|
||||
// symAlgID AlgorithmIdentifier,
|
||||
// sysmEncryptedKey SM2Cipher,
|
||||
// sm2PublicKey SM2PublicKey,
|
||||
// sm2EncryptedPrivateKey BIT STRING,
|
||||
// }
|
||||
//
|
||||
// This implementation follows GB/T 35276-2017, uses SM4 cipher to encrypt sm2 private key.
|
||||
func MarshalEnvelopedPrivateKey(rand io.Reader, pub *ecdsa.PublicKey, tobeEnveloped *PrivateKey) ([]byte, error) {
|
||||
// encrypt sm2 private key
|
||||
size := (tobeEnveloped.Curve.Params().N.BitLen() + 7) / 8
|
||||
if tobeEnveloped.D.BitLen() > size*8 {
|
||||
return nil, errors.New("sm2: invalid private key")
|
||||
}
|
||||
plaintext := tobeEnveloped.D.FillBytes(make([]byte, size))
|
||||
|
||||
key := make([]byte, sm4.BlockSize)
|
||||
if _, err := io.ReadFull(rand, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := cipher.NewECBEncrypter(block)
|
||||
|
||||
encryptedPrivateKey := make([]byte, len(plaintext))
|
||||
mode.CryptBlocks(encryptedPrivateKey, plaintext)
|
||||
|
||||
// encrypt the symmetric key
|
||||
encryptedKey, err := EncryptASN1(rand, pub, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// marshal the result
|
||||
var b cryptobyte.Builder
|
||||
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) {
|
||||
b.AddASN1ObjectIdentifier(oidSM4) // use oidSM4ECB?
|
||||
b.AddBytes(encryptedKey)
|
||||
b.AddASN1BitString(elliptic.Marshal(tobeEnveloped.Curve, tobeEnveloped.X, tobeEnveloped.Y))
|
||||
b.AddASN1BitString(encryptedPrivateKey)
|
||||
})
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// ParseEnvelopedPrivateKey, parses and decrypts the enveloped SM2 private key.
|
||||
// This methed just supports SM4 cipher now.
|
||||
func ParseEnvelopedPrivateKey(priv *PrivateKey, enveloped []byte) (*PrivateKey, error) {
|
||||
// unmarshal the asn.1 data
|
||||
var (
|
||||
symAlgId asn1.ObjectIdentifier
|
||||
encryptedPrivateKey, pub asn1.BitString
|
||||
inner, symEncryptedKey cryptobyte.String
|
||||
)
|
||||
input := cryptobyte.String(enveloped)
|
||||
if !input.ReadASN1(&inner, cryptobyte_asn1.SEQUENCE) ||
|
||||
!input.Empty() ||
|
||||
!inner.ReadASN1ObjectIdentifier(&symAlgId) ||
|
||||
!inner.ReadASN1Element(&symEncryptedKey, cryptobyte_asn1.SEQUENCE) ||
|
||||
!inner.ReadASN1BitString(&pub) ||
|
||||
!inner.ReadASN1BitString(&encryptedPrivateKey) ||
|
||||
!inner.Empty() {
|
||||
return nil, errors.New("sm2: invalid asn1 format enveloped key")
|
||||
}
|
||||
|
||||
if !(symAlgId.Equal(oidSM4) || symAlgId.Equal(oidSM4ECB)) {
|
||||
return nil, fmt.Errorf("sm2: unsupported symmetric cipher <%v>", symAlgId)
|
||||
}
|
||||
|
||||
// parse public key
|
||||
x, y := elliptic.Unmarshal(P256(), pub.RightAlign())
|
||||
if x == nil || y == nil {
|
||||
return nil, errors.New("sm2: invald public key in enveloped data")
|
||||
}
|
||||
|
||||
// decrypt symmetric cipher key
|
||||
key, err := priv.Decrypt(rand.Reader, symEncryptedKey, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// decrypt sm2 private key
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := cipher.NewECBDecrypter(block)
|
||||
bytes := encryptedPrivateKey.RightAlign()
|
||||
plaintext := make([]byte, len(bytes))
|
||||
mode.CryptBlocks(plaintext, bytes)
|
||||
sm2Key := new(PrivateKey)
|
||||
sm2Key.D = new(big.Int).SetBytes(plaintext)
|
||||
sm2Key.Curve = P256()
|
||||
sm2Key.X, sm2Key.Y = sm2Key.ScalarBaseMult(plaintext)
|
||||
|
||||
if sm2Key.X.Cmp(x) != 0 || sm2Key.Y.Cmp(y) != 0 {
|
||||
return nil, errors.New("sm2: mismatch key pair in enveloped data")
|
||||
}
|
||||
|
||||
return sm2Key, nil
|
||||
}
|
50
sm2/sm2_envelopedkey_test.go
Normal file
50
sm2/sm2_envelopedkey_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
package sm2_test
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
)
|
||||
|
||||
func TestMarshalEnvelopedPrivateKey(t *testing.T) {
|
||||
priv, _ := sm2.GenerateKey(rand.Reader)
|
||||
tobeEnveloped, _ := sm2.GenerateKey(rand.Reader)
|
||||
|
||||
result, err := sm2.MarshalEnvelopedPrivateKey(rand.Reader, &priv.PublicKey, tobeEnveloped)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parsedKey, err := sm2.ParseEnvelopedPrivateKey(priv, result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !tobeEnveloped.Equal(parsedKey) {
|
||||
t.Error("not same key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEnvelopedPrivateKey(t *testing.T) {
|
||||
key, _ := hex.DecodeString("622dddddf4658c971e6485f1599a814a9aa0161aadcbc4f880d5841ea79561cb")
|
||||
sm2Key := new(sm2.PrivateKey)
|
||||
sm2Key.D = new(big.Int).SetBytes(key)
|
||||
sm2Key.Curve = sm2.P256()
|
||||
sm2Key.X, sm2Key.Y = sm2Key.ScalarBaseMult(key)
|
||||
|
||||
invalidASN1, _ := hex.DecodeString("3081ea06082a811ccf550168013079022003858a7ca681c2e7034804d2bcece2d1c200e128ca973f3ad12541b59ec639cd022100bcf5834c775d5d43615abc27d3aeee399985d30942c65cdbe95afc87d96b12860420f84efafe256413fb28af65a57d815cb9a2fc64f754ab29adc1a78e81c433cfe90410fd485762e9c5714a6ee008e76675a14c0441049355f3009f1db15d6a6f751531f3c4741a36a43d1146fc1b0f660314e5fc3b825ed2fda18cb2f624ac6afb370b3755bb267b5747dd8f15836c830b52d4a74d2c04206fd2ef53be43aaa7f0440e96aafd846096f993e254e2a79a9a5b583204487183")
|
||||
if _, err := sm2.ParseEnvelopedPrivateKey(sm2Key, invalidASN1); err.Error() != "sm2: invalid asn1 format enveloped key" {
|
||||
t.Errorf("expected asn1 error, got %s", err)
|
||||
}
|
||||
|
||||
decryptErr, _ := hex.DecodeString("3081ed06082a811ccf55016801307a022100e5ef46b1d4ebd964852e4166d345027625a38e0a17ad41c9febc7bf024f5efc7022100ca5bf589d32f8e9312196fcbb2624442f16e78470ee09dcf770e54eb28a2f3a9042084c55d419c24dbaa5814e9fde8f74e43c0a876a2055f9900ec6d25fd81e1a42104103c6ca06f337cfc666bf59fb02ad1d8d503420004e82a429129f2d73231edcf06f4dad403de94cae7ad565dd3dd511f7d404bef9edcf4e4c856808d797db90bae9ff1f77f6041435ded07b5d783605f5681c17681032100002f5ba3a33feb59e67be3ae4b087bcc42fec46e2d7f15f3b86162ab83965c74")
|
||||
if _, err := sm2.ParseEnvelopedPrivateKey(sm2Key, decryptErr); err.Error() != "sm2: decryption error" {
|
||||
t.Errorf("expected decrypt error, got %s", err)
|
||||
}
|
||||
|
||||
invalidOID, _ := hex.DecodeString("3081ec06082a811ccf550168023079022010cae556013f072ae40873b3e0a4cef6bc841277da233b12f3d8676bb9b0f8a8022100f626c7122e8b7d977d60694bf876433a5a1a9298c109b541d35b928b8eed43550420423c303ec2c8ee14dcea529ffb887c781dcc2e0fc7b77acd1d355d6d344f62700410b9365ed091fde6bbd8152d09fa3a24e10342000445b8fadb3e0f932bfebfead4d686e8b3b7d215d60c4893b18152e3cf35eede1b06e62ca4943bfc8fd47d5651605b808bdde3f701e4d51783485903cfba6cc812032100c9fd8695e09d25c9974ae82f519aa1cdd7b7a7f29c0a5aa8cc93ec6384222fca")
|
||||
if _, err := sm2.ParseEnvelopedPrivateKey(sm2Key, invalidOID); err.Error() != "sm2: unsupported symmetric cipher <1.2.156.10197.1.104.2>" {
|
||||
t.Errorf("expected invalid oid error, got %s", err)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user