diff --git a/README.md b/README.md index e73d46e..2cb2e27 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,9 @@ err = sign.Sign(input_file, output_file, rdr, size, sign.SignData{ CertType: sign.CertificationSignature, DocMDPPerm: sign.AllowFillingExistingFormFieldsAndSignaturesPerms, }, - Signer: privateKey, // crypto.Signer - Certificate: certificate, // x509.Certificate + Signer: privateKey, // crypto.Signer + DigestAlgorithm: crypto.SHA256, // hash algorithm for the digest creation + Certificate: certificate, // x509.Certificate CertificateChains: certificate_chains, // x509.Certificate.Verify() TSA: sign.TSA{ URL: "https://freetsa.org/tsr", diff --git a/sign.go b/sign.go index bd8d629..6e12573 100644 --- a/sign.go +++ b/sign.go @@ -1,6 +1,7 @@ package main import ( + "crypto" "flag" "fmt" "log" @@ -142,6 +143,7 @@ func main() { DocMDPPerm: sign.AllowFillingExistingFormFieldsAndSignaturesPerms, }, Signer: pkey, + DigestAlgorithm: crypto.SHA256, Certificate: cert, CertificateChains: certificate_chains, TSA: sign.TSA{ diff --git a/sign/helpers.go b/sign/helpers.go index cd756ca..93861f6 100644 --- a/sign/helpers.go +++ b/sign/helpers.go @@ -1,6 +1,8 @@ package sign import ( + "crypto" + "encoding/asn1" "errors" "fmt" "io" @@ -140,3 +142,28 @@ func writePartFromSourceFileToTargetFile(input_file io.ReadSeeker, output_file i return nil } + +var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{ + crypto.SHA1: asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}), + crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}), + crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}), + crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}), +} + +func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash { + for hash, oid := range hashOIDs { + if oid.Equal(target) { + return hash + } + } + return crypto.Hash(0) +} + +func getOIDFromHashAlgorithm(target crypto.Hash) asn1.ObjectIdentifier { + for hash, oid := range hashOIDs { + if hash == target { + return oid + } + } + return nil +} diff --git a/sign/pdfsignature.go b/sign/pdfsignature.go index 79f1fa0..134b5fd 100644 --- a/sign/pdfsignature.go +++ b/sign/pdfsignature.go @@ -2,6 +2,7 @@ package sign import ( "bytes" + "crypto" "crypto/x509" "encoding/asn1" "encoding/hex" @@ -13,6 +14,8 @@ import ( "github.com/digitorus/pkcs7" "github.com/digitorus/timestamp" + "golang.org/x/crypto/cryptobyte" + cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" ) const signatureByteRangePlaceholder = "/ByteRange[0 ********** ********** **********]" @@ -121,6 +124,39 @@ func (context *SignContext) fetchRevocationData() error { return nil } +func (context *SignContext) createSigningCertificateAttribute() (*pkcs7.Attribute, error) { + hash := context.SignData.DigestAlgorithm.New() + hash.Write(context.SignData.Certificate.Raw) + + var b cryptobyte.Builder + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { // SigningCertificate + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { // []ESSCertID, []ESSCertIDv2 + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { // ESSCertID, ESSCertIDv2 + if context.SignData.DigestAlgorithm.HashFunc() != crypto.SHA1 && + context.SignData.DigestAlgorithm.HashFunc() != crypto.SHA256 { // default SHA-256 + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { // AlgorithmIdentifier + b.AddASN1ObjectIdentifier(getOIDFromHashAlgorithm(context.SignData.DigestAlgorithm)) + }) + } + b.AddASN1OctetString(hash.Sum(nil)) // certHash + }) + }) + }) + + sse, err := b.Bytes() + if err != nil { + return nil, err + } + signingCertificate := pkcs7.Attribute{ + Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 47}, // SigningCertificateV2 + Value: asn1.RawValue{FullBytes: sse}, + } + if context.SignData.DigestAlgorithm.HashFunc() == crypto.SHA1 { + signingCertificate.Type = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 12} // SigningCertificate + } + return &signingCertificate, nil +} + func (context *SignContext) createSignature() ([]byte, error) { if _, err := context.OutputBuffer.Seek(0, 0); err != nil { return nil, err @@ -140,12 +176,19 @@ func (context *SignContext) createSignature() ([]byte, error) { return nil, fmt.Errorf("new signed data: %w", err) } + signed_data.SetDigestAlgorithm(getOIDFromHashAlgorithm(context.SignData.DigestAlgorithm)) + signingCertificate, err := context.createSigningCertificateAttribute() + if err != nil { + return nil, fmt.Errorf("new signed data: %w", err) + } + signer_config := pkcs7.SignerInfoConfig{ ExtraSignedAttributes: []pkcs7.Attribute{ { Type: asn1.ObjectIdentifier{1, 2, 840, 113583, 1, 1, 8}, Value: context.SignData.RevocationData, }, + *signingCertificate, }, } diff --git a/sign/sign.go b/sign/sign.go index 6c6aea9..aec7477 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -34,6 +34,7 @@ type SignData struct { ObjectId uint32 Signature SignDataSignature Signer crypto.Signer + DigestAlgorithm crypto.Hash Certificate *x509.Certificate CertificateChains [][]*x509.Certificate TSA TSA