mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-26 13:48:55 +08:00

* Add timestamp signature handler * Add timestamp signature handler test * fix PR issues * fix PR issues * fix PR issues * Fix Co-authored-by: Gunnsteinn Hall <gunnsteinn.hall@gmail.com>
177 lines
4.7 KiB
Go
177 lines
4.7 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 (
|
|
"bytes"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/unidoc/pkcs7"
|
|
|
|
"github.com/unidoc/unipdf/v3/core"
|
|
"github.com/unidoc/unipdf/v3/model"
|
|
)
|
|
|
|
// Adobe PKCS7 detached signature handler.
|
|
type adobePKCS7Detached struct {
|
|
privateKey *rsa.PrivateKey
|
|
certificate *x509.Certificate
|
|
|
|
emptySignature bool
|
|
emptySignatureLen int
|
|
}
|
|
|
|
// NewEmptyAdobePKCS7Detached creates a new Adobe.PPKMS/Adobe.PPKLite adbe.pkcs7.detached
|
|
// signature handler. The generated signature is empty and of size signatureLen.
|
|
// The signatureLen parameter can be 0 for the signature validation.
|
|
func NewEmptyAdobePKCS7Detached(signatureLen int) (model.SignatureHandler, error) {
|
|
return &adobePKCS7Detached{
|
|
emptySignature: true,
|
|
emptySignatureLen: signatureLen,
|
|
}, nil
|
|
}
|
|
|
|
// NewAdobePKCS7Detached creates a new Adobe.PPKMS/Adobe.PPKLite adbe.pkcs7.detached signature handler.
|
|
// Both parameters may be nil for the signature validation.
|
|
func NewAdobePKCS7Detached(privateKey *rsa.PrivateKey, certificate *x509.Certificate) (model.SignatureHandler, error) {
|
|
return &adobePKCS7Detached{
|
|
certificate: certificate,
|
|
privateKey: privateKey,
|
|
}, nil
|
|
}
|
|
|
|
// InitSignature initialises the PdfSignature.
|
|
func (a *adobePKCS7Detached) InitSignature(sig *model.PdfSignature) error {
|
|
if !a.emptySignature {
|
|
if a.certificate == nil {
|
|
return errors.New("certificate must not be nil")
|
|
}
|
|
if a.privateKey == nil {
|
|
return errors.New("privateKey must not be nil")
|
|
}
|
|
}
|
|
|
|
handler := *a
|
|
sig.Handler = &handler
|
|
sig.Filter = core.MakeName("Adobe.PPKLite")
|
|
sig.SubFilter = core.MakeName("adbe.pkcs7.detached")
|
|
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 (a *adobePKCS7Detached) 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 *adobePKCS7Detached) NewDigest(sig *model.PdfSignature) (model.Hasher, error) {
|
|
return bytes.NewBuffer(nil), nil
|
|
}
|
|
|
|
// Validate validates PdfSignature.
|
|
func (a *adobePKCS7Detached) Validate(sig *model.PdfSignature, digest model.Hasher) (model.SignatureValidationResult, error) {
|
|
signed := sig.Contents.Bytes()
|
|
p7, err := pkcs7.Parse(signed)
|
|
if err != nil {
|
|
return model.SignatureValidationResult{}, err
|
|
}
|
|
|
|
buffer := digest.(*bytes.Buffer)
|
|
p7.Content = buffer.Bytes()
|
|
if err = p7.Verify(); err != nil {
|
|
return model.SignatureValidationResult{}, err
|
|
}
|
|
|
|
return model.SignatureValidationResult{
|
|
IsSigned: true,
|
|
IsVerified: true,
|
|
}, nil
|
|
}
|
|
|
|
// Sign sets the Contents fields.
|
|
func (a *adobePKCS7Detached) Sign(sig *model.PdfSignature, digest model.Hasher) error {
|
|
if a.emptySignature {
|
|
sigLen := a.emptySignatureLen
|
|
if sigLen <= 0 {
|
|
sigLen = 8192
|
|
}
|
|
|
|
sig.Contents = core.MakeHexString(string(make([]byte, sigLen)))
|
|
return nil
|
|
}
|
|
|
|
buffer := digest.(*bytes.Buffer)
|
|
signedData, err := pkcs7.NewSignedData(buffer.Bytes())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Add the signing cert and private key
|
|
if err := signedData.AddSigner(a.certificate, a.privateKey, pkcs7.SignerInfoConfig{}); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Call Detach() is you want to remove content from the signature
|
|
// and generate an S/MIME detached signature
|
|
signedData.Detach()
|
|
// Finish() to obtain the signature bytes
|
|
detachedSignature, err := signedData.Finish()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
data := make([]byte, 8192)
|
|
copy(data, detachedSignature)
|
|
|
|
sig.Contents = core.MakeHexString(string(data))
|
|
return nil
|
|
}
|
|
|
|
// IsApplicable returns true if the signature handler is applicable for the PdfSignature
|
|
func (a *adobePKCS7Detached) 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.pkcs7.detached"
|
|
}
|