mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-27 20:56:18 +08:00
pkcs7: 签名支持外部hash
This commit is contained in:
parent
5a0ff81dc3
commit
cbdb454b14
@ -26,6 +26,7 @@ type PKCS7 struct {
|
|||||||
Certificates []*smx509.Certificate
|
Certificates []*smx509.Certificate
|
||||||
CRLs []pkix.CertificateList
|
CRLs []pkix.CertificateList
|
||||||
Signers []signerInfo
|
Signers []signerInfo
|
||||||
|
isDigest bool
|
||||||
raw any
|
raw any
|
||||||
session Session
|
session Session
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ type SignedData struct {
|
|||||||
sd signedData
|
sd signedData
|
||||||
certs []*smx509.Certificate
|
certs []*smx509.Certificate
|
||||||
data []byte
|
data []byte
|
||||||
|
isDigest bool
|
||||||
contentTypeOid asn1.ObjectIdentifier
|
contentTypeOid asn1.ObjectIdentifier
|
||||||
digestOid asn1.ObjectIdentifier
|
digestOid asn1.ObjectIdentifier
|
||||||
encryptionOid asn1.ObjectIdentifier
|
encryptionOid asn1.ObjectIdentifier
|
||||||
@ -129,6 +130,10 @@ func (sd *SignedData) SetEncryptionAlgorithm(d asn1.ObjectIdentifier) {
|
|||||||
sd.encryptionOid = d
|
sd.encryptionOid = d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sd *SignedData) SetIsDigest() {
|
||||||
|
sd.isDigest = true
|
||||||
|
}
|
||||||
|
|
||||||
// AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent.
|
// 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 {
|
func (sd *SignedData) AddSigner(ee *smx509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
|
||||||
var parents []*smx509.Certificate
|
var parents []*smx509.Certificate
|
||||||
@ -250,7 +255,7 @@ func (sd *SignedData) SignWithoutAttr(ee *smx509.Certificate, pkey crypto.Privat
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
return err
|
||||||
}
|
}
|
||||||
var ias issuerAndSerial
|
var ias issuerAndSerial
|
||||||
@ -377,22 +382,29 @@ func signAttributes(attrs []attribute, pkey crypto.PrivateKey, hasher crypto.Has
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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.
|
// 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.
|
// 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)
|
key, ok := pkey.(crypto.Signer)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("pkcs7: private key does not implement crypto.Signer")
|
return nil, errors.New("pkcs7: private key does not implement crypto.Signer")
|
||||||
}
|
}
|
||||||
hash := data
|
hash := data
|
||||||
var opts crypto.SignerOpts = hasher
|
var opts crypto.SignerOpts = hasher
|
||||||
|
if isDigest {
|
||||||
|
opts = crypto.Hash(0)
|
||||||
|
}
|
||||||
|
|
||||||
if !hasher.Available() {
|
if !hasher.Available() {
|
||||||
if sm2.IsSM2PublicKey(key.Public()) {
|
if sm2.IsSM2PublicKey(key.Public()) {
|
||||||
|
if isDigest {
|
||||||
|
opts = sm2.NewSM2SignerOption(false, nil)
|
||||||
|
} else {
|
||||||
opts = sm2.DefaultSM2SignerOpts
|
opts = sm2.DefaultSM2SignerOpts
|
||||||
|
}
|
||||||
switch realKey := key.(type) {
|
switch realKey := key.(type) {
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
{
|
{
|
||||||
@ -405,10 +417,12 @@ func signData(data []byte, pkey crypto.PrivateKey, hasher crypto.Hash) ([]byte,
|
|||||||
return nil, fmt.Errorf("pkcs7: unsupported hash function %s", hasher)
|
return nil, fmt.Errorf("pkcs7: unsupported hash function %s", hasher)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if !isDigest {
|
||||||
h := hasher.New()
|
h := hasher.New()
|
||||||
h.Write(data)
|
h.Write(data)
|
||||||
hash = h.Sum(nil)
|
hash = h.Sum(nil)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return key.Sign(rand.Reader, hash, opts)
|
return key.Sign(rand.Reader, hash, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ func (saed *SignedAndEnvelopedData) AddSignerChain(ee *smx509.Certificate, pkey
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
signature, err := signData(saed.data, pkey, hasher)
|
signature, err := signData(saed.data, pkey, hasher, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package pkcs7
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/emmansun/gmsm/sm2"
|
||||||
"github.com/emmansun/gmsm/smx509"
|
"github.com/emmansun/gmsm/smx509"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,19 +38,39 @@ 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)
|
t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||||
}
|
}
|
||||||
for _, testDetach := range []bool{false, true} {
|
for _, testDetach := range []bool{false, true} {
|
||||||
log.Printf("test %s/%s/%s detached %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach)
|
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
|
var toBeSigned *SignedData
|
||||||
if isSM {
|
if isSM {
|
||||||
toBeSigned, err = NewSMSignedData(content)
|
toBeSigned, err = NewSMSignedData(data)
|
||||||
} else {
|
} else {
|
||||||
toBeSigned, err = NewSignedData(content)
|
toBeSigned, err = NewSignedData(data)
|
||||||
}
|
}
|
||||||
|
toBeSigned.isDigest = testDigest
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the digest to match the end entity cert
|
// Set the digest to match the end entity cert
|
||||||
signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalgsigner)
|
|
||||||
toBeSigned.SetDigestAlgorithm(signerDigest)
|
toBeSigned.SetDigestAlgorithm(signerDigest)
|
||||||
|
|
||||||
if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil {
|
if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil {
|
||||||
@ -70,11 +92,14 @@ func testSign(t *testing.T, isSM bool, content []byte, sigalgs []x509.SignatureA
|
|||||||
// Detached signature should not contain the content
|
// Detached signature should not contain the content
|
||||||
// So we should not be able to find the content in the parsed data
|
// 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
|
// We should suppliment the content to the parsed data before verifying
|
||||||
p7.Content = content
|
p7.Content = data
|
||||||
}
|
}
|
||||||
if !bytes.Equal(content, p7.Content) {
|
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)
|
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 {
|
if err := p7.VerifyWithChain(truststore); err != nil {
|
||||||
t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||||
}
|
}
|
||||||
@ -87,6 +112,7 @@ func testSign(t *testing.T, isSM bool, content []byte, sigalgs []x509.SignatureA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSign(t *testing.T) {
|
func TestSign(t *testing.T) {
|
||||||
content := []byte("Hello World")
|
content := []byte("Hello World")
|
||||||
@ -299,21 +325,44 @@ func TestSignWithoutAttr(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, sigalg := range sigalgs {
|
for _, sigalg := range sigalgs {
|
||||||
|
for _, testDigest := range []bool{false, true} {
|
||||||
cert, err := createTestCertificate(sigalg.sigAlg, false)
|
cert, err := createTestCertificate(sigalg.sigAlg, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalg.sigAlg)
|
||||||
|
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
|
var toBeSigned *SignedData
|
||||||
if sigalg.isSM {
|
if sigalg.isSM {
|
||||||
toBeSigned, err = NewSMSignedData(content)
|
toBeSigned, err = NewSMSignedData(data)
|
||||||
} else {
|
} else {
|
||||||
toBeSigned, err = NewSignedData(content)
|
toBeSigned, err = NewSignedData(data)
|
||||||
signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalg.sigAlg)
|
|
||||||
toBeSigned.SetDigestAlgorithm(signerDigest)
|
toBeSigned.SetDigestAlgorithm(signerDigest)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Cannot initialize signed data: %s", err)
|
t.Fatalf("Cannot initialize signed data: %s", err)
|
||||||
}
|
}
|
||||||
|
toBeSigned.isDigest = testDigest
|
||||||
|
|
||||||
if err := toBeSigned.SignWithoutAttr(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{SkipCertificates: sigalg.skipCert}); err != nil {
|
if err := toBeSigned.SignWithoutAttr(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{SkipCertificates: sigalg.skipCert}); err != nil {
|
||||||
t.Fatalf("Cannot add signer: %s", err)
|
t.Fatalf("Cannot add signer: %s", err)
|
||||||
}
|
}
|
||||||
@ -325,6 +374,9 @@ func TestSignWithoutAttr(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Cannot parse signed data: %v", err)
|
t.Fatalf("Cannot parse signed data: %v", err)
|
||||||
}
|
}
|
||||||
|
if testDigest {
|
||||||
|
p7.SetIsDigest()
|
||||||
|
}
|
||||||
if !sigalg.skipCert {
|
if !sigalg.skipCert {
|
||||||
if len(p7.Certificates) == 0 {
|
if len(p7.Certificates) == 0 {
|
||||||
t.Errorf("No certificates")
|
t.Errorf("No certificates")
|
||||||
@ -349,3 +401,4 @@ func TestSignWithoutAttr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package pkcs7
|
package pkcs7
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
@ -9,6 +13,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/emmansun/gmsm/sm2"
|
||||||
"github.com/emmansun/gmsm/smx509"
|
"github.com/emmansun/gmsm/smx509"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,6 +24,10 @@ func (p7 *PKCS7) Verify() (err error) {
|
|||||||
return p7.VerifyWithChain(nil)
|
return p7.VerifyWithChain(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p7 *PKCS7) SetIsDigest() {
|
||||||
|
p7.isDigest = true
|
||||||
|
}
|
||||||
|
|
||||||
// VerifyWithChain checks the signatures of a PKCS7 object.
|
// VerifyWithChain checks the signatures of a PKCS7 object.
|
||||||
//
|
//
|
||||||
// If truststore is not nil, it also verifies the chain of trust of
|
// If truststore is not nil, it also verifies the chain of trust of
|
||||||
@ -111,8 +120,40 @@ func verifySignature(p7 *PKCS7, signer signerInfo, truststore *smx509.CertPool,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(signer.AuthenticatedAttributes) == 0 && p7.isDigest {
|
||||||
|
return checkSignature(sigalg, signedData, signer.EncryptedDigest, ee.PublicKey)
|
||||||
|
} else {
|
||||||
return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
|
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
|
// GetOnlySigner returns an x509.Certificate for the first signer of the signed
|
||||||
// data payload. If there are more or less than one signer, nil is returned
|
// data payload. If there are more or less than one signer, nil is returned
|
||||||
|
Loading…
x
Reference in New Issue
Block a user