Skip to content

Commit

Permalink
cfca: extract encrypt/decrypt functions with SM4CBC and SM3 KDF
Browse files Browse the repository at this point in the history
  • Loading branch information
emmansun authored Dec 11, 2024
1 parent 6553262 commit 6a98350
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 30 deletions.
57 changes: 57 additions & 0 deletions cfca/encrypt.go
Original file line number Diff line number Diff line change
@@ -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)
}
33 changes: 6 additions & 27 deletions cfca/pkcs12_sm2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
}
Expand All @@ -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
}

Expand Down
7 changes: 4 additions & 3 deletions docs/cfca.md
Original file line number Diff line number Diff line change
Expand Up @@ -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加密,申请者解密)。

0 comments on commit 6a98350

Please sign in to comment.