mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-21 17:56:19 +08:00
pkcs7: sign precomputed digest #294
This commit is contained in:
parent
5a0ff81dc3
commit
bef7f5421e
@ -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
|
package cfca
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -56,3 +56,28 @@ func VerifyMessageDetach(p7Der, sourceData []byte) error {
|
|||||||
p7.Content = sourceData
|
p7.Content = sourceData
|
||||||
return p7.Verify()
|
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()
|
||||||
|
}
|
||||||
|
@ -5,8 +5,11 @@
|
|||||||
package cfca
|
package cfca
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/emmansun/gmsm/sm2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSignMessageAttach(t *testing.T) {
|
func TestSignMessageAttach(t *testing.T) {
|
||||||
@ -89,3 +92,31 @@ func TestSignMessageDetach(t *testing.T) {
|
|||||||
|
|
||||||
var sadkSignedData = "MIICgAYKKoEcz1UGAQQCAqCCAnAwggJsAgEBMQ4wDAYIKoEcz1UBgxEFADAjBgoqgRzPVQYBBAIBoBUEE0hlbGxvIFNlY3JldCBXb3JsZCGgggGNMIIBiTCCAS+gAwIBAgIFAKncGpAwCgYIKoEcz1UBg3UwKTEQMA4GA1UEChMHQWNtZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTI0MTExOTAwMTIyNVoXDTI1MTExOTAwMTIyNlowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNub3cwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAATYcgrHXJmFO1/t/9WQ6GkCW6D0yDyd2ya5wRXjVAU08I9Oo6k99jB2MPauCn64W81APRCPHLlwWOtuIsmSmQhjo0gwRjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwQwHwYDVR0jBBgwFoAUyBfYaeGJxaf9ST9aCRgotC+MwvwwCgYIKoEcz1UBg3UDSAAwRQIgRaF0PA74cCYKeu8pZ4VDQti+rE283Hq/tGXzXUzOWKUCIQDl3z1boZxtRscbnOGOXg1NY+yoY2lz5b63kGOTkn/SxzGBoDCBnQIBATAyMCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyawIFAKncGpAwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARHMEUCIQCl145xtYc7QWTymATxUGbLfF1mlPlyMoIKSp9alu14UQIgQSV/Ll3yYCyXSNxhPelz8Nsbxopky+Pt56Al54rv3p0="
|
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"
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ SADK(Security Application Development Kit)是CFCA推出的一套支持全平
|
|||||||
其JAVA版本(其它语言版本未知)基本上是一个基于[Bouncy Castle](https://www.bouncycastle.org/)的实现(当然它看起来也支持JNI接入OpenSSL、U盾?)。
|
其JAVA版本(其它语言版本未知)基本上是一个基于[Bouncy Castle](https://www.bouncycastle.org/)的实现(当然它看起来也支持JNI接入OpenSSL、U盾?)。
|
||||||
|
|
||||||
## 为什么会有互操作性问题
|
## 为什么会有互操作性问题
|
||||||
|
* CFCA有一些实现没有相关标准。
|
||||||
* SADK存在较早,可能有些实现早于标准发布。
|
* SADK存在较早,可能有些实现早于标准发布。
|
||||||
* SADK版本较多,不同版本也会有互操作性兼容问题。
|
* SADK版本较多,不同版本也会有互操作性兼容问题。
|
||||||
* 其它未知原因。
|
* 其它未知原因。
|
||||||
|
@ -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
|
||||||
@ -47,6 +48,22 @@ func NewSignedData(data []byte) (*SignedData, error) {
|
|||||||
return &SignedData{sd: sd, data: data, digestOid: OIDDigestAlgorithmSHA1, contentTypeOid: OIDSignedData}, nil
|
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
|
// 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
|
// ready to be signed via AddSigner. The digest algorithm is set to SM3 by default
|
||||||
// and can be changed by calling SetDigestAlgorithm.
|
// and can be changed by calling SetDigestAlgorithm.
|
||||||
@ -61,6 +78,20 @@ func NewSMSignedData(data []byte) (*SignedData, error) {
|
|||||||
return sd, nil
|
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
|
// SignerInfoConfig are optional values to include when adding a signer
|
||||||
type SignerInfoConfig struct {
|
type SignerInfoConfig struct {
|
||||||
ExtraSignedAttributes []Attribute
|
ExtraSignedAttributes []Attribute
|
||||||
@ -203,9 +234,12 @@ func (sd *SignedData) signWithAttributes(pkey crypto.PrivateKey, config SignerIn
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
messageDigest := sd.data
|
||||||
|
if !sd.isDigest {
|
||||||
h := newHash(hasher, sd.digestOid)
|
h := newHash(hasher, sd.digestOid)
|
||||||
h.Write(sd.data)
|
h.Write(sd.data)
|
||||||
messageDigest := h.Sum(nil)
|
messageDigest = h.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
attrs := &attributes{}
|
attrs := &attributes{}
|
||||||
attrs.Add(OIDAttributeContentType, sd.sd.ContentInfo.ContentType)
|
attrs.Add(OIDAttributeContentType, sd.sd.ContentInfo.ContentType)
|
||||||
@ -250,9 +284,10 @@ 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
|
||||||
ias.SerialNumber = ee.SerialNumber
|
ias.SerialNumber = ee.SerialNumber
|
||||||
// no parent, the issue is the end-entity cert itself
|
// no parent, the issue is the end-entity cert itself
|
||||||
@ -377,12 +412,12 @@ 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")
|
||||||
@ -392,7 +427,11 @@ func signData(data []byte, pkey crypto.PrivateKey, hasher crypto.Hash) ([]byte,
|
|||||||
|
|
||||||
if !hasher.Available() {
|
if !hasher.Available() {
|
||||||
if sm2.IsSM2PublicKey(key.Public()) {
|
if sm2.IsSM2PublicKey(key.Public()) {
|
||||||
|
if !isDigest {
|
||||||
opts = sm2.DefaultSM2SignerOpts
|
opts = sm2.DefaultSM2SignerOpts
|
||||||
|
} else if len(hash) != sm3.Size {
|
||||||
|
return nil, fmt.Errorf("pkcs7: invalid hash value fo SM2 signature")
|
||||||
|
}
|
||||||
switch realKey := key.(type) {
|
switch realKey := key.(type) {
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
{
|
{
|
||||||
@ -404,10 +443,12 @@ func signData(data []byte, pkey crypto.PrivateKey, hasher crypto.Hash) ([]byte,
|
|||||||
} else {
|
} else {
|
||||||
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)
|
||||||
|
} else if len(hash) != hasher.Size() {
|
||||||
|
return nil, fmt.Errorf("pkcs7: invalid hash for %s", hasher)
|
||||||
}
|
}
|
||||||
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,16 +2,21 @@ package pkcs7
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/emmansun/gmsm/sm2"
|
||||||
|
"github.com/emmansun/gmsm/sm3"
|
||||||
"github.com/emmansun/gmsm/smx509"
|
"github.com/emmansun/gmsm/smx509"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -349,3 +354,211 @@ func TestSignWithoutAttr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testSignDigest(t *testing.T, isSM bool, content []byte, sigalgs []x509.SignatureAlgorithm) {
|
||||||
|
for _, sigalgroot := range sigalgs {
|
||||||
|
rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err)
|
||||||
|
}
|
||||||
|
truststore := smx509.NewCertPool()
|
||||||
|
truststore.AddCert(rootCert.Certificate)
|
||||||
|
for _, sigalginter := range sigalgs {
|
||||||
|
interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err)
|
||||||
|
}
|
||||||
|
var parents []*smx509.Certificate
|
||||||
|
parents = append(parents, interCert.Certificate)
|
||||||
|
for _, sigalgsigner := range sigalgs {
|
||||||
|
signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||||
|
}
|
||||||
|
hashOID, err := getDigestOIDForSignatureAlgorithm(sigalgsigner)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %s/%s/%s: cannot get digest OID: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||||
|
}
|
||||||
|
hasher, err := getHashForOID(hashOID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %s/%s/%s: cannot get hasher: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||||
|
}
|
||||||
|
var hashInst hash.Hash
|
||||||
|
if hasher == crypto.Hash(0) {
|
||||||
|
hashInst = sm3.New()
|
||||||
|
} else {
|
||||||
|
hashInst = hasher.New()
|
||||||
|
}
|
||||||
|
hashInst.Write(content)
|
||||||
|
digest := hashInst.Sum(nil)
|
||||||
|
|
||||||
|
var toBeSigned *SignedData
|
||||||
|
if isSM {
|
||||||
|
toBeSigned, err = NewSMSignedDataWithDegist(digest)
|
||||||
|
} else {
|
||||||
|
toBeSigned, err = NewSignedDataWithDegist(digest)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the digest to match the end entity cert
|
||||||
|
signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalgsigner)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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 len(p7.Content) > 0 {
|
||||||
|
t.Errorf("Content should be empty")
|
||||||
|
}
|
||||||
|
if err := p7.VerifyAsDigestWithChain(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignWithDigest(t *testing.T) {
|
||||||
|
content := []byte("Hello World")
|
||||||
|
sigalgs := []x509.SignatureAlgorithm{
|
||||||
|
x509.SHA1WithRSA,
|
||||||
|
x509.SHA256WithRSA,
|
||||||
|
x509.SHA512WithRSA,
|
||||||
|
x509.ECDSAWithSHA1,
|
||||||
|
x509.ECDSAWithSHA256,
|
||||||
|
x509.ECDSAWithSHA384,
|
||||||
|
x509.ECDSAWithSHA512,
|
||||||
|
smx509.SM2WithSM3,
|
||||||
|
}
|
||||||
|
testSignDigest(t, false, content, sigalgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignSMWithDigest(t *testing.T) {
|
||||||
|
content := []byte("Hello World")
|
||||||
|
sigalgs := []x509.SignatureAlgorithm{
|
||||||
|
smx509.SM2WithSM3,
|
||||||
|
}
|
||||||
|
testSignDigest(t, true, content, sigalgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignWithoutAttrWithDigest(t *testing.T) {
|
||||||
|
content := []byte("Hello World")
|
||||||
|
sigalgs := []struct {
|
||||||
|
isSM bool
|
||||||
|
sigAlg x509.SignatureAlgorithm
|
||||||
|
skipCert bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
x509.SHA256WithRSA,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
smx509.SM2WithSM3,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
x509.SHA256WithRSA,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
smx509.SM2WithSM3,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, sigalg := range sigalgs {
|
||||||
|
cert, err := createTestCertificate(sigalg.sigAlg, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
hashOID, err := getDigestOIDForSignatureAlgorithm(sigalg.sigAlg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %s: cannot get digest OID: %s", sigalg.sigAlg, err)
|
||||||
|
}
|
||||||
|
hasher, err := getHashForOID(hashOID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %s: cannot get hasher: %s", sigalg.sigAlg, err)
|
||||||
|
}
|
||||||
|
var digest []byte
|
||||||
|
if hasher == crypto.Hash(0) {
|
||||||
|
publicKey, ok := cert.Certificate.PublicKey.(*ecdsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Cannot cast public key to ECDSA public key")
|
||||||
|
}
|
||||||
|
digest, err = sm2.CalculateSM2Hash(publicKey, content, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot calculate SM2 hash: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hashInst := hasher.New()
|
||||||
|
hashInst.Write(content)
|
||||||
|
digest = hashInst.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var toBeSigned *SignedData
|
||||||
|
if sigalg.isSM {
|
||||||
|
toBeSigned, err = NewSMSignedDataWithDegist(digest)
|
||||||
|
} else {
|
||||||
|
toBeSigned, err = NewSignedDataWithDegist(digest)
|
||||||
|
toBeSigned.SetDigestAlgorithm(hashOID)
|
||||||
|
}
|
||||||
|
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 len(p7.Content) > 0 {
|
||||||
|
t.Errorf("Content should be empty")
|
||||||
|
}
|
||||||
|
p7.Content = digest
|
||||||
|
if !sigalg.skipCert {
|
||||||
|
if len(p7.Certificates) == 0 {
|
||||||
|
t.Errorf("No certificates")
|
||||||
|
}
|
||||||
|
err = p7.VerifyAsDigest()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(p7.Certificates) > 0 {
|
||||||
|
t.Errorf("No certificates expected")
|
||||||
|
}
|
||||||
|
err = p7.VerifyAsDigest()
|
||||||
|
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.VerifyAsDigest()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -19,6 +19,12 @@ func (p7 *PKCS7) Verify() (err error) {
|
|||||||
return p7.VerifyWithChain(nil)
|
return p7.VerifyWithChain(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifyAsDigest verifies the PKCS7 signature, treats the content as a digest.
|
||||||
|
// It returns an error if the verification fails.
|
||||||
|
func (p7 *PKCS7) VerifyAsDigest() (err error) {
|
||||||
|
return p7.verifyWithChain(nil, 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
|
||||||
@ -27,11 +33,22 @@ func (p7 *PKCS7) Verify() (err error) {
|
|||||||
// authenticated attr verifies the chain at that time and UTC now
|
// authenticated attr verifies the chain at that time and UTC now
|
||||||
// otherwise.
|
// otherwise.
|
||||||
func (p7 *PKCS7) VerifyWithChain(truststore *smx509.CertPool) (err error) {
|
func (p7 *PKCS7) VerifyWithChain(truststore *smx509.CertPool) (err error) {
|
||||||
|
return p7.verifyWithChain(truststore, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// VerifyAsDigestWithChain verifies the PKCS7 signature using the provided truststore
|
||||||
|
// and treats the content as a precomputed digest. It returns an error if the verification fails.
|
||||||
|
func (p7 *PKCS7) VerifyAsDigestWithChain(truststore *smx509.CertPool) (err error) {
|
||||||
|
return p7.verifyWithChain(truststore, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p7 *PKCS7) verifyWithChain(truststore *smx509.CertPool, isDigest bool) (err error) {
|
||||||
if len(p7.Signers) == 0 {
|
if len(p7.Signers) == 0 {
|
||||||
return errors.New("pkcs7: Message has no signers")
|
return errors.New("pkcs7: Message has no signers")
|
||||||
}
|
}
|
||||||
for _, signer := range p7.Signers {
|
for _, signer := range p7.Signers {
|
||||||
if err := verifySignature(p7, signer, truststore, nil); err != nil {
|
if err := verifySignature(p7, signer, truststore, nil, isDigest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,24 +62,41 @@ func (p7 *PKCS7) VerifyWithChain(truststore *smx509.CertPool) (err error) {
|
|||||||
// currentTime. It does not use the signing time authenticated
|
// currentTime. It does not use the signing time authenticated
|
||||||
// attribute.
|
// attribute.
|
||||||
func (p7 *PKCS7) VerifyWithChainAtTime(truststore *smx509.CertPool, currentTime *time.Time) (err error) {
|
func (p7 *PKCS7) VerifyWithChainAtTime(truststore *smx509.CertPool, currentTime *time.Time) (err error) {
|
||||||
|
return p7.verifyWithChainAtTime(truststore, currentTime, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p7 *PKCS7) verifyWithChainAtTime(truststore *smx509.CertPool, currentTime *time.Time, isDigest bool) (err error) {
|
||||||
if len(p7.Signers) == 0 {
|
if len(p7.Signers) == 0 {
|
||||||
return errors.New("pkcs7: Message has no signers")
|
return errors.New("pkcs7: Message has no signers")
|
||||||
}
|
}
|
||||||
for _, signer := range p7.Signers {
|
for _, signer := range p7.Signers {
|
||||||
if err := verifySignature(p7, signer, truststore, currentTime); err != nil {
|
if err := verifySignature(p7, signer, truststore, currentTime, isDigest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifySignature(p7 *PKCS7, signer signerInfo, truststore *smx509.CertPool, currentTime *time.Time) (err error) {
|
func verifySignature(p7 *PKCS7, signer signerInfo, truststore *smx509.CertPool, currentTime *time.Time, isDigest bool) (err error) {
|
||||||
signedData := p7.Content
|
signedData := p7.Content
|
||||||
ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
|
ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
|
||||||
if ee == nil {
|
if ee == nil {
|
||||||
return errors.New("pkcs7: No certificate for signer")
|
return errors.New("pkcs7: No certificate for signer")
|
||||||
}
|
}
|
||||||
signingTime := time.Now().UTC()
|
signingTime := time.Now().UTC()
|
||||||
|
if truststore != nil {
|
||||||
|
if currentTime != nil {
|
||||||
|
signingTime = *currentTime
|
||||||
|
}
|
||||||
|
_, err = verifyCertChain(ee, p7.Certificates, truststore, signingTime)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if len(signer.AuthenticatedAttributes) > 0 {
|
if len(signer.AuthenticatedAttributes) > 0 {
|
||||||
// TODO(fullsailor): First check the content type match
|
// TODO(fullsailor): First check the content type match
|
||||||
var digest []byte
|
var digest []byte
|
||||||
@ -74,9 +108,12 @@ func verifySignature(p7 *PKCS7, signer signerInfo, truststore *smx509.CertPool,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
computed := signedData
|
||||||
|
if !isDigest {
|
||||||
h := newHash(hasher, signer.DigestAlgorithm.Algorithm)
|
h := newHash(hasher, signer.DigestAlgorithm.Algorithm)
|
||||||
h.Write(p7.Content)
|
h.Write(p7.Content)
|
||||||
computed := h.Sum(nil)
|
computed = h.Sum(nil)
|
||||||
|
}
|
||||||
if subtle.ConstantTimeCompare(digest, computed) != 1 {
|
if subtle.ConstantTimeCompare(digest, computed) != 1 {
|
||||||
return &MessageDigestMismatchError{
|
return &MessageDigestMismatchError{
|
||||||
ExpectedDigest: digest,
|
ExpectedDigest: digest,
|
||||||
@ -97,19 +134,10 @@ func verifySignature(p7 *PKCS7, signer signerInfo, truststore *smx509.CertPool,
|
|||||||
ee.NotAfter.Format(time.RFC3339))
|
ee.NotAfter.Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
|
||||||
}
|
}
|
||||||
if truststore != nil {
|
if isDigest {
|
||||||
if currentTime != nil {
|
return ee.CheckSignatureWithDigest(sigalg, signedData, signer.EncryptedDigest)
|
||||||
signingTime = *currentTime
|
|
||||||
}
|
|
||||||
_, err = verifyCertChain(ee, p7.Certificates, truststore, signingTime)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
|
return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
|
||||||
}
|
}
|
||||||
|
@ -732,6 +732,70 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
|
|||||||
return checkSignature(algo, signed, signature, c.PublicKey, true)
|
return checkSignature(algo, signed, signature, c.PublicKey, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckSignatureWithDigest verifies the signature of a certificate using the specified
|
||||||
|
// signature algorithm and digest. It supports RSA, ECDSA, and SM2 public keys.
|
||||||
|
//
|
||||||
|
// This is a low-level API that performs no validity checks on the certificate.
|
||||||
|
func (c *Certificate) CheckSignatureWithDigest(algo SignatureAlgorithm, digest, signature []byte) (err error) {
|
||||||
|
var hashType crypto.Hash
|
||||||
|
var pubKeyAlgo PublicKeyAlgorithm
|
||||||
|
|
||||||
|
publicKey := c.PublicKey
|
||||||
|
|
||||||
|
isSM2 := (algo == SM2WithSM3)
|
||||||
|
for _, details := range signatureAlgorithmDetails {
|
||||||
|
if details.algo == algo {
|
||||||
|
hashType = details.hash
|
||||||
|
pubKeyAlgo = details.pubKeyAlgo
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch hashType {
|
||||||
|
case crypto.Hash(0):
|
||||||
|
if !isSM2 {
|
||||||
|
return x509.ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
if len(digest) != 32 { // SM3 hash size
|
||||||
|
return errors.New("x509: inconsistent digest and signature algorithm")
|
||||||
|
}
|
||||||
|
case crypto.MD5:
|
||||||
|
return x509.InsecureAlgorithmError(algo)
|
||||||
|
default:
|
||||||
|
if !hashType.Available() {
|
||||||
|
return x509.ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
if len(digest) != hashType.Size() {
|
||||||
|
return errors.New("x509: inconsistent digest and signature algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pub := publicKey.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
if pubKeyAlgo != RSA {
|
||||||
|
return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
|
||||||
|
}
|
||||||
|
if isRSAPSS(algo) {
|
||||||
|
return rsa.VerifyPSS(pub, hashType, digest, signature, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
|
||||||
|
} else {
|
||||||
|
return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
|
||||||
|
}
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
if pubKeyAlgo != ECDSA {
|
||||||
|
return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
|
||||||
|
}
|
||||||
|
if isSM2 {
|
||||||
|
if !sm2.VerifyASN1(pub, digest, signature) {
|
||||||
|
return errors.New("x509: SM2 verification failure")
|
||||||
|
}
|
||||||
|
} else if !ecdsa.VerifyASN1(pub, digest, signature) {
|
||||||
|
return errors.New("x509: ECDSA verification failure")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return x509.ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Certificate) hasNameConstraints() bool {
|
func (c *Certificate) hasNameConstraints() bool {
|
||||||
return oidInExtensions(oidExtensionNameConstraints, c.Extensions)
|
return oidInExtensions(oidExtensionNameConstraints, c.Extensions)
|
||||||
}
|
}
|
||||||
@ -1426,7 +1490,6 @@ var emptyASN1Subject = []byte{0x30, 0}
|
|||||||
//
|
//
|
||||||
// If template.SerialNumber is nil, a serial number will be generated which
|
// If template.SerialNumber is nil, a serial number will be generated which
|
||||||
// conforms to RFC 5280, Section 4.1.2.2 using entropy from rand.
|
// conforms to RFC 5280, Section 4.1.2.2 using entropy from rand.
|
||||||
//
|
|
||||||
func CreateCertificate(rand io.Reader, template, parent, pub, priv any) ([]byte, error) {
|
func CreateCertificate(rand io.Reader, template, parent, pub, priv any) ([]byte, error) {
|
||||||
realTemplate, err := toCertificate(template)
|
realTemplate, err := toCertificate(template)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
package smx509
|
package smx509
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/md5"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -292,3 +301,225 @@ func TestInvalidParentTemplate(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error message: %v", err.Error())
|
t.Fatalf("unexpected error message: %v", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckSignatureWithDigest(t *testing.T) {
|
||||||
|
rawMessage := []byte("test message")
|
||||||
|
rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate RSA key: %s", err)
|
||||||
|
}
|
||||||
|
ecdsaPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate ECDSA key: %s", err)
|
||||||
|
}
|
||||||
|
ecdsaPrivateKey2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate ECDSA key: %s", err)
|
||||||
|
}
|
||||||
|
sm2PrivateKey, err := sm2.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate SM2 key: %s", err)
|
||||||
|
}
|
||||||
|
sm2PrivateKey2, err := sm2.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate SM2 key: %s", err)
|
||||||
|
}
|
||||||
|
ed25519Pub, ed25519Priv, err := ed25519.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate Ed25519 key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
cert *Certificate
|
||||||
|
algo SignatureAlgorithm
|
||||||
|
digest []byte
|
||||||
|
signature []byte
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid RSA PKCS1v15 signature",
|
||||||
|
cert: &Certificate{
|
||||||
|
PublicKey: &rsaPrivateKey.PublicKey,
|
||||||
|
},
|
||||||
|
algo: SHA256WithRSA,
|
||||||
|
digest: func() []byte {
|
||||||
|
hash := sha256.Sum256(rawMessage)
|
||||||
|
return hash[:]
|
||||||
|
}(),
|
||||||
|
signature: func() []byte {
|
||||||
|
hash := sha256.Sum256(rawMessage)
|
||||||
|
return mustSignPKCS1v15(t, rsaPrivateKey, crypto.SHA256, hash[:])
|
||||||
|
}(),
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid ECDSA signature",
|
||||||
|
cert: &Certificate{
|
||||||
|
PublicKey: &ecdsaPrivateKey.PublicKey,
|
||||||
|
},
|
||||||
|
algo: ECDSAWithSHA256,
|
||||||
|
digest: func() []byte {
|
||||||
|
hash := sha256.Sum256(rawMessage)
|
||||||
|
return hash[:]
|
||||||
|
}(),
|
||||||
|
signature: func() []byte {
|
||||||
|
hash := sha256.Sum256(rawMessage)
|
||||||
|
return mustSignECDSA(t, ecdsaPrivateKey, hash[:])
|
||||||
|
}(),
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid ECDSA signature",
|
||||||
|
cert: &Certificate{
|
||||||
|
PublicKey: &ecdsaPrivateKey.PublicKey,
|
||||||
|
},
|
||||||
|
algo: ECDSAWithSHA256,
|
||||||
|
digest: func() []byte {
|
||||||
|
hash := sha256.Sum256(rawMessage)
|
||||||
|
return hash[:]
|
||||||
|
}(),
|
||||||
|
signature: func() []byte {
|
||||||
|
hash := sha256.Sum256(rawMessage)
|
||||||
|
return mustSignECDSA(t, ecdsaPrivateKey2, hash[:])
|
||||||
|
}(),
|
||||||
|
expectedError: errors.New("x509: ECDSA verification failure"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid SM2 signature",
|
||||||
|
cert: &Certificate{
|
||||||
|
PublicKey: &sm2PrivateKey.PublicKey,
|
||||||
|
},
|
||||||
|
algo: SM2WithSM3,
|
||||||
|
digest: func() []byte {
|
||||||
|
hash, _ := sm2.CalculateSM2Hash(&sm2PrivateKey.PublicKey, rawMessage, nil)
|
||||||
|
return hash[:]
|
||||||
|
}(),
|
||||||
|
signature: func() []byte {
|
||||||
|
hash, _ := sm2.CalculateSM2Hash(&sm2PrivateKey.PublicKey, rawMessage, nil)
|
||||||
|
return mustSignSM2(t, sm2PrivateKey, hash[:])
|
||||||
|
}(),
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid SM2 signature",
|
||||||
|
cert: &Certificate{
|
||||||
|
PublicKey: &sm2PrivateKey.PublicKey,
|
||||||
|
},
|
||||||
|
algo: SM2WithSM3,
|
||||||
|
digest: func() []byte {
|
||||||
|
hash, _ := sm2.CalculateSM2Hash(&sm2PrivateKey.PublicKey, rawMessage, nil)
|
||||||
|
return hash[:]
|
||||||
|
}(),
|
||||||
|
signature: func() []byte {
|
||||||
|
hash, _ := sm2.CalculateSM2Hash(&sm2PrivateKey2.PublicKey, rawMessage, nil)
|
||||||
|
return mustSignSM2(t, sm2PrivateKey2, hash[:])
|
||||||
|
}(),
|
||||||
|
expectedError: errors.New("x509: SM2 verification failure"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Insecure algorithm",
|
||||||
|
cert: &Certificate{
|
||||||
|
PublicKey: &rsaPrivateKey.PublicKey,
|
||||||
|
},
|
||||||
|
algo: MD5WithRSA,
|
||||||
|
digest: func() []byte {
|
||||||
|
hash := md5.Sum(rawMessage)
|
||||||
|
return hash[:]
|
||||||
|
}(),
|
||||||
|
signature: func() []byte {
|
||||||
|
hash := md5.Sum(rawMessage)
|
||||||
|
return mustSignPKCS1v15(t, rsaPrivateKey, crypto.MD5, hash[:])
|
||||||
|
}(),
|
||||||
|
expectedError: x509.InsecureAlgorithmError(MD5WithRSA),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Unsupported algorithm",
|
||||||
|
cert: &Certificate{
|
||||||
|
PublicKey: ed25519Pub,
|
||||||
|
},
|
||||||
|
algo: PureEd25519,
|
||||||
|
digest: func() []byte {
|
||||||
|
hash := sha256.Sum256(rawMessage)
|
||||||
|
return hash[:]
|
||||||
|
}(),
|
||||||
|
signature: func() []byte {
|
||||||
|
hash := sha256.Sum256(rawMessage)
|
||||||
|
return ed25519.Sign(ed25519Priv, hash[:])
|
||||||
|
}(),
|
||||||
|
expectedError: x509.ErrUnsupportedAlgorithm,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Inconsistent digest and signature algorithm",
|
||||||
|
cert: &Certificate{
|
||||||
|
PublicKey: &rsaPrivateKey.PublicKey,
|
||||||
|
},
|
||||||
|
algo: SHA256WithRSA,
|
||||||
|
digest: func() []byte {
|
||||||
|
hash := sha1.Sum(rawMessage)
|
||||||
|
return hash[:]
|
||||||
|
}(),
|
||||||
|
signature: func() []byte {
|
||||||
|
hash := sha256.Sum256(rawMessage)
|
||||||
|
return mustSignPKCS1v15(t, rsaPrivateKey, crypto.SHA256, hash[:])
|
||||||
|
}(),
|
||||||
|
expectedError: errors.New("x509: inconsistent digest and signature algorithm"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Inconsistent digest and signature algorithm (SM2)",
|
||||||
|
cert: &Certificate{
|
||||||
|
PublicKey: &sm2PrivateKey.PublicKey,
|
||||||
|
},
|
||||||
|
algo: SM2WithSM3,
|
||||||
|
digest: func() []byte {
|
||||||
|
hash, _ := sm2.CalculateSM2Hash(&sm2PrivateKey.PublicKey, rawMessage, nil)
|
||||||
|
return hash[:20]
|
||||||
|
}(),
|
||||||
|
signature: func() []byte {
|
||||||
|
hash, _ := sm2.CalculateSM2Hash(&sm2PrivateKey.PublicKey, rawMessage, nil)
|
||||||
|
return mustSignSM2(t, sm2PrivateKey, hash[:])
|
||||||
|
}(),
|
||||||
|
expectedError: errors.New("x509: inconsistent digest and signature algorithm"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := tt.cert.CheckSignatureWithDigest(tt.algo, tt.digest, tt.signature)
|
||||||
|
if (err == nil || tt.expectedError == nil) && err != tt.expectedError {
|
||||||
|
t.Errorf("Case <%v>: expected error %v, got %v", tt.name, tt.expectedError, err)
|
||||||
|
}
|
||||||
|
if err != nil && tt.expectedError != nil && err.Error() != tt.expectedError.Error() {
|
||||||
|
t.Errorf("Case <%v>: expected error %v, got %v", tt.name, tt.expectedError, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustSignPKCS1v15(t *testing.T, priv *rsa.PrivateKey, hash crypto.Hash, digest []byte) []byte {
|
||||||
|
signature, err := rsa.SignPKCS1v15(rand.Reader, priv, hash, digest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to sign: %s", err)
|
||||||
|
}
|
||||||
|
return signature
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustSignECDSA(t *testing.T, priv *ecdsa.PrivateKey, digest []byte) []byte {
|
||||||
|
r, s, err := ecdsa.Sign(rand.Reader, priv, digest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to sign: %s", err)
|
||||||
|
}
|
||||||
|
signature, err := asn1.Marshal(struct{ R, S *big.Int }{r, s})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to marshal signature: %s", err)
|
||||||
|
}
|
||||||
|
return signature
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustSignSM2(t *testing.T, priv *sm2.PrivateKey, digest []byte) []byte {
|
||||||
|
signature, err := sm2.SignASN1(rand.Reader, priv, digest, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to sign: %s", err)
|
||||||
|
}
|
||||||
|
return signature
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user