github.com/emmansun/gmsm@v0.29.1/sm2/sm2_envelopedkey.go (about) 1 package sm2 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/elliptic" 6 "crypto/rand" 7 "crypto/x509/pkix" 8 "encoding/asn1" 9 "errors" 10 "fmt" 11 "io" 12 13 "github.com/emmansun/gmsm/cipher" 14 "github.com/emmansun/gmsm/sm4" 15 "golang.org/x/crypto/cryptobyte" 16 cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" 17 ) 18 19 var ( 20 oidSM4 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104} 21 oidSM4ECB = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 1} 22 ) 23 24 // MarshalEnvelopedPrivateKey, returns sm2 key pair protected data with ASN.1 format: 25 // 26 // SM2EnvelopedKey ::= SEQUENCE { 27 // symAlgID AlgorithmIdentifier, 28 // symEncryptedKey SM2Cipher, 29 // sm2PublicKey SM2PublicKey, 30 // sm2EncryptedPrivateKey BIT STRING, 31 // } 32 // 33 // This implementation follows GB/T 35276-2017, uses SM4 cipher to encrypt sm2 private key. 34 // Please note the standard did NOT clarify if the ECB mode requires padding or not. 35 // 36 // This function can be used in CSRResponse.encryptedPrivateKey, reference GM/T 0092-2020 37 // Specification of certificate request syntax based on SM2 cryptographic algorithm. 38 func MarshalEnvelopedPrivateKey(rand io.Reader, pub *ecdsa.PublicKey, tobeEnveloped *PrivateKey) ([]byte, error) { 39 // encrypt sm2 private key 40 size := (tobeEnveloped.Curve.Params().N.BitLen() + 7) / 8 41 if tobeEnveloped.D.BitLen() > size*8 { 42 return nil, errors.New("sm2: invalid private key") 43 } 44 plaintext := tobeEnveloped.D.FillBytes(make([]byte, size)) 45 46 key := make([]byte, sm4.BlockSize) 47 if _, err := io.ReadFull(rand, key); err != nil { 48 return nil, err 49 } 50 block, err := sm4.NewCipher(key) 51 if err != nil { 52 return nil, err 53 } 54 mode := cipher.NewECBEncrypter(block) 55 56 encryptedPrivateKey := make([]byte, len(plaintext)) 57 mode.CryptBlocks(encryptedPrivateKey, plaintext) 58 59 // encrypt the symmetric key 60 encryptedKey, err := EncryptASN1(rand, pub, key) 61 if err != nil { 62 return nil, err 63 } 64 65 symAlgID := pkix.AlgorithmIdentifier{ 66 Algorithm: oidSM4ECB, 67 Parameters: asn1.NullRawValue, 68 } 69 symAlgIDBytes, _ := asn1.Marshal(symAlgID) 70 71 // marshal the result 72 var b cryptobyte.Builder 73 b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { 74 b.AddBytes(symAlgIDBytes) 75 b.AddBytes(encryptedKey) 76 b.AddASN1BitString(elliptic.Marshal(tobeEnveloped.Curve, tobeEnveloped.X, tobeEnveloped.Y)) 77 b.AddASN1BitString(encryptedPrivateKey) 78 }) 79 return b.Bytes() 80 } 81 82 // ParseEnvelopedPrivateKey, parses and decrypts the enveloped SM2 private key. 83 // This methed just supports SM4 cipher now. 84 func ParseEnvelopedPrivateKey(priv *PrivateKey, enveloped []byte) (*PrivateKey, error) { 85 // unmarshal the asn.1 data 86 var ( 87 symAlgId pkix.AlgorithmIdentifier 88 encryptedPrivateKey, pub asn1.BitString 89 inner, symEncryptedKey, symAlgIdBytes cryptobyte.String 90 ) 91 input := cryptobyte.String(enveloped) 92 if !input.ReadASN1(&inner, cryptobyte_asn1.SEQUENCE) || 93 !input.Empty() || 94 !inner.ReadASN1Element(&symAlgIdBytes, cryptobyte_asn1.SEQUENCE) || 95 !inner.ReadASN1Element(&symEncryptedKey, cryptobyte_asn1.SEQUENCE) || 96 !inner.ReadASN1BitString(&pub) || 97 !inner.ReadASN1BitString(&encryptedPrivateKey) || 98 !inner.Empty() { 99 return nil, errors.New("sm2: invalid asn1 format enveloped key") 100 } 101 102 if _, err := asn1.Unmarshal(symAlgIdBytes, &symAlgId); err != nil { 103 return nil, err 104 } 105 106 if !(symAlgId.Algorithm.Equal(oidSM4) || symAlgId.Algorithm.Equal(oidSM4ECB)) { 107 return nil, fmt.Errorf("sm2: unsupported symmetric cipher <%v>", symAlgId.Algorithm) 108 } 109 110 // parse public key 111 pubKey, err := NewPublicKey(pub.RightAlign()) 112 if err != nil { 113 return nil, err 114 } 115 116 // decrypt symmetric cipher key 117 key, err := priv.Decrypt(rand.Reader, symEncryptedKey, nil) 118 if err != nil { 119 return nil, err 120 } 121 122 // decrypt sm2 private key 123 block, err := sm4.NewCipher(key) 124 if err != nil { 125 return nil, err 126 } 127 mode := cipher.NewECBDecrypter(block) 128 bytes := encryptedPrivateKey.RightAlign() 129 plaintext := make([]byte, len(bytes)) 130 mode.CryptBlocks(plaintext, bytes) 131 // Do we need to check length in order to be compatible with some implementations with padding? 132 sm2Key, err := NewPrivateKey(plaintext) 133 if err != nil { 134 return nil, err 135 } 136 if !sm2Key.PublicKey.Equal(pubKey) { 137 return nil, errors.New("sm2: mismatch key pair in enveloped data") 138 } 139 140 return sm2Key, nil 141 }