mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-11 19:29:40 +08:00
177 lines
5.2 KiB
Go
177 lines
5.2 KiB
Go
/*
|
|
* This file is subject to the terms and conditions defined in
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
*/
|
|
|
|
package sighandler
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/asn1"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
|
|
"github.com/unidoc/unipdf/v3/core"
|
|
"github.com/unidoc/unipdf/v3/model"
|
|
)
|
|
|
|
// SignFunc represents a custom signing function. The function should return
|
|
// the computed signature.
|
|
type SignFunc func(sig *model.PdfSignature, digest model.Hasher) ([]byte, error)
|
|
|
|
// Adobe X509 RSA SHA1 signature handler.
|
|
type adobeX509RSASHA1 struct {
|
|
privateKey *rsa.PrivateKey
|
|
certificate *x509.Certificate
|
|
signFunc SignFunc
|
|
}
|
|
|
|
// NewAdobeX509RSASHA1Custom creates a new Adobe.PPKMS/Adobe.PPKLite adbe.x509.rsa_sha1 signature handler
|
|
// with a custom signing function. Both parameters may be nil for the signature validation.
|
|
func NewAdobeX509RSASHA1Custom(certificate *x509.Certificate, signFunc SignFunc) (model.SignatureHandler, error) {
|
|
return &adobeX509RSASHA1{certificate: certificate, signFunc: signFunc}, nil
|
|
}
|
|
|
|
// NewAdobeX509RSASHA1 creates a new Adobe.PPKMS/Adobe.PPKLite adbe.x509.rsa_sha1 signature handler.
|
|
// Both parameters may be nil for the signature validation.
|
|
func NewAdobeX509RSASHA1(privateKey *rsa.PrivateKey, certificate *x509.Certificate) (model.SignatureHandler, error) {
|
|
return &adobeX509RSASHA1{certificate: certificate, privateKey: privateKey}, nil
|
|
}
|
|
|
|
// InitSignature initialises the PdfSignature.
|
|
func (a *adobeX509RSASHA1) InitSignature(sig *model.PdfSignature) error {
|
|
if a.certificate == nil {
|
|
return errors.New("certificate must not be nil")
|
|
}
|
|
if a.privateKey == nil && a.signFunc == nil {
|
|
return errors.New("must provide either a private key or a signing function")
|
|
}
|
|
|
|
handler := *a
|
|
sig.Handler = &handler
|
|
sig.Filter = core.MakeName("Adobe.PPKLite")
|
|
sig.SubFilter = core.MakeName("adbe.x509.rsa_sha1")
|
|
sig.Cert = core.MakeString(string(handler.certificate.Raw))
|
|
sig.Reference = nil
|
|
|
|
digest, err := handler.NewDigest(sig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
digest.Write([]byte("calculate the Contents field size"))
|
|
return handler.Sign(sig, digest)
|
|
}
|
|
|
|
func getHashFromSignatureAlgorithm(sa x509.SignatureAlgorithm) (crypto.Hash, bool) {
|
|
return crypto.SHA1, true
|
|
}
|
|
|
|
func (a *adobeX509RSASHA1) getCertificate(sig *model.PdfSignature) (*x509.Certificate, error) {
|
|
if a.certificate != nil {
|
|
return a.certificate, nil
|
|
}
|
|
|
|
var certData []byte
|
|
switch certObj := sig.Cert.(type) {
|
|
case *core.PdfObjectString:
|
|
certData = certObj.Bytes()
|
|
case *core.PdfObjectArray:
|
|
if certObj.Len() == 0 {
|
|
return nil, errors.New("no signature certificates found")
|
|
}
|
|
for _, obj := range certObj.Elements() {
|
|
certStr, ok := core.GetString(obj)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid certificate object type in signature certificate chain: %T", obj)
|
|
}
|
|
certData = append(certData, certStr.Bytes()...)
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("invalid signature certificate object type: %T", certObj)
|
|
}
|
|
|
|
certs, err := x509.ParseCertificates(certData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return certs[0], nil
|
|
}
|
|
|
|
// NewDigest creates a new digest.
|
|
func (a *adobeX509RSASHA1) NewDigest(sig *model.PdfSignature) (model.Hasher, error) {
|
|
certificate, err := a.getCertificate(sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
h, _ := getHashFromSignatureAlgorithm(certificate.SignatureAlgorithm)
|
|
return h.New(), nil
|
|
}
|
|
|
|
// Validate validates PdfSignature.
|
|
func (a *adobeX509RSASHA1) Validate(sig *model.PdfSignature, digest model.Hasher) (model.SignatureValidationResult, error) {
|
|
certificate, err := a.getCertificate(sig)
|
|
if err != nil {
|
|
return model.SignatureValidationResult{}, err
|
|
}
|
|
|
|
signed := sig.Contents.Bytes()
|
|
var sigHash []byte
|
|
if _, err := asn1.Unmarshal(signed, &sigHash); err != nil {
|
|
return model.SignatureValidationResult{}, err
|
|
}
|
|
h, ok := digest.(hash.Hash)
|
|
if !ok {
|
|
return model.SignatureValidationResult{}, errors.New("hash type error")
|
|
}
|
|
ha, _ := getHashFromSignatureAlgorithm(certificate.SignatureAlgorithm)
|
|
if err := rsa.VerifyPKCS1v15(certificate.PublicKey.(*rsa.PublicKey), ha, h.Sum(nil), sigHash); err != nil {
|
|
return model.SignatureValidationResult{}, err
|
|
}
|
|
return model.SignatureValidationResult{IsSigned: true, IsVerified: true}, nil
|
|
}
|
|
|
|
// Sign sets the Contents fields for the PdfSignature.
|
|
func (a *adobeX509RSASHA1) Sign(sig *model.PdfSignature, digest model.Hasher) error {
|
|
var data []byte
|
|
var err error
|
|
|
|
if a.signFunc != nil {
|
|
data, err = a.signFunc(sig, digest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
h, ok := digest.(hash.Hash)
|
|
if !ok {
|
|
return errors.New("hash type error")
|
|
}
|
|
ha, _ := getHashFromSignatureAlgorithm(a.certificate.SignatureAlgorithm)
|
|
|
|
data, err = rsa.SignPKCS1v15(rand.Reader, a.privateKey, ha, h.Sum(nil))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
data, err = asn1.Marshal(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sig.Contents = core.MakeHexString(string(data))
|
|
return nil
|
|
}
|
|
|
|
// IsApplicable returns true if the signature handler is applicable for the PdfSignature.
|
|
func (a *adobeX509RSASHA1) IsApplicable(sig *model.PdfSignature) bool {
|
|
if sig == nil || sig.Filter == nil || sig.SubFilter == nil {
|
|
return false
|
|
}
|
|
return (*sig.Filter == "Adobe.PPKMS" || *sig.Filter == "Adobe.PPKLite") && *sig.SubFilter == "adbe.x509.rsa_sha1"
|
|
}
|