mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-02 22:17:06 +08:00
Merge pull request #343 from adrg/signature-support
Signature support refactoring
This commit is contained in:
commit
4d25f7fd1a
@ -7,17 +7,17 @@ package model
|
||||
|
||||
import "github.com/unidoc/unidoc/pdf/core"
|
||||
|
||||
// PdfAppearance contains the common attributes of an appearance form field.
|
||||
type PdfAppearance struct {
|
||||
// PdfSignatureAppearance contains the common attributes of an appearance form field.
|
||||
type PdfSignatureAppearance struct {
|
||||
*PdfField
|
||||
*PdfAnnotationWidget
|
||||
// TODO(gunnsth): Signature is not really part of an appearance.
|
||||
Signature *PdfSignature
|
||||
}
|
||||
|
||||
// NewPdfAppearance returns an initialized annotation widget.
|
||||
func NewPdfAppearance() *PdfAppearance {
|
||||
app := &PdfAppearance{}
|
||||
// NewPdfSignatureAppearance returns an initialized annotation widget.
|
||||
func NewPdfSignatureAppearance() *PdfSignatureAppearance {
|
||||
app := &PdfSignatureAppearance{}
|
||||
app.PdfField = NewPdfField()
|
||||
app.PdfAnnotationWidget = NewPdfAnnotationWidget()
|
||||
app.PdfField.SetContext(app)
|
||||
@ -27,12 +27,13 @@ func NewPdfAppearance() *PdfAppearance {
|
||||
}
|
||||
|
||||
// ToPdfObject implements interface PdfModel.
|
||||
func (app *PdfAppearance) ToPdfObject() core.PdfObject {
|
||||
func (app *PdfSignatureAppearance) ToPdfObject() core.PdfObject {
|
||||
if app.Signature != nil {
|
||||
app.V = app.Signature.ToPdfObject()
|
||||
}
|
||||
app.PdfAnnotation.ToPdfObject()
|
||||
app.PdfField.ToPdfObject()
|
||||
|
||||
container := app.container
|
||||
d := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
|
||||
|
@ -382,22 +382,17 @@ func (a *PdfAppender) ReplacePage(pageNum int, page *PdfPage) {
|
||||
|
||||
// Sign signs a specific page with a digital signature using a specified signature handler.
|
||||
// Returns an Acroform and PdfAppearance that can be used to customize the signature appearance.
|
||||
func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *PdfAcroForm, appearance *PdfAppearance, err error) {
|
||||
func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *PdfAcroForm, appearance *PdfSignatureAppearance, err error) {
|
||||
acroForm = a.Reader.AcroForm
|
||||
if acroForm == nil {
|
||||
acroForm = NewPdfAcroForm()
|
||||
}
|
||||
|
||||
pageIndex := pageNum - 1
|
||||
var page *PdfPage
|
||||
for i, p := range a.pages {
|
||||
if i == pageIndex {
|
||||
page = p.Duplicate()
|
||||
break
|
||||
}
|
||||
}
|
||||
if page == nil {
|
||||
if pageIndex < 0 || pageIndex > len(a.pages)-1 {
|
||||
return nil, nil, fmt.Errorf("page %d not found", pageNum)
|
||||
}
|
||||
page := a.pages[pageIndex].Duplicate()
|
||||
|
||||
// TODO add more checks before set the fields
|
||||
acroForm.SigFlags = core.MakeInteger(3)
|
||||
@ -416,7 +411,7 @@ func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *Pdf
|
||||
}
|
||||
a.addNewObjects(sig.container)
|
||||
|
||||
appearance = NewPdfAppearance()
|
||||
appearance = NewPdfSignatureAppearance()
|
||||
|
||||
fields := append(acroForm.AllFields(), appearance.PdfField)
|
||||
acroForm.Fields = &fields
|
||||
@ -456,7 +451,6 @@ func (a *PdfAppender) Write(w io.Writer) error {
|
||||
if a.written {
|
||||
return errors.New("appender write can only be invoked once")
|
||||
}
|
||||
a.written = true
|
||||
|
||||
writer := NewPdfWriter()
|
||||
|
||||
@ -704,6 +698,7 @@ func (a *PdfAppender) Write(w io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
a.written = true
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -245,46 +245,37 @@ func (f *PdfField) String() string {
|
||||
}
|
||||
|
||||
// ToPdfObject sets the common field elements.
|
||||
// Note: Call the more field context's ToPdfObject to set both the generic and non-generic information.
|
||||
// Note: Call the more field context's ToPdfObject to set both the generic and
|
||||
// non-generic information.
|
||||
func (f *PdfField) ToPdfObject() core.PdfObject {
|
||||
container := f.container
|
||||
d := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
|
||||
d.SetIfNotNil("FT", f.FT)
|
||||
if f.Parent != nil {
|
||||
d.Set("Parent", f.Parent.GetContainingPdfObject())
|
||||
}
|
||||
// Create an array of the kids (fields or widgets).
|
||||
kids := core.MakeArray()
|
||||
if f.Kids != nil {
|
||||
// Create an array of the kids (fields or widgets).
|
||||
for _, child := range f.Kids {
|
||||
kids.Append(child.ToPdfObject())
|
||||
}
|
||||
for _, child := range f.Kids {
|
||||
kids.Append(child.ToPdfObject())
|
||||
}
|
||||
for _, annot := range f.Annotations {
|
||||
kids.Append(annot.GetContext().ToPdfObject())
|
||||
}
|
||||
|
||||
if f.Annotations != nil {
|
||||
for _, annot := range f.Annotations {
|
||||
kids.Append(annot.GetContext().ToPdfObject())
|
||||
}
|
||||
// Set fields.
|
||||
if f.Parent != nil {
|
||||
d.SetIfNotNil("Parent", f.Parent.GetContainingPdfObject())
|
||||
}
|
||||
|
||||
if f.Kids != nil || f.Annotations != nil {
|
||||
if kids.Len() > 0 {
|
||||
d.Set("Kids", kids)
|
||||
}
|
||||
|
||||
d.SetIfNotNil("FT", f.FT)
|
||||
d.SetIfNotNil("T", f.T)
|
||||
d.SetIfNotNil("TU", f.TU)
|
||||
d.SetIfNotNil("TM", f.TM)
|
||||
d.SetIfNotNil("Ff", f.Ff)
|
||||
if f.V != nil {
|
||||
d.Set("V", f.V)
|
||||
}
|
||||
if f.DV != nil {
|
||||
d.Set("DV", f.DV)
|
||||
}
|
||||
if f.AA != nil {
|
||||
d.Set("AA", f.AA)
|
||||
}
|
||||
d.SetIfNotNil("V", f.V)
|
||||
d.SetIfNotNil("DV", f.DV)
|
||||
d.SetIfNotNil("AA", f.AA)
|
||||
|
||||
return container
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ func (a *adobePKCS7Detached) InitSignature(sig *model.PdfSignature) error {
|
||||
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
|
||||
@ -73,23 +73,21 @@ func (a *adobePKCS7Detached) NewDigest(sig *model.PdfSignature) (model.Hasher, e
|
||||
// Validate validates PdfSignature.
|
||||
func (a *adobePKCS7Detached) Validate(sig *model.PdfSignature, digest model.Hasher) (model.SignatureValidationResult, error) {
|
||||
signed := sig.Contents.Bytes()
|
||||
|
||||
buffer := digest.(*bytes.Buffer)
|
||||
p7, err := pkcs7.Parse(signed)
|
||||
if err != nil {
|
||||
return model.SignatureValidationResult{}, err
|
||||
}
|
||||
|
||||
buffer := digest.(*bytes.Buffer)
|
||||
p7.Content = buffer.Bytes()
|
||||
err = p7.Verify()
|
||||
if err != nil {
|
||||
if err = p7.Verify(); err != nil {
|
||||
return model.SignatureValidationResult{}, err
|
||||
}
|
||||
|
||||
result := model.SignatureValidationResult{
|
||||
return model.SignatureValidationResult{
|
||||
IsSigned: true,
|
||||
IsVerified: true,
|
||||
}
|
||||
return result, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Sign sets the Contents fields.
|
||||
|
@ -45,8 +45,8 @@ func (a *adobeX509RSASHA1) InitSignature(sig *model.PdfSignature) error {
|
||||
//sig.Filter = core.MakeName("Adobe.PPKMS")
|
||||
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
|
||||
|
@ -140,49 +140,23 @@ func (sig *PdfSignature) ToPdfObject() core.PdfObject {
|
||||
}
|
||||
|
||||
dict.Set("Type", sig.Type)
|
||||
dict.SetIfNotNil("Filter", sig.Filter)
|
||||
dict.SetIfNotNil("SubFilter", sig.SubFilter)
|
||||
dict.SetIfNotNil("Contents", sig.Contents)
|
||||
dict.SetIfNotNil("Cert", sig.Cert)
|
||||
dict.SetIfNotNil("ByteRange", sig.ByteRange)
|
||||
dict.SetIfNotNil("Reference", sig.Reference)
|
||||
dict.SetIfNotNil("Changes", sig.Changes)
|
||||
dict.SetIfNotNil("Name", sig.Name)
|
||||
dict.SetIfNotNil("M", sig.M)
|
||||
dict.SetIfNotNil("Reason", sig.Reason)
|
||||
dict.SetIfNotNil("ContactInfo", sig.ContactInfo)
|
||||
dict.SetIfNotNil("ByteRange", sig.ByteRange)
|
||||
dict.SetIfNotNil("Contents", sig.Contents)
|
||||
|
||||
if sig.Filter != nil {
|
||||
dict.Set("Filter", sig.Filter)
|
||||
}
|
||||
if sig.SubFilter != nil {
|
||||
dict.Set("SubFilter", sig.SubFilter)
|
||||
}
|
||||
if sig.Contents != nil {
|
||||
dict.Set("Contents", sig.Contents)
|
||||
}
|
||||
if sig.Cert != nil {
|
||||
dict.Set("Cert", sig.Cert)
|
||||
}
|
||||
if sig.ByteRange != nil {
|
||||
dict.Set("ByteRange", sig.ByteRange)
|
||||
}
|
||||
if sig.Reference != nil {
|
||||
dict.Set("Reference", sig.Reference)
|
||||
}
|
||||
if sig.Changes != nil {
|
||||
dict.Set("Changes", sig.Changes)
|
||||
}
|
||||
if sig.Name != nil {
|
||||
dict.Set("Name", sig.Name)
|
||||
}
|
||||
if sig.M != nil {
|
||||
dict.Set("M", sig.M)
|
||||
}
|
||||
if sig.Reason != nil {
|
||||
dict.Set("Reason", sig.Reason)
|
||||
}
|
||||
if sig.ContactInfo != nil {
|
||||
dict.Set("ContactInfo", sig.ContactInfo)
|
||||
}
|
||||
if sig.ByteRange != nil {
|
||||
dict.Set("ByteRange", sig.ByteRange)
|
||||
}
|
||||
if sig.Contents != nil {
|
||||
dict.Set("Contents", sig.Contents)
|
||||
}
|
||||
// NOTE: ByteRange and Contents need to be updated dynamically.
|
||||
// TODO: Currently dynamic update is only in the appender, need to support in the PdfWriter
|
||||
// too for the initial signature on document creation.
|
||||
// TODO: Currently dynamic update is only in the appender, need to support
|
||||
// in the PdfWriter too for the initial signature on document creation.
|
||||
return container
|
||||
}
|
||||
|
||||
@ -280,20 +254,15 @@ func (sf *PdfSignatureField) ToPdfObject() core.PdfObject {
|
||||
container := sf.container
|
||||
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
|
||||
dict.Set("FT", core.MakeName("Sig"))
|
||||
|
||||
// Set fields.
|
||||
if sf.V != nil {
|
||||
dict.Set("V", sf.V.ToPdfObject())
|
||||
}
|
||||
if sf.Lock != nil {
|
||||
dict.Set("Lock", sf.Lock)
|
||||
}
|
||||
if sf.SV != nil {
|
||||
dict.Set("SV", sf.SV)
|
||||
}
|
||||
if sf.Kids != nil {
|
||||
dict.Set("Kids", sf.Kids)
|
||||
}
|
||||
|
||||
dict.Set("FT", core.MakeName("Sig"))
|
||||
dict.SetIfNotNil("Lock", sf.Lock)
|
||||
dict.SetIfNotNil("SV", sf.SV)
|
||||
dict.SetIfNotNil("Kids", sf.Kids)
|
||||
// Other standard fields...
|
||||
|
||||
return container
|
||||
@ -332,45 +301,20 @@ func (pss *PdfSignatureFieldSeed) ToPdfObject() core.PdfObject {
|
||||
container := pss.container
|
||||
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
|
||||
if pss.Ff != nil {
|
||||
dict.Set("Ff", pss.Ff)
|
||||
}
|
||||
if pss.Filter != nil {
|
||||
dict.Set("Filter", pss.Filter)
|
||||
}
|
||||
if pss.SubFilter != nil {
|
||||
dict.Set("SubFilter", pss.SubFilter)
|
||||
}
|
||||
if pss.DigestMethod != nil {
|
||||
dict.Set("DigestMethod", pss.DigestMethod)
|
||||
}
|
||||
if pss.V != nil {
|
||||
dict.Set("V", pss.V)
|
||||
}
|
||||
if pss.Cert != nil {
|
||||
dict.Set("Cert", pss.Cert)
|
||||
}
|
||||
if pss.Reasons != nil {
|
||||
dict.Set("Reasons", pss.Reasons)
|
||||
}
|
||||
if pss.MDP != nil {
|
||||
dict.Set("MDP", pss.MDP)
|
||||
}
|
||||
if pss.TimeStamp != nil {
|
||||
dict.Set("TimeStamp", pss.TimeStamp)
|
||||
}
|
||||
if pss.LegalAttestation != nil {
|
||||
dict.Set("LegalAttestation", pss.LegalAttestation)
|
||||
}
|
||||
if pss.AddRevInfo != nil {
|
||||
dict.Set("AddRevInfo", pss.AddRevInfo)
|
||||
}
|
||||
if pss.LockDocument != nil {
|
||||
dict.Set("LockDocument", pss.LockDocument)
|
||||
}
|
||||
if pss.AppearanceFilter != nil {
|
||||
dict.Set("AppearanceFilter", pss.AppearanceFilter)
|
||||
}
|
||||
dict.SetIfNotNil("Ff", pss.Ff)
|
||||
dict.SetIfNotNil("Filter", pss.Filter)
|
||||
dict.SetIfNotNil("SubFilter", pss.SubFilter)
|
||||
dict.SetIfNotNil("DigestMethod", pss.DigestMethod)
|
||||
dict.SetIfNotNil("V", pss.V)
|
||||
dict.SetIfNotNil("Cert", pss.Cert)
|
||||
dict.SetIfNotNil("Reasons", pss.Reasons)
|
||||
dict.SetIfNotNil("MDP", pss.MDP)
|
||||
dict.SetIfNotNil("TimeStamp", pss.TimeStamp)
|
||||
dict.SetIfNotNil("LegalAttestation", pss.LegalAttestation)
|
||||
dict.SetIfNotNil("AddRevInfo", pss.AddRevInfo)
|
||||
dict.SetIfNotNil("LockDocument", pss.LockDocument)
|
||||
dict.SetIfNotNil("AppearanceFilter", pss.AppearanceFilter)
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
@ -396,41 +340,19 @@ type PdfCertificateSeed struct {
|
||||
func (pcs *PdfCertificateSeed) ToPdfObject() core.PdfObject {
|
||||
container := pcs.container
|
||||
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
if pcs.Ff != nil {
|
||||
dict.Set("Ff", pcs.Ff)
|
||||
}
|
||||
if pcs.Subject != nil {
|
||||
dict.Set("Subject", pcs.Subject)
|
||||
}
|
||||
if pcs.SignaturePolicyOID != nil {
|
||||
dict.Set("SignaturePolicyOID", pcs.SignaturePolicyOID)
|
||||
}
|
||||
if pcs.SignaturePolicyHashValue != nil {
|
||||
dict.Set("SignaturePolicyHashValue", pcs.SignaturePolicyHashValue)
|
||||
}
|
||||
if pcs.SignaturePolicyHashAlgorithm != nil {
|
||||
dict.Set("SignaturePolicyHashAlgorithm", pcs.SignaturePolicyHashAlgorithm)
|
||||
}
|
||||
if pcs.SignaturePolicyCommitmentType != nil {
|
||||
dict.Set("SignaturePolicyCommitmentType", pcs.SignaturePolicyCommitmentType)
|
||||
}
|
||||
if pcs.SubjectDN != nil {
|
||||
dict.Set("SubjectDN", pcs.SubjectDN)
|
||||
}
|
||||
if pcs.KeyUsage != nil {
|
||||
dict.Set("KeyUsage", pcs.KeyUsage)
|
||||
}
|
||||
if pcs.Issuer != nil {
|
||||
dict.Set("Issuer", pcs.Issuer)
|
||||
}
|
||||
if pcs.OID != nil {
|
||||
dict.Set("OID", pcs.OID)
|
||||
}
|
||||
if pcs.URL != nil {
|
||||
dict.Set("URL", pcs.URL)
|
||||
}
|
||||
if pcs.URLType != nil {
|
||||
dict.Set("URLType", pcs.URLType)
|
||||
}
|
||||
|
||||
dict.SetIfNotNil("Ff", pcs.Ff)
|
||||
dict.SetIfNotNil("Subject", pcs.Subject)
|
||||
dict.SetIfNotNil("SignaturePolicyOID", pcs.SignaturePolicyOID)
|
||||
dict.SetIfNotNil("SignaturePolicyHashValue", pcs.SignaturePolicyHashValue)
|
||||
dict.SetIfNotNil("SignaturePolicyHashAlgorithm", pcs.SignaturePolicyHashAlgorithm)
|
||||
dict.SetIfNotNil("SignaturePolicyCommitmentType", pcs.SignaturePolicyCommitmentType)
|
||||
dict.SetIfNotNil("SubjectDN", pcs.SubjectDN)
|
||||
dict.SetIfNotNil("KeyUsage", pcs.KeyUsage)
|
||||
dict.SetIfNotNil("Issuer", pcs.Issuer)
|
||||
dict.SetIfNotNil("OID", pcs.OID)
|
||||
dict.SetIfNotNil("URL", pcs.URL)
|
||||
dict.SetIfNotNil("URLType", pcs.URLType)
|
||||
|
||||
return container
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/pdf/core"
|
||||
)
|
||||
|
||||
@ -104,35 +105,44 @@ func (r *PdfReader) ValidateSignatures(handlers []SignatureHandler) ([]Signature
|
||||
field *PdfField
|
||||
handler SignatureHandler
|
||||
}
|
||||
var pairs []*sigFieldPair
|
||||
|
||||
var pairs []*sigFieldPair
|
||||
for _, f := range r.AcroForm.AllFields() {
|
||||
if f.V == nil {
|
||||
continue
|
||||
}
|
||||
if d, found := core.GetDict(f.V); found {
|
||||
if name, ok := core.GetNameVal(d.Get("Type")); ok && name == "Sig" {
|
||||
ind, _ := core.GetIndirect(f.V) // TODO refactoring
|
||||
ind, found := core.GetIndirect(f.V)
|
||||
if !found {
|
||||
common.Log.Debug("ERROR: Signature container is nil")
|
||||
return nil, ErrTypeCheck
|
||||
}
|
||||
|
||||
sig, err := r.newPdfSignatureFromIndirect(ind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairs = append(pairs, &sigFieldPair{sig: sig, field: f})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pair := range pairs {
|
||||
for _, handler := range handlers {
|
||||
if handler.IsApplicable(pair.sig) {
|
||||
pair.handler = handler
|
||||
break
|
||||
// Search for an appropriate handler.
|
||||
var sigHandler SignatureHandler
|
||||
for _, handler := range handlers {
|
||||
if handler.IsApplicable(sig) {
|
||||
sigHandler = handler
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pairs = append(pairs, &sigFieldPair{
|
||||
sig: sig,
|
||||
field: f,
|
||||
handler: sigHandler,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var results []SignatureValidationResult
|
||||
|
||||
for _, pair := range pairs {
|
||||
defaultResult := SignatureValidationResult{
|
||||
IsSigned: true,
|
||||
|
@ -226,9 +226,11 @@ func copyObject(obj core.PdfObject, objectToObjectCopyMap map[core.PdfObject]cor
|
||||
objectToObjectCopyMap[obj] = &newObj
|
||||
return &newObj
|
||||
case *pdfSignDictionary:
|
||||
newObj := &pdfSignDictionary{PdfObjectDictionary: MakeDict()}
|
||||
newObj.handler = t.handler
|
||||
newObj.signature = t.signature
|
||||
newObj := &pdfSignDictionary{
|
||||
PdfObjectDictionary: core.MakeDict(),
|
||||
handler: t.handler,
|
||||
signature: t.signature,
|
||||
}
|
||||
objectToObjectCopyMap[obj] = newObj
|
||||
for _, key := range t.Keys() {
|
||||
val := t.Get(key)
|
||||
|
Loading…
x
Reference in New Issue
Block a user