mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-27 04:36:19 +08:00
pkcs7: 签名支持外部hash
This commit is contained in:
parent
5a0ff81dc3
commit
cbdb454b14
@ -26,6 +26,7 @@ type PKCS7 struct {
|
||||
Certificates []*smx509.Certificate
|
||||
CRLs []pkix.CertificateList
|
||||
Signers []signerInfo
|
||||
isDigest bool
|
||||
raw any
|
||||
session Session
|
||||
}
|
||||
|
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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:
|
||||
{
|
||||
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
@ -12,6 +13,7 @@ import (
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
p7.Certificates = append(p7.Certificates, cert.Certificate)
|
||||
err = p7.Verify()
|
||||
signed, err := toBeSigned.Finish()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("Cannot finish signing data: %s", err)
|
||||
}
|
||||
p7, err := Parse(signed)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/subtle"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
@ -9,6 +13,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
@ -19,6 +24,10 @@ func (p7 *PKCS7) Verify() (err error) {
|
||||
return p7.VerifyWithChain(nil)
|
||||
}
|
||||
|
||||
func (p7 *PKCS7) SetIsDigest() {
|
||||
p7.isDigest = true
|
||||
}
|
||||
|
||||
// VerifyWithChain checks the signatures of a PKCS7 object.
|
||||
//
|
||||
// If truststore is not nil, it also verifies the chain of trust of
|
||||
@ -111,7 +120,39 @@ func verifySignature(p7 *PKCS7, signer signerInfo, truststore *smx509.CertPool,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
|
||||
|
||||
if len(signer.AuthenticatedAttributes) == 0 && p7.isDigest {
|
||||
return checkSignature(sigalg, signedData, signer.EncryptedDigest, ee.PublicKey)
|
||||
} else {
|
||||
return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
|
||||
}
|
||||
}
|
||||
|
||||
func checkSignature(algo smx509.SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey) (err error) {
|
||||
isSM2 := (algo == smx509.SM2WithSM3)
|
||||
switch pub := publicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if algo == smx509.SHA256WithRSAPSS || algo == smx509.SHA384WithRSAPSS || algo == smx509.SHA512WithRSAPSS {
|
||||
return rsa.VerifyPSS(pub, 0, signed, signature, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
|
||||
} else {
|
||||
return rsa.VerifyPKCS1v15(pub, 0, signed, signature)
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
if isSM2 {
|
||||
if !sm2.VerifyASN1(pub, signed, signature) {
|
||||
return errors.New("x509: SM2 verification failure")
|
||||
}
|
||||
} else if !ecdsa.VerifyASN1(pub, signed, signature) {
|
||||
return errors.New("x509: ECDSA verification failure")
|
||||
}
|
||||
return
|
||||
case ed25519.PublicKey:
|
||||
if !ed25519.Verify(pub, signed, signature) {
|
||||
return errors.New("x509: Ed25519 verification failure")
|
||||
}
|
||||
return
|
||||
}
|
||||
return x509.ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
// GetOnlySigner returns an x509.Certificate for the first signer of the signed
|
||||
|
Loading…
x
Reference in New Issue
Block a user