Skip to content

Commit

Permalink
pkcs7: sign precomputed digest #294
Browse files Browse the repository at this point in the history
  • Loading branch information
emmansun authored Jan 14, 2025
1 parent 5a0ff81 commit bef7f54
Show file tree
Hide file tree
Showing 10 changed files with 664 additions and 31 deletions.
2 changes: 1 addition & 1 deletion cfca/pkcs12_sm2.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package cfca supports part of CFCA SADK's functions.
// Package cfca supports part of CFCA SADK's functions, provides interoperability with CFCA SADK.
package cfca

import (
Expand Down
25 changes: 25 additions & 0 deletions cfca/pkcs7_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,28 @@ func VerifyMessageDetach(p7Der, sourceData []byte) error {
p7.Content = sourceData
return p7.Verify()
}

// SignDigestDetach signs a given digest using the provided certificate and private key,
// and returns the detached PKCS7 signature.
//
// This method corresponds to CFCA SADK's cfca.sadk.util.p7SignByHash.
func SignDigestDetach(digest []byte, cert *smx509.Certificate, key crypto.PrivateKey) ([]byte, error) {
signData, _ := pkcs7.NewSMSignedDataWithDegist(digest)
if err := signData.SignWithoutAttr(cert, key, pkcs7.SignerInfoConfig{}); err != nil {
return nil, err
}
return signData.Finish()
}

// VerifyDigestDetach verifies a detached PKCS7 signature against a given digest.
// It parses the p7Der, assigns the provided digest to the parsed PKCS7 content, and then verifies it.
//
// This method corresponds to CFCA SADK's cfca.sadk.util.p7VerifyByHash.
func VerifyDigestDetach(p7Der, digest []byte) error {
p7, err := pkcs7.Parse(p7Der)
if err != nil {
return err
}
p7.Content = digest
return p7.VerifyAsDigest()
}
31 changes: 31 additions & 0 deletions cfca/pkcs7_sign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
package cfca

import (
"crypto/ecdsa"
"encoding/base64"
"testing"

"github.com/emmansun/gmsm/sm2"
)

func TestSignMessageAttach(t *testing.T) {
Expand Down Expand Up @@ -89,3 +92,31 @@ func TestSignMessageDetach(t *testing.T) {

var sadkSignedData = "MIICgAYKKoEcz1UGAQQCAqCCAnAwggJsAgEBMQ4wDAYIKoEcz1UBgxEFADAjBgoqgRzPVQYBBAIBoBUEE0hlbGxvIFNlY3JldCBXb3JsZCGgggGNMIIBiTCCAS+gAwIBAgIFAKncGpAwCgYIKoEcz1UBg3UwKTEQMA4GA1UEChMHQWNtZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTI0MTExOTAwMTIyNVoXDTI1MTExOTAwMTIyNlowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNub3cwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAATYcgrHXJmFO1/t/9WQ6GkCW6D0yDyd2ya5wRXjVAU08I9Oo6k99jB2MPauCn64W81APRCPHLlwWOtuIsmSmQhjo0gwRjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwQwHwYDVR0jBBgwFoAUyBfYaeGJxaf9ST9aCRgotC+MwvwwCgYIKoEcz1UBg3UDSAAwRQIgRaF0PA74cCYKeu8pZ4VDQti+rE283Hq/tGXzXUzOWKUCIQDl3z1boZxtRscbnOGOXg1NY+yoY2lz5b63kGOTkn/SxzGBoDCBnQIBATAyMCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyawIFAKncGpAwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARHMEUCIQCl145xtYc7QWTymATxUGbLfF1mlPlyMoIKSp9alu14UQIgQSV/Ll3yYCyXSNxhPelz8Nsbxopky+Pt56Al54rv3p0="
var sadkSignedDataDetach = "MIICaQYKKoEcz1UGAQQCAqCCAlkwggJVAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIIBjTCCAYkwggEvoAMCAQICBQCp3BqQMAoGCCqBHM9VAYN1MCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0yNDExMTkwMDEyMjVaFw0yNTExMTkwMDEyMjZaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBTbm93MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE2HIKx1yZhTtf7f/VkOhpAlug9Mg8ndsmucEV41QFNPCPTqOpPfYwdjD2rgp+uFvNQD0Qjxy5cFjrbiLJkpkIY6NIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMEMB8GA1UdIwQYMBaAFMgX2GnhicWn/Uk/WgkYKLQvjML8MAoGCCqBHM9VAYN1A0gAMEUCIEWhdDwO+HAmCnrvKWeFQ0LYvqxNvNx6v7Rl811MzlilAiEA5d89W6GcbUbHG5zhjl4NTWPsqGNpc+W+t5Bjk5J/0scxgaAwgZ0CAQEwMjApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQCp3BqQMAwGCCqBHM9VAYMRBQAwDQYJKoEcz1UBgi0BBQAERzBFAiEA4ylCl8qQDfNDfBw7VkxVN0bUs4N56TZDqZhAdEv01N8CIDtOG5VbmWNZeagC8VRfzEhu+ratFCo3fTu2liV8kH5h"

func TestSignDigestDetach(t *testing.T) {
_, err := SignDigestDetach(nil, nil, nil)
if err == nil {
t.Fatalf("SignDigestDetach() error = %v, wantErr %v", err, true)
}
pair, err := createTestSM2Certificate(false)
if err != nil {
t.Fatal(err)
}
rawMessage := []byte("test")
digest, err := sm2.CalculateSM2Hash(pair.Certificate.PublicKey.(*ecdsa.PublicKey), rawMessage, nil)
if err != nil {
t.Fatal(err)
}
p7, err := SignDigestDetach(digest, pair.Certificate, pair.PrivateKey)
if err != nil {
t.Fatal(err)
}
err = VerifyDigestDetach(p7, digest)
if err != nil {
t.Fatal(err)
}
err = VerifyMessageDetach(p7, rawMessage)
if err != nil {
t.Fatal(err)
}
}
1 change: 1 addition & 0 deletions docs/cfca.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ SADK(Security Application Development Kit)是CFCA推出的一套支持全平
其JAVA版本(其它语言版本未知)基本上是一个基于[Bouncy Castle](https://www.bouncycastle.org/)的实现(当然它看起来也支持JNI接入OpenSSL、U盾?)。

## 为什么会有互操作性问题
* CFCA有一些实现没有相关标准。
* SADK存在较早,可能有些实现早于标准发布。
* SADK版本较多,不同版本也会有互操作性兼容问题。
* 其它未知原因。
Expand Down
57 changes: 49 additions & 8 deletions pkcs7/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type SignedData struct {
sd signedData
certs []*smx509.Certificate
data []byte
isDigest bool
contentTypeOid asn1.ObjectIdentifier
digestOid asn1.ObjectIdentifier
encryptionOid asn1.ObjectIdentifier
Expand All @@ -47,6 +48,22 @@ func NewSignedData(data []byte) (*SignedData, error) {
return &SignedData{sd: sd, data: data, digestOid: OIDDigestAlgorithmSHA1, contentTypeOid: OIDSignedData}, nil
}

// NewSignedDataWithDegist creates a new SignedData instance using the provided digest.
// It sets the isDigest field to true, indicating that the input is already a digest.
// Returns the SignedData instance or an error if the creation fails.
func NewSignedDataWithDegist(digest []byte) (*SignedData, error) {
ci := contentInfo{
ContentType: OIDData,
Content: asn1.RawValue{}, // for sign digest, content is empty
}
sd := signedData{
ContentInfo: ci,
Version: 1,
}

return &SignedData{sd: sd, data: digest, digestOid: OIDDigestAlgorithmSHA1, contentTypeOid: OIDSignedData, isDigest: true}, nil
}

// NewSMSignedData takes data and initializes a PKCS7 SignedData struct that is
// ready to be signed via AddSigner. The digest algorithm is set to SM3 by default
// and can be changed by calling SetDigestAlgorithm.
Expand All @@ -61,6 +78,20 @@ func NewSMSignedData(data []byte) (*SignedData, error) {
return sd, nil
}

// NewSMSignedDataWithDegist creates a new SignedData object using the provided digest.
// It calls the NewSMSignedData function with the given digest and sets the isDigest flag to true.
// If there is an error during the creation of the SignedData object, it returns the error.
func NewSMSignedDataWithDegist(digest []byte) (*SignedData, error) {
sd, err := NewSignedDataWithDegist(digest)
if err != nil {
return nil, err
}
sd.sd.ContentInfo.ContentType = SM2OIDData
sd.digestOid = OIDDigestAlgorithmSM3
sd.contentTypeOid = SM2OIDSignedData
return sd, nil
}

// SignerInfoConfig are optional values to include when adding a signer
type SignerInfoConfig struct {
ExtraSignedAttributes []Attribute
Expand Down Expand Up @@ -203,9 +234,12 @@ func (sd *SignedData) signWithAttributes(pkey crypto.PrivateKey, config SignerIn
if err != nil {
return nil, nil, err
}
h := newHash(hasher, sd.digestOid)
h.Write(sd.data)
messageDigest := h.Sum(nil)
messageDigest := sd.data
if !sd.isDigest {
h := newHash(hasher, sd.digestOid)
h.Write(sd.data)
messageDigest = h.Sum(nil)
}

attrs := &attributes{}
attrs.Add(OIDAttributeContentType, sd.sd.ContentInfo.ContentType)
Expand Down Expand Up @@ -250,9 +284,10 @@ func (sd *SignedData) SignWithoutAttr(ee *smx509.Certificate, pkey crypto.Privat
if err != nil {
return err
}
if signature, err = signData(sd.data, pkey, hasher); err != nil {
if signature, err = signData(sd.data, pkey, hasher, sd.isDigest); err != nil {
return err
}

var ias issuerAndSerial
ias.SerialNumber = ee.SerialNumber
// no parent, the issue is the end-entity cert itself
Expand Down Expand Up @@ -377,12 +412,12 @@ func signAttributes(attrs []attribute, pkey crypto.PrivateKey, hasher crypto.Has
if err != nil {
return nil, err
}
return signData(attrBytes, pkey, hasher)
return signData(attrBytes, pkey, hasher, false)
}

// signData signs the provided data using the given private key and hash function.
// It returns the signed data or an error if the signing process fails.
func signData(data []byte, pkey crypto.PrivateKey, hasher crypto.Hash) ([]byte, error) {
func signData(data []byte, pkey crypto.PrivateKey, hasher crypto.Hash, isDigest bool) ([]byte, error) {
key, ok := pkey.(crypto.Signer)
if !ok {
return nil, errors.New("pkcs7: private key does not implement crypto.Signer")
Expand All @@ -392,7 +427,11 @@ func signData(data []byte, pkey crypto.PrivateKey, hasher crypto.Hash) ([]byte,

if !hasher.Available() {
if sm2.IsSM2PublicKey(key.Public()) {
opts = sm2.DefaultSM2SignerOpts
if !isDigest {
opts = sm2.DefaultSM2SignerOpts
} else if len(hash) != sm3.Size {
return nil, fmt.Errorf("pkcs7: invalid hash value fo SM2 signature")
}
switch realKey := key.(type) {
case *ecdsa.PrivateKey:
{
Expand All @@ -404,10 +443,12 @@ func signData(data []byte, pkey crypto.PrivateKey, hasher crypto.Hash) ([]byte,
} else {
return nil, fmt.Errorf("pkcs7: unsupported hash function %s", hasher)
}
} else {
} else if !isDigest {
h := hasher.New()
h.Write(data)
hash = h.Sum(nil)
} else if len(hash) != hasher.Size() {
return nil, fmt.Errorf("pkcs7: invalid hash for %s", hasher)
}
return key.Sign(rand.Reader, hash, opts)
}
Expand Down
2 changes: 1 addition & 1 deletion pkcs7/sign_enveloped.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func (saed *SignedAndEnvelopedData) AddSignerChain(ee *smx509.Certificate, pkey
if err != nil {
return err
}
signature, err := signData(saed.data, pkey, hasher)
signature, err := signData(saed.data, pkey, hasher, false)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit bef7f54

Please sign in to comment.