Merge pull request #343 from adrg/signature-support

Signature support refactoring
This commit is contained in:
Gunnsteinn Hall 2019-02-12 19:57:55 +00:00 committed by GitHub
commit 4d25f7fd1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 112 additions and 193 deletions

View File

@ -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)

View File

@ -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
}

View File

@ -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
}

View File

@ -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.

View File

@ -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

View File

@ -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
}

View File

@ -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,

View File

@ -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)