From 7b19471330f103fb399bf96c8dda871d49e1bc96 Mon Sep 17 00:00:00 2001 From: Jeroen Bobbeldijk Date: Tue, 11 Jul 2017 08:21:18 +0200 Subject: [PATCH] Valid signature --- sign/helpers.go | 16 ++++++++++++++++ sign/pdfcatalog.go | 4 ++-- sign/pdfinfo.go | 25 +++++++++++++++++++++++++ sign/pdfsignature.go | 2 +- sign/pdftrailer.go | 9 ++++++++- sign/pdfvisualsignature.go | 12 +++++++++--- sign/pdfxref.go | 19 ++++++++++++++----- sign/sign.go | 33 ++++++++++++++++++++++++++++----- verify/verify.go | 8 +------- 9 files changed, 104 insertions(+), 24 deletions(-) create mode 100644 sign/pdfinfo.go diff --git a/sign/helpers.go b/sign/helpers.go index fb1c22d..91472a2 100644 --- a/sign/helpers.go +++ b/sign/helpers.go @@ -1,6 +1,8 @@ package sign import ( + "bitbucket.org/digitorus/pdf" + "errors" "fmt" "io" "math" @@ -9,6 +11,20 @@ import ( "time" ) +func findFirstPage(parent pdf.Value) (pdf.Value, error) { + value_type := parent.Key("Type").String() + if value_type == "/Pages" { + recurse_parent, recurse_err := findFirstPage(parent.Key("Kids").Index(0)) + return recurse_parent, recurse_err + } + + if value_type == "/Page" { + return parent, nil + } + + return parent, errors.New("Could not find first page.") +} + func pdfString(text string) string { text = strings.Replace(text, "\\", "\\\\", -1) text = strings.Replace(text, ")", "\\)", -1) diff --git a/sign/pdfcatalog.go b/sign/pdfcatalog.go index 4f7a42f..b8957b8 100644 --- a/sign/pdfcatalog.go +++ b/sign/pdfcatalog.go @@ -46,9 +46,9 @@ func (context *SignContext) createCatalog() (catalog string, err error) { if !context.SignData.Signature.Approval { if context.SignData.Signature.CertType > 0 { - catalog += " /Perms << /DocMDP " + strconv.Itoa(int(context.SignData.ObjectId)) + " 0 R >>"; + catalog += " /Perms << /DocMDP " + strconv.Itoa(int(context.SignData.ObjectId)) + " 0 R >>" } else { - catalog += " /Perms << /UR3 " + strconv.Itoa(int(context.SignData.ObjectId)) + " 0 R >>"; + catalog += " /Perms << /UR3 " + strconv.Itoa(int(context.SignData.ObjectId)) + " 0 R >>" } } diff --git a/sign/pdfinfo.go b/sign/pdfinfo.go new file mode 100644 index 0000000..d6661ff --- /dev/null +++ b/sign/pdfinfo.go @@ -0,0 +1,25 @@ +package sign + +import ( + "strconv" +) + +func (context *SignContext) createInfo() (info string, err error) { + original_info := context.PDFReader.Trailer().Key("Info") + info = strconv.Itoa(int(context.InfoData.ObjectId)) + " 0 obj\n" + info += "<<" + + info_keys := original_info.Keys() + for _, key := range info_keys { + info += "/" + key + if key == "ModDate" { + info += pdfDateTime(context.SignData.Signature.Info.Date) + } else { + info += pdfString(original_info.Key(key).RawString()) + } + } + + info += ">>" + info += "\nendobj\n" + return info, nil +} diff --git a/sign/pdfsignature.go b/sign/pdfsignature.go index 9317333..956bd2e 100644 --- a/sign/pdfsignature.go +++ b/sign/pdfsignature.go @@ -108,7 +108,7 @@ func (context *SignContext) replaceSignature() error { dst := make([]byte, hex.EncodedLen(len(signature))) hex.Encode(dst, signature) - context.OutputFile.WriteAt(dst, context.ByteRangeValues[0] + context.ByteRangeValues[1] + 1) + context.OutputFile.WriteAt(dst, context.ByteRangeValues[0]+context.ByteRangeValues[1]+1) return nil } diff --git a/sign/pdftrailer.go b/sign/pdftrailer.go index ec06c18..e73b792 100644 --- a/sign/pdftrailer.go +++ b/sign/pdftrailer.go @@ -19,11 +19,18 @@ func (context *SignContext) writeTrailer() error { new_root := "Root " + strconv.FormatInt(int64(context.CatalogData.ObjectId), 10) + " 0 R" size_string := "Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount, 10) - new_size := "Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+3, 10) + new_size := "Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+4, 10) + + info := context.PDFReader.Trailer().Key("Info") + infoPtr := info.GetPtr() + + info_string := "Info " + strconv.Itoa(int(infoPtr.GetID())) + " " + strconv.Itoa(int(infoPtr.GetGen())) + " R" + new_info := "Info " + strconv.FormatInt(int64(context.InfoData.ObjectId), 10) + " 0 R" trailer_string := string(trailer_buf) trailer_string = strings.Replace(trailer_string, root_string, new_root, -1) trailer_string = strings.Replace(trailer_string, size_string, new_size, -1) + trailer_string = strings.Replace(trailer_string, info_string, new_info, -1) // Write the new trailer. if _, err := context.OutputFile.Write([]byte(trailer_string)); err != nil { diff --git a/sign/pdfvisualsignature.go b/sign/pdfvisualsignature.go index b55296a..bbc9007 100644 --- a/sign/pdfvisualsignature.go +++ b/sign/pdfvisualsignature.go @@ -28,10 +28,16 @@ func (context *SignContext) createVisualSignature() (visual_signature string, er rootPtr := root.GetPtr() context.CatalogData.RootString = strconv.Itoa(int(rootPtr.GetID())) + " " + strconv.Itoa(int(rootPtr.GetGen())) + " R" - page := root.Key("Pages").Key("Kids").Index(0).GetPtr() - visual_signature += " /P " + strconv.Itoa(int(page.GetID())) + " " + strconv.Itoa(int(page.GetGen())) + " R" + first_page, err := findFirstPage(root.Key("Pages")) + if err != nil { + return "", err + } - visual_signature += " /F 4" + first_page_ptr := first_page.GetPtr() + + visual_signature += " /P " + strconv.Itoa(int(first_page_ptr.GetID())) + " " + strconv.Itoa(int(first_page_ptr.GetGen())) + " R" + + visual_signature += " /F 132" visual_signature += " /FT /Sig" visual_signature += " /T " + pdfString("Signature") visual_signature += " /Ff 0" diff --git a/sign/pdfxref.go b/sign/pdfxref.go index 7c8fd1e..566e6cb 100644 --- a/sign/pdfxref.go +++ b/sign/pdfxref.go @@ -22,7 +22,7 @@ func (context *SignContext) writeXref() error { func (context *SignContext) writeXrefTable() error { xref_size := "xref\n0 " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount, 10) - new_xref_size := "xref\n0 " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+3, 10) + new_xref_size := "xref\n0 " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+4, 10) if _, err := context.OutputFile.Write([]byte(new_xref_size)); err != nil { return err @@ -35,7 +35,7 @@ func (context *SignContext) writeXrefTable() error { // Create the new catalog xref line. visual_signature_object_start_position := strconv.FormatInt(context.Filesize, 10) - visual_signature_xref_line := leftPad(visual_signature_object_start_position, "0", 10-len(visual_signature_object_start_position)) + " 00000 n\n" + visual_signature_xref_line := leftPad(visual_signature_object_start_position, "0", 10-len(visual_signature_object_start_position)) + " 00000 n \n" // Write the new catalog xref line. if _, err := context.OutputFile.Write([]byte(visual_signature_xref_line)); err != nil { @@ -44,7 +44,7 @@ func (context *SignContext) writeXrefTable() error { // Create the new catalog xref line. catalog_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length, 10) - catalog_xref_line := leftPad(catalog_object_start_position, "0", 10-len(catalog_object_start_position)) + " 00000 n\n" + catalog_xref_line := leftPad(catalog_object_start_position, "0", 10-len(catalog_object_start_position)) + " 00000 n \n" // Write the new catalog xref line. if _, err := context.OutputFile.Write([]byte(catalog_xref_line)); err != nil { @@ -52,8 +52,17 @@ func (context *SignContext) writeXrefTable() error { } // Create the new signature xref line. - signature_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length+context.CatalogData.Length, 10) - signature_xref_line := leftPad(signature_object_start_position, "0", 10-len(signature_object_start_position)) + " 00000 n\n" + info_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length+context.CatalogData.Length, 10) + info_xref_line := leftPad(info_object_start_position, "0", 10-len(info_object_start_position)) + " 00000 n \n" + + // Write the new signature xref line. + if _, err := context.OutputFile.Write([]byte(info_xref_line)); err != nil { + return err + } + + // Create the new signature xref line. + signature_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length+context.CatalogData.Length+context.InfoData.Length, 10) + signature_xref_line := leftPad(signature_object_start_position, "0", 10-len(signature_object_start_position)) + " 00000 n \n" // Write the new signature xref line. if _, err := context.OutputFile.Write([]byte(signature_xref_line)); err != nil { diff --git a/sign/sign.go b/sign/sign.go index fba1e71..e8c1d30 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -3,11 +3,11 @@ package sign import ( "crypto" "crypto/x509" + "io" "os" "time" "bitbucket.org/digitorus/pdf" - "io" ) type CatalogData struct { @@ -29,6 +29,11 @@ type VisualSignData struct { Length int64 } +type InfoData struct { + ObjectId uint32 + Length int64 +} + type SignDataSignature struct { Approval bool CertType uint32 @@ -50,6 +55,7 @@ type SignContext struct { SignData SignData CatalogData CatalogData VisualSignData VisualSignData + InfoData InfoData PDFReader *pdf.Reader NewXrefStart int64 ByteRangeStartByte int64 @@ -81,7 +87,7 @@ func SignFile(input string, output string, sign_data SignData) error { return err } - sign_data.ObjectId = uint32(rdr.XrefInformation.ItemCount) + 2 + sign_data.ObjectId = uint32(rdr.XrefInformation.ItemCount) + 3 // We do size+1 because we insert a newline. context := SignContext{ @@ -95,6 +101,9 @@ func SignFile(input string, output string, sign_data SignData) error { CatalogData: CatalogData{ ObjectId: uint32(rdr.XrefInformation.ItemCount) + 1, }, + InfoData: InfoData{ + ObjectId: uint32(rdr.XrefInformation.ItemCount) + 2, + }, SignData: sign_data, } @@ -149,9 +158,23 @@ func (context *SignContext) SignPDF() error { // Create the signature object signature_object, byte_range_start_byte, signature_contents_start_byte := context.createSignaturePlaceholder() + info, err := context.createInfo() + if err != nil { + return err + } + + context.InfoData.Length = int64(len(info)) + + // Write the new catalog object. + if _, err := context.OutputFile.Write([]byte(info)); err != nil { + return err + } + + appended_bytes := context.Filesize + int64(len(catalog)) + int64(len(visual_signature)) + int64(len(info)) + // Positions are relative to old start position of xref table. - byte_range_start_byte += context.Filesize + int64(len(catalog)) + int64(len(visual_signature)) - signature_contents_start_byte += context.Filesize + int64(len(catalog)) + int64(len(visual_signature)) + byte_range_start_byte += appended_bytes + signature_contents_start_byte += appended_bytes context.ByteRangeStartByte = byte_range_start_byte context.SignatureContentsStartByte = signature_contents_start_byte @@ -162,7 +185,7 @@ func (context *SignContext) SignPDF() error { } // Calculate the new start position of the xref table. - context.NewXrefStart = context.Filesize + int64(len(signature_object)) + int64(len(catalog)) + int64(len(visual_signature)) + context.NewXrefStart = appended_bytes + int64(len(signature_object)) if err := context.writeXref(); err != nil { return err diff --git a/verify/verify.go b/verify/verify.go index a993865..74b9743 100644 --- a/verify/verify.go +++ b/verify/verify.go @@ -13,8 +13,8 @@ import ( "bitbucket.org/digitorus/pdf" "github.com/digitorus/pkcs7" "github.com/digitorus/timestamp" - "golang.org/x/crypto/ocsp" "go/src/log" + "golang.org/x/crypto/ocsp" ) type RevocationInfoArchival struct { @@ -132,12 +132,6 @@ func Verify(file *os.File) (apiResp *Response, err error) { apiResp.Error = fmt.Sprintln("Failed to get ByteRange:", i, err) } - log.Println(v.Key("ByteRange").Index(i-1).Int64()) - log.Println(v.Key("ByteRange").Index(i).Int64()) - log.Println(string(content[0:60])) - log.Println(string(content[len(content)-60:len(content)])) - log.Println(len(content)) - p7.Content = append(p7.Content, content...) }