Skip to content

Commit

Permalink
pkcs7: 签名支持外部hash
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangyongding committed Jan 13, 2025
1 parent 5a0ff81 commit cbdb454
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 91 deletions.
1 change: 1 addition & 0 deletions pkcs7/pkcs7.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type PKCS7 struct {
Certificates []*smx509.Certificate
CRLs []pkix.CertificateList
Signers []signerInfo
isDigest bool
raw any
session Session
}
Expand Down
28 changes: 21 additions & 7 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 Down Expand Up @@ -129,6 +130,10 @@ func (sd *SignedData) SetEncryptionAlgorithm(d asn1.ObjectIdentifier) {
sd.encryptionOid = d
}

func (sd *SignedData) SetIsDigest() {
sd.isDigest = true
}

// AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent.
func (sd *SignedData) AddSigner(ee *smx509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
var parents []*smx509.Certificate
Expand Down Expand Up @@ -250,7 +255,7 @@ 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
Expand Down Expand Up @@ -377,22 +382,29 @@ 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")
}
hash := data
var opts crypto.SignerOpts = hasher
if isDigest {
opts = crypto.Hash(0)
}

if !hasher.Available() {
if sm2.IsSM2PublicKey(key.Public()) {
opts = sm2.DefaultSM2SignerOpts
if isDigest {
opts = sm2.NewSM2SignerOption(false, nil)
} else {
opts = sm2.DefaultSM2SignerOpts
}
switch realKey := key.(type) {
case *ecdsa.PrivateKey:
{
Expand All @@ -405,9 +417,11 @@ func signData(data []byte, pkey crypto.PrivateKey, hasher crypto.Hash) ([]byte,
return nil, fmt.Errorf("pkcs7: unsupported hash function %s", hasher)
}
} else {
h := hasher.New()
h.Write(data)
hash = h.Sum(nil)
if !isDigest {
h := hasher.New()
h.Write(data)
hash = h.Sum(nil)
}
}
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
217 changes: 135 additions & 82 deletions pkcs7/sign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pkcs7

import (
"bytes"
"crypto/ecdsa"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
Expand All @@ -12,6 +13,7 @@ import (
"os/exec"
"testing"

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

Expand All @@ -36,51 +38,75 @@ func testSign(t *testing.T, isSM bool, content []byte, sigalgs []x509.SignatureA
t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
for _, testDetach := range []bool{false, true} {
log.Printf("test %s/%s/%s detached %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach)
var toBeSigned *SignedData
if isSM {
toBeSigned, err = NewSMSignedData(content)
} else {
toBeSigned, err = NewSignedData(content)
}
if err != nil {
t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
for _, testDigest := range []bool{false, true} {
log.Printf("test %s/%s/%s detached %t hashed %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach, testDigest)
signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalgsigner)
data := content
if testDigest {
if isSM {
data, err = sm2.CalculateSM2Hash(signerCert.Certificate.PublicKey.(*ecdsa.PublicKey), content, nil)
if err != nil {
t.Fatalf("test %s/%s/%s: cannot generate hash: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
} else {
hasher, err := getHashForOID(signerDigest)
if err != nil {
t.Fatalf("test %s/%s/%s: cannot generate hash: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
h := newHash(hasher, signerDigest)
h.Write(content)
data = h.Sum(nil)
}
}
var toBeSigned *SignedData
if isSM {
toBeSigned, err = NewSMSignedData(data)
} else {
toBeSigned, err = NewSignedData(data)
}
toBeSigned.isDigest = testDigest

// Set the digest to match the end entity cert
signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalgsigner)
toBeSigned.SetDigestAlgorithm(signerDigest)
if err != nil {
t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}

if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil {
t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
if testDetach {
toBeSigned.Detach()
}
signed, err := toBeSigned.Finish()
if err != nil {
t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed})
p7, err := Parse(signed)
if err != nil {
t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
if testDetach {
// Detached signature should not contain the content
// So we should not be able to find the content in the parsed data
// We should suppliment the content to the parsed data before verifying
p7.Content = content
}
if !bytes.Equal(content, p7.Content) {
t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content)
}
if err := p7.VerifyWithChain(truststore); err != nil {
t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) {
t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q",
sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm)
// Set the digest to match the end entity cert
toBeSigned.SetDigestAlgorithm(signerDigest)

if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil {
t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
if testDetach {
toBeSigned.Detach()
}
signed, err := toBeSigned.Finish()
if err != nil {
t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed})
p7, err := Parse(signed)
if err != nil {
t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
if testDetach {
// Detached signature should not contain the content
// So we should not be able to find the content in the parsed data
// We should suppliment the content to the parsed data before verifying
p7.Content = data
}
if !bytes.Equal(data, p7.Content) {
t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content)
}
if testDigest {
p7.SetIsDigest()
}
if err := p7.VerifyWithChain(truststore); err != nil {
t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
}
if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) {
t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q",
sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm)
}
}
}
}
Expand Down Expand Up @@ -299,52 +325,79 @@ func TestSignWithoutAttr(t *testing.T) {
},
}
for _, sigalg := range sigalgs {
cert, err := createTestCertificate(sigalg.sigAlg, false)
if err != nil {
t.Fatal(err)
}
var toBeSigned *SignedData
if sigalg.isSM {
toBeSigned, err = NewSMSignedData(content)
} else {
toBeSigned, err = NewSignedData(content)
for _, testDigest := range []bool{false, true} {
cert, err := createTestCertificate(sigalg.sigAlg, false)
if err != nil {
t.Fatal(err)
}

signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalg.sigAlg)
toBeSigned.SetDigestAlgorithm(signerDigest)
}
if err != nil {
t.Fatalf("Cannot initialize signed data: %s", err)
}
if err := toBeSigned.SignWithoutAttr(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{SkipCertificates: sigalg.skipCert}); err != nil {
t.Fatalf("Cannot add signer: %s", err)
}
signed, err := toBeSigned.Finish()
if err != nil {
t.Fatalf("Cannot finish signing data: %s", err)
}
p7, err := Parse(signed)
if err != nil {
t.Fatalf("Cannot parse signed data: %v", err)
}
if !sigalg.skipCert {
if len(p7.Certificates) == 0 {
t.Errorf("No certificates")
data := content
if testDigest {
if sigalg.isSM {
priv := (*cert.PrivateKey).(*sm2.PrivateKey)
data, err = sm2.CalculateSM2Hash(&priv.PublicKey, content, nil)
if err != nil {
t.Fatal(err)
}
} else {
hasher, err := getHashForOID(signerDigest)
if err != nil {
t.Fatal(err)
}
h := newHash(hasher, signerDigest)
h.Write(content)
data = h.Sum(nil)
}
}

var toBeSigned *SignedData
if sigalg.isSM {
toBeSigned, err = NewSMSignedData(data)
} else {
toBeSigned, err = NewSignedData(data)
toBeSigned.SetDigestAlgorithm(signerDigest)
}
err = p7.Verify()
if err != nil {
t.Fatal(err)
t.Fatalf("Cannot initialize signed data: %s", err)
}
} else {
if len(p7.Certificates) > 0 {
t.Errorf("No certificates expected")
toBeSigned.isDigest = testDigest

if err := toBeSigned.SignWithoutAttr(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{SkipCertificates: sigalg.skipCert}); err != nil {
t.Fatalf("Cannot add signer: %s", err)
}
err = p7.Verify()
if sigalg.skipCert && err.Error() != "pkcs7: No certificate for signer" {
t.Fatalf("Expected pkcs7: No certificate for signer")
signed, err := toBeSigned.Finish()
if err != nil {
t.Fatalf("Cannot finish signing data: %s", err)
}
p7.Certificates = append(p7.Certificates, cert.Certificate)
err = p7.Verify()
p7, err := Parse(signed)
if err != nil {
t.Fatal(err)
t.Fatalf("Cannot parse signed data: %v", err)
}
if testDigest {
p7.SetIsDigest()
}
if !sigalg.skipCert {
if len(p7.Certificates) == 0 {
t.Errorf("No certificates")
}
err = p7.Verify()
if err != nil {
t.Fatal(err)
}
} else {
if len(p7.Certificates) > 0 {
t.Errorf("No certificates expected")
}
err = p7.Verify()
if sigalg.skipCert && err.Error() != "pkcs7: No certificate for signer" {
t.Fatalf("Expected pkcs7: No certificate for signer")
}
p7.Certificates = append(p7.Certificates, cert.Certificate)
err = p7.Verify()
if err != nil {
t.Fatal(err)
}
}
}
}
Expand Down
Loading

0 comments on commit cbdb454

Please sign in to comment.