From 6a98350f2e6d7d1020b05e2b6fcb098ded34884b Mon Sep 17 00:00:00 2001 From: Sun Yimin Date: Wed, 11 Dec 2024 17:51:31 +0800 Subject: [PATCH] cfca: extract encrypt/decrypt functions with SM4CBC and SM3 KDF --- cfca/encrypt.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++ cfca/pkcs12_sm2.go | 33 +++++---------------------- docs/cfca.md | 7 +++--- 3 files changed, 67 insertions(+), 30 deletions(-) create mode 100644 cfca/encrypt.go diff --git a/cfca/encrypt.go b/cfca/encrypt.go new file mode 100644 index 0000000..cddfbe9 --- /dev/null +++ b/cfca/encrypt.go @@ -0,0 +1,57 @@ +// Copyright 2024 Sun Yimin. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cfca + +import ( + "crypto/cipher" + "errors" + + "github.com/emmansun/gmsm/padding" + "github.com/emmansun/gmsm/sm3" + "github.com/emmansun/gmsm/sm4" +) + +// NewSM4CBCBlockMode creates a new SM4-CBC block mode with the password. +func NewSM4CBCBlockMode(password []byte, isEncrypter bool) (cipher.BlockMode, error) { + if len(password) == 0 { + return nil, errors.New("cfca: invalid password") + } + ivkey := sm3.Kdf(password, 32) + block, err := sm4.NewCipher(ivkey[16:]) + if err != nil { + return nil, err + } + if isEncrypter { + return cipher.NewCBCEncrypter(block, ivkey[:16]), nil + } + return cipher.NewCBCDecrypter(block, ivkey[:16]), nil +} + +// EncryptBySM4CBC encrypts the data with the password using SM4-CBC algorithm. +// Corresponds to the cfca.sadk.util.encryptMessageBySM4 method. +func EncryptBySM4CBC(plaintext, password []byte) ([]byte, error) { + mode, err := NewSM4CBCBlockMode(password, true) + if err != nil { + return nil, err + } + pkcs7 := padding.NewPKCS7Padding(uint(mode.BlockSize())) + plaintext = pkcs7.Pad(plaintext) + ciphertext := make([]byte, len(plaintext)) + mode.CryptBlocks(ciphertext, plaintext) + return ciphertext, nil +} + +// DecryptBySM4CBC decrypts the data with the password using SM4-CBC algorithm. +// Corresponds to the cfca.sadk.util.decryptMessageBySM4 method. +func DecryptBySM4CBC(ciphertext, password []byte) ([]byte, error) { + mode, err := NewSM4CBCBlockMode(password, false) + if err != nil { + return nil, err + } + plaintext := make([]byte, len(ciphertext)) + mode.CryptBlocks(plaintext, ciphertext) + pkcs7 := padding.NewPKCS7Padding(uint(mode.BlockSize())) + return pkcs7.Unpad(plaintext) +} diff --git a/cfca/pkcs12_sm2.go b/cfca/pkcs12_sm2.go index dd2237f..11a5376 100644 --- a/cfca/pkcs12_sm2.go +++ b/cfca/pkcs12_sm2.go @@ -2,18 +2,14 @@ package cfca import ( - "crypto/cipher" "encoding/asn1" "errors" "fmt" "math/big" - "github.com/emmansun/gmsm/padding" "github.com/emmansun/gmsm/pkcs" "github.com/emmansun/gmsm/pkcs7" "github.com/emmansun/gmsm/sm2" - "github.com/emmansun/gmsm/sm3" - "github.com/emmansun/gmsm/sm4" "github.com/emmansun/gmsm/smx509" ) @@ -59,18 +55,11 @@ func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, erro if !keys.EncryptedKey.Algorithm.Equal(oidSM4) && !keys.EncryptedKey.Algorithm.Equal(oidSM4CBC) { return nil, nil, fmt.Errorf("cfca: unsupported algorithm <%v>", keys.EncryptedKey.Algorithm) } - ivkey := sm3.Kdf(password, 32) - marshalledIV, err := asn1.Marshal(ivkey[:16]) + pk, err := DecryptBySM4CBC(keys.EncryptedKey.EncryptedContent.Bytes, password) if err != nil { return nil, nil, err } - pk, err := pkcs.SM4CBC.Decrypt(ivkey[16:], &asn1.RawValue{FullBytes: marshalledIV}, keys.EncryptedKey.EncryptedContent.Bytes) - if err != nil { - return nil, nil, err - } - d := new(big.Int).SetBytes(pk) // here we do NOT check if the d is in (0, N) or not - // Create private key from *big.Int - prvKey, err := sm2.NewPrivateKeyFromInt(d) + prvKey, err := sm2.NewPrivateKeyFromInt(new(big.Int).SetBytes(pk)) if err != nil { return nil, nil, err } @@ -87,22 +76,12 @@ func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, erro // MarshalSM2 encodes sm2 private key and related certificate to cfca defined format func MarshalSM2(password []byte, key *sm2.PrivateKey, cert *smx509.Certificate) ([]byte, error) { - if len(password) == 0 { - return nil, errors.New("cfca: invalid password") - } - ivkey := sm3.Kdf(password, 32) - block, err := sm4.NewCipher(ivkey[16:]) - if err != nil { + var err error + var ciphertext []byte + if ciphertext, err = EncryptBySM4CBC(key.D.Bytes(), password); err != nil { return nil, err } - mode := cipher.NewCBCEncrypter(block, ivkey[:16]) - pkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize())) - plainText := pkcs7.Pad(key.D.Bytes()) - ciphertext := make([]byte, len(plainText)) - mode.CryptBlocks(ciphertext, plainText) - - ciphertext, err = asn1.Marshal(ciphertext) - if err != nil { + if ciphertext, err = asn1.Marshal(ciphertext); err != nil { return nil, err } diff --git a/docs/cfca.md b/docs/cfca.md index 8768148..3b35e80 100644 --- a/docs/cfca.md +++ b/docs/cfca.md @@ -100,8 +100,9 @@ SADK 3.2之后的版本,支持下列SM2密文格式(encryptedType): * 0x04 - C1为非压缩点格式,具体是C1C3C2还是C1C2C3取决于解密时的选项参数,默认为C1C3C2。 * 0x02/0x03 - C1为压缩点格式,具体是C1C3C2还是C1C2C3取决于解密时的选项参数,默认为C1C3C2。 +### 生成双密钥CSR (v0.29.6+) +`cfca.CreateCertificateRequest`,和CFCA SADK不同,调用者需要自行先生成两对密钥对,一对用于签名证书,一对用于加解密CFCA生成的加密用私钥文件(CFCA加密,申请者解密)。 +使用`cfca.ParseEscrowPrivateKey`解析CFCA返回的加密用私钥。 + ### SM2私钥、证书的解析 这个是CFCA自定义的,未见相关标准,可以通过```cfca.ParseSM2```来解析。 - -### 生成双密钥CSR -`cfca.CreateCertificateRequest`,和CFCA SADK不同,调用者需要自行先生成两对密钥对,一对用于签名证书,一对用于加解密CFCA生成的加密用私钥文件(CFCA加密,申请者解密)。