Add sign functionality
This commit is contained in:
117
sign.go
Normal file
117
sign.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"bitbucket.org/digitorus/pdfsign/sign"
|
||||||
|
"bitbucket.org/digitorus/pdfsign/verify"
|
||||||
|
)
|
||||||
|
|
||||||
|
func usage() {
|
||||||
|
log.Fatal("Usage: sign input.pdf output.pdf certificate.crt private_key.key OR verify input.pdf")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if len(flag.Args()) < 2 {
|
||||||
|
usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
method := flag.Arg(0)
|
||||||
|
if method != "sign" && method != "verify" {
|
||||||
|
usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
input := flag.Arg(1)
|
||||||
|
if len(input) == 0 {
|
||||||
|
usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
if method == "verify" {
|
||||||
|
input_file, err := os.Open(input)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer input_file.Close()
|
||||||
|
|
||||||
|
resp, err := verify.Verify(input_file)
|
||||||
|
log.Println(resp)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if method == "sign" {
|
||||||
|
if len(flag.Args()) < 5 {
|
||||||
|
usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
output := flag.Arg(2)
|
||||||
|
if len(output) == 0 {
|
||||||
|
usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate_data, err := ioutil.ReadFile(flag.Arg(3))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
certificate_data_block, _ := pem.Decode(certificate_data)
|
||||||
|
if certificate_data_block == nil {
|
||||||
|
log.Fatal(errors.New("failed to parse PEM block containing the certificate"))
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := x509.ParseCertificate(certificate_data_block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key_data, err := ioutil.ReadFile(flag.Arg(4))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
key_data_block, _ := pem.Decode(key_data)
|
||||||
|
if key_data_block == nil {
|
||||||
|
log.Fatal(errors.New("failed to parse PEM block containing the private key"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pkey, err := x509.ParsePKCS8PrivateKey(key_data_block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key, ok := pkey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
log.Fatal(errors.New("private key does not implement crypto.Signer"))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sign.SignFile(input, output, sign.SignData{
|
||||||
|
Signature: sign.SignDataSignature{
|
||||||
|
Info: sign.SignDataSignatureInfo{
|
||||||
|
Name: "Jeroen Bobbeldijk",
|
||||||
|
Location: "Rotterdam",
|
||||||
|
Reason: "Test",
|
||||||
|
ContactInfo: "Geen",
|
||||||
|
Date: time.Now().Local(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Signer: key,
|
||||||
|
Certificate: cert,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
} else {
|
||||||
|
log.Println("Signed PDF written to " + output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
109
sign/helpers.go
Normal file
109
sign/helpers.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pdfString(text string) string {
|
||||||
|
text = strings.Replace(text, "\\", "\\\\", -1)
|
||||||
|
text = strings.Replace(text, ")", "\\)", -1)
|
||||||
|
text = strings.Replace(text, "(", "\\(", -1)
|
||||||
|
text = strings.Replace(text, "\r", "\\r", -1)
|
||||||
|
|
||||||
|
text = "(" + text + ")"
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
func pdfDateTime(date time.Time) string {
|
||||||
|
// Calculate timezone offset from GMT.
|
||||||
|
_, original_offset := date.Zone()
|
||||||
|
offset := original_offset
|
||||||
|
if offset < 0 {
|
||||||
|
offset = (offset - offset) - offset
|
||||||
|
}
|
||||||
|
|
||||||
|
offset_duration := time.Duration(offset) * time.Second
|
||||||
|
offset_hours := int(math.Floor(offset_duration.Hours()))
|
||||||
|
offset_minutes := int(math.Floor(offset_duration.Minutes()))
|
||||||
|
offset_minutes = offset_minutes - (offset_hours * 60)
|
||||||
|
|
||||||
|
dateString := "D:" + date.Format("20060102150405")
|
||||||
|
|
||||||
|
// Do some special formatting as the PDF timezone format isn't supported by Go.
|
||||||
|
if original_offset < 0 {
|
||||||
|
dateString += "-"
|
||||||
|
} else {
|
||||||
|
dateString += "+"
|
||||||
|
}
|
||||||
|
|
||||||
|
offset_hours_formatted := fmt.Sprintf("%d", offset_hours)
|
||||||
|
offset_minutes_formatted := fmt.Sprintf("%d", offset_minutes)
|
||||||
|
dateString += leftPad(offset_hours_formatted, "0", 2-len(offset_hours_formatted)) + "'" + leftPad(offset_minutes_formatted, "0", 2-len(offset_minutes_formatted)) + "'"
|
||||||
|
|
||||||
|
return pdfString(dateString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func leftPad(s string, padStr string, pLen int) string {
|
||||||
|
return strings.Repeat(padStr, pLen) + s
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePartFromSourceFileToTargetFile(input_file *os.File, output_file *os.File, offset int64, length int64) error {
|
||||||
|
input_file.Seek(offset, 0)
|
||||||
|
|
||||||
|
// Create a small buffer for proper IO handling.
|
||||||
|
max_chunk_length := int64(1024)
|
||||||
|
|
||||||
|
// If the target length is smaller than our chunk size, use that as chunk size.
|
||||||
|
if length < max_chunk_length {
|
||||||
|
max_chunk_length = length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track read/written bytes so we know when we're done.
|
||||||
|
read_bytes := int64(0)
|
||||||
|
|
||||||
|
// Create a buffer for the chunks.
|
||||||
|
buf := make([]byte, max_chunk_length)
|
||||||
|
for {
|
||||||
|
// Read the chunk from the input file.
|
||||||
|
n, err := input_file.Read(buf)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got to the end of the file, break.
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// If nothing was read, break.
|
||||||
|
if n == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the chunk to the output file.
|
||||||
|
if _, err := output_file.Write(buf[:n]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
read_bytes += int64(n)
|
||||||
|
|
||||||
|
// If we read enough bytes, break.
|
||||||
|
if read_bytes >= length {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// If our next chunk will be too big, make a smaller buffer.
|
||||||
|
// If we won't do this, we might end up with more data than we want.
|
||||||
|
if length-read_bytes < max_chunk_length {
|
||||||
|
buf = make([]byte, length-read_bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
45
sign/pdfbyterange.go
Normal file
45
sign/pdfbyterange.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (context *SignContext) updateByteRange() error {
|
||||||
|
// Get current filesize. Easier than what should be the current size.
|
||||||
|
// @todo: find out of this is safe.
|
||||||
|
output_file_stat, _ := context.OutputFile.Stat()
|
||||||
|
|
||||||
|
// Don't count last newline as file length.
|
||||||
|
output_file_size := output_file_stat.Size() - 1
|
||||||
|
|
||||||
|
// Calculate ByteRange values to replace them.
|
||||||
|
context.ByteRangeValues = make([]int64, 4)
|
||||||
|
|
||||||
|
// Signature ByteRange part 1 start byte is always byte 0.
|
||||||
|
context.ByteRangeValues[0] = int64(0)
|
||||||
|
|
||||||
|
// Signature ByteRange part 1 length always stops at the actual signature start byte.
|
||||||
|
context.ByteRangeValues[1] = context.SignatureContentsStartByte
|
||||||
|
|
||||||
|
// Signature ByteRange part 2 start byte directly starts after the actual signature.
|
||||||
|
context.ByteRangeValues[2] = context.ByteRangeValues[1] + int64(signatureMaxLength)
|
||||||
|
|
||||||
|
// Signature ByteRange part 2 length is everything else of the file.
|
||||||
|
context.ByteRangeValues[3] = output_file_size - context.ByteRangeValues[2]
|
||||||
|
|
||||||
|
new_byte_range := fmt.Sprintf("/ByteRange[%d %d %d %d]", context.ByteRangeValues[0], context.ByteRangeValues[1], context.ByteRangeValues[2], context.ByteRangeValues[3])
|
||||||
|
|
||||||
|
// Make sure our ByteRange string didn't shrink in length.
|
||||||
|
new_byte_range += strings.Repeat(" ", len(signatureByteRangePlaceholder)-len(new_byte_range))
|
||||||
|
|
||||||
|
// Seek to ByteRange position in file.
|
||||||
|
context.OutputFile.Seek(context.ByteRangeStartByte, 0)
|
||||||
|
|
||||||
|
// Write new ByteRange.
|
||||||
|
if _, err := context.OutputFile.Write([]byte(new_byte_range)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
60
sign/pdfcatalog.go
Normal file
60
sign/pdfcatalog.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (context *SignContext) createCatalog() (catalog string, err error) {
|
||||||
|
catalog = strconv.Itoa(int(context.CatalogData.ObjectId)) + " 0 obj\n"
|
||||||
|
catalog += "<< /Type /Catalog"
|
||||||
|
catalog += " /Version /" + context.PDFReader.PDFVersion
|
||||||
|
|
||||||
|
root := context.PDFReader.Trailer().Key("Root")
|
||||||
|
root_keys := root.Keys()
|
||||||
|
found_pages := false
|
||||||
|
for _, key := range root_keys {
|
||||||
|
if key == "Pages" {
|
||||||
|
found_pages = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found_pages {
|
||||||
|
return "", errors.New("Didn't find pages in PDF trailer Root.")
|
||||||
|
}
|
||||||
|
|
||||||
|
rootPtr := root.GetPtr()
|
||||||
|
context.CatalogData.RootString = strconv.Itoa(int(rootPtr.GetID())) + " " + strconv.Itoa(int(rootPtr.GetGen())) + " R"
|
||||||
|
|
||||||
|
pages := root.Key("Pages").GetPtr()
|
||||||
|
catalog += " /Pages " + strconv.Itoa(int(pages.GetID())) + " " + strconv.Itoa(int(pages.GetGen())) + " R"
|
||||||
|
catalog += " /AcroForm <<"
|
||||||
|
catalog += " /Fields [" + strconv.Itoa(int(context.SignData.ObjectId)) + " 0 R]"
|
||||||
|
|
||||||
|
if !context.SignData.Signature.Approval {
|
||||||
|
catalog += " /NeedAppearances false"
|
||||||
|
}
|
||||||
|
|
||||||
|
if context.SignData.Signature.CertType > 0 {
|
||||||
|
catalog += " /SigFlags 3"
|
||||||
|
} else {
|
||||||
|
catalog += " /SigFlags 1"
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog += " >>"
|
||||||
|
|
||||||
|
// @todo: what do these do?
|
||||||
|
if !context.SignData.Signature.Approval {
|
||||||
|
if context.SignData.Signature.CertType > 0 {
|
||||||
|
//catalog += " /Perms << /DocMDP " + strconv.Itoa(int(context.SignData.ObjectId)) + " 0 R >>";
|
||||||
|
} else {
|
||||||
|
//catalog += " /Perms << /UR3 " + strconv.Itoa(int(context.SignData.ObjectId)) + " 0 R >>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog += " >>"
|
||||||
|
catalog += "\nendobj\n"
|
||||||
|
|
||||||
|
return catalog, nil
|
||||||
|
}
|
117
sign/pdfsignature.go
Normal file
117
sign/pdfsignature.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/digitorus/pkcs7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var signatureMaxLength = uint32(11742)
|
||||||
|
var signatureByteRangePlaceholder = "/ByteRange[0 ********** ********** **********]"
|
||||||
|
|
||||||
|
func (context *SignContext) createSignaturePlaceholder() (signature string, byte_range_start_byte int64, signature_contents_start_byte int64) {
|
||||||
|
signature = strconv.Itoa(int(context.SignData.ObjectId)) + " 0 obj\n"
|
||||||
|
signature += "<< /Type /Sig"
|
||||||
|
signature += " /Filter /Adobe.PPKLite"
|
||||||
|
signature += " /SubFilter /adbe.pkcs7.detached"
|
||||||
|
|
||||||
|
byte_range_start_byte = int64(len(signature)) + 1
|
||||||
|
|
||||||
|
// Create a placeholder for the byte range string, we will replace it later.
|
||||||
|
signature += " " + signatureByteRangePlaceholder
|
||||||
|
|
||||||
|
signature_contents_start_byte = int64(len(signature)) + 11
|
||||||
|
|
||||||
|
// Create a placeholder for the actual signature content, we wil replace it later.
|
||||||
|
signature += " /Contents<" + strings.Repeat("0", int(signatureMaxLength)) + ">"
|
||||||
|
|
||||||
|
if context.SignData.Signature.Approval {
|
||||||
|
signature += " /Reference [" // array of signature reference dictionaries
|
||||||
|
signature += " << /Type /SigRef"
|
||||||
|
if context.SignData.Signature.CertType > 0 {
|
||||||
|
signature += " /TransformMethod /DocMDP"
|
||||||
|
signature += " /TransformParams <<"
|
||||||
|
signature += " /Type /TransformParams"
|
||||||
|
signature += " /P " + strconv.Itoa(int(context.SignData.Signature.CertType))
|
||||||
|
signature += " /V /1.2"
|
||||||
|
} else {
|
||||||
|
signature += " /TransformMethod /UR3"
|
||||||
|
signature += " /TransformParams <<"
|
||||||
|
signature += " /Type /TransformParams"
|
||||||
|
signature += " /V /2.2"
|
||||||
|
}
|
||||||
|
|
||||||
|
signature += " >>" // close TransformParams
|
||||||
|
signature += " >>"
|
||||||
|
signature += " ]" // end of reference
|
||||||
|
}
|
||||||
|
|
||||||
|
if context.SignData.Signature.Info.Name != "" {
|
||||||
|
signature += " /Name " + pdfString(context.SignData.Signature.Info.Name)
|
||||||
|
}
|
||||||
|
if context.SignData.Signature.Info.Location != "" {
|
||||||
|
signature += " /Location " + pdfString(context.SignData.Signature.Info.Location)
|
||||||
|
}
|
||||||
|
if context.SignData.Signature.Info.Reason != "" {
|
||||||
|
signature += " /Reason " + pdfString(context.SignData.Signature.Info.Reason)
|
||||||
|
}
|
||||||
|
if context.SignData.Signature.Info.ContactInfo != "" {
|
||||||
|
signature += " /ContactInfo " + pdfString(context.SignData.Signature.Info.ContactInfo)
|
||||||
|
}
|
||||||
|
signature += " /M " + pdfDateTime(context.SignData.Signature.Info.Date)
|
||||||
|
signature += " >>"
|
||||||
|
signature += "\nendobj\n"
|
||||||
|
|
||||||
|
return signature, byte_range_start_byte, signature_contents_start_byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *SignContext) createSignature() ([]byte, error) {
|
||||||
|
|
||||||
|
// Sadly we can't efficiently sign a file, we need to read all the bytes we want to sign.
|
||||||
|
context.OutputFile.Seek(0, 0)
|
||||||
|
sign_buf := bytes.NewBuffer(nil)
|
||||||
|
io.Copy(sign_buf, context.OutputFile)
|
||||||
|
file_content := sign_buf.Bytes()
|
||||||
|
|
||||||
|
// Remove trailing newline.
|
||||||
|
file_content = file_content[:len(file_content)-1]
|
||||||
|
|
||||||
|
// Collect the parts to sign.
|
||||||
|
sign_content := make([]byte, context.ByteRangeValues[1]+context.ByteRangeValues[3])
|
||||||
|
sign_content = append(sign_content, file_content[context.ByteRangeValues[0]:(context.ByteRangeValues[0]+context.ByteRangeValues[1])]...)
|
||||||
|
sign_content = append(sign_content, file_content[context.ByteRangeValues[2]:(context.ByteRangeValues[2]+context.ByteRangeValues[3])]...)
|
||||||
|
|
||||||
|
// Initialize pkcs7 signer.
|
||||||
|
signed_data, err := pkcs7.NewSignedData(sign_content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the signer and sign the data.
|
||||||
|
if err := signed_data.AddSignerChain(context.SignData.Certificate, context.SignData.Signer, context.SignData.CertificateChain, pkcs7.SignerInfoConfig{}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDF needs a detached signature, meaning the content isn't included.
|
||||||
|
signed_data.Detach()
|
||||||
|
|
||||||
|
return signed_data.Finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *SignContext) replaceSignature() error {
|
||||||
|
signature, err := context.createSignature()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := make([]byte, hex.EncodedLen(len(signature)))
|
||||||
|
hex.Encode(dst, signature)
|
||||||
|
|
||||||
|
context.OutputFile.WriteAt(dst, context.ByteRangeValues[0]+context.ByteRangeValues[1])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
44
sign/pdftrailer.go
Normal file
44
sign/pdftrailer.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (context *SignContext) writeTrailer() error {
|
||||||
|
trailer_length := context.PDFReader.XrefInformation.IncludingTrailerEndPos - context.PDFReader.XrefInformation.EndPos
|
||||||
|
|
||||||
|
// Read the trailer so we can replace the size.
|
||||||
|
context.InputFile.Seek(context.PDFReader.XrefInformation.EndPos+1, 0)
|
||||||
|
trailer_buf := make([]byte, trailer_length)
|
||||||
|
if _, err := context.InputFile.Read(trailer_buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
root_string := "Root " + context.CatalogData.RootString
|
||||||
|
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+2, 10)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// Write the new trailer.
|
||||||
|
if _, err := context.OutputFile.Write([]byte(trailer_string)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the new xref start position.
|
||||||
|
if _, err := context.OutputFile.Write([]byte(strconv.FormatInt(context.NewXrefStart, 10) + "\n")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write PDF ending.
|
||||||
|
if _, err := context.OutputFile.Write([]byte("%%EOF\n")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
55
sign/pdfxref.go
Normal file
55
sign/pdfxref.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (context *SignContext) writeXref() error {
|
||||||
|
|
||||||
|
// @todo: support stream xref.
|
||||||
|
|
||||||
|
if context.PDFReader.XrefInformation.Type == "table" {
|
||||||
|
if err := context.writeXrefTable(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return errors.New("Unkwn xref type: " + context.PDFReader.XrefInformation.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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+2, 10)
|
||||||
|
|
||||||
|
if _, err := context.OutputFile.Write([]byte(new_xref_size)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the old xref table to the output pdf.
|
||||||
|
if err := writePartFromSourceFileToTargetFile(context.InputFile, context.OutputFile, context.PDFReader.XrefInformation.StartPos+int64(len(xref_size)), context.PDFReader.XrefInformation.Length-int64(len(xref_size))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new catalog xref line.
|
||||||
|
catalog_object_start_position := strconv.FormatInt(context.PDFReader.XrefInformation.StartPos, 10)
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new signature xref line.
|
||||||
|
signature_object_start_position := strconv.FormatInt(context.PDFReader.XrefInformation.StartPos+context.CatalogData.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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
149
sign/sign.go
Normal file
149
sign/sign.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"bitbucket.org/digitorus/pdf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CatalogData struct {
|
||||||
|
ObjectId uint32
|
||||||
|
Length int64
|
||||||
|
RootString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignData struct {
|
||||||
|
ObjectId uint32
|
||||||
|
Signature SignDataSignature
|
||||||
|
Signer crypto.Signer
|
||||||
|
Certificate *x509.Certificate
|
||||||
|
CertificateChain []*x509.Certificate
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignDataSignature struct {
|
||||||
|
Approval bool
|
||||||
|
CertType uint32
|
||||||
|
Info SignDataSignatureInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignDataSignatureInfo struct {
|
||||||
|
Name string
|
||||||
|
Location string
|
||||||
|
Reason string
|
||||||
|
ContactInfo string
|
||||||
|
Date time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignContext struct {
|
||||||
|
InputFile *os.File
|
||||||
|
OutputFile *os.File
|
||||||
|
SignData SignData
|
||||||
|
CatalogData CatalogData
|
||||||
|
PDFReader *pdf.Reader
|
||||||
|
NewXrefStart int64
|
||||||
|
ByteRangeStartByte int64
|
||||||
|
SignatureContentsStartByte int64
|
||||||
|
ByteRangeValues []int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignFile(input string, output string, sign_data SignData) error {
|
||||||
|
input_file, err := os.Open(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer input_file.Close()
|
||||||
|
|
||||||
|
output_file, err := os.Create(output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer output_file.Close()
|
||||||
|
|
||||||
|
finfo, err := input_file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
size := finfo.Size()
|
||||||
|
|
||||||
|
rdr, err := pdf.NewReader(input_file, size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sign_data.ObjectId = uint32(rdr.XrefInformation.ItemCount) + 1
|
||||||
|
|
||||||
|
context := SignContext{
|
||||||
|
PDFReader: rdr,
|
||||||
|
InputFile: input_file,
|
||||||
|
OutputFile: output_file,
|
||||||
|
CatalogData: CatalogData{
|
||||||
|
ObjectId: uint32(rdr.XrefInformation.ItemCount),
|
||||||
|
},
|
||||||
|
SignData: sign_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = context.SignPDF()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *SignContext) SignPDF() error {
|
||||||
|
// Write the PDF file to the output up til the xref.
|
||||||
|
if err := writePartFromSourceFileToTargetFile(context.InputFile, context.OutputFile, 0, context.PDFReader.XrefInformation.StartPos); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog, err := context.createCatalog()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
context.CatalogData.Length = int64(len(catalog))
|
||||||
|
|
||||||
|
// Write the new catalog object.
|
||||||
|
if _, err := context.OutputFile.Write([]byte(catalog)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the signature object
|
||||||
|
signature_object, byte_range_start_byte, signature_contents_start_byte := context.createSignaturePlaceholder()
|
||||||
|
|
||||||
|
// Positions are relative to old start position of xref table.
|
||||||
|
byte_range_start_byte += context.PDFReader.XrefInformation.StartPos + int64(len(catalog))
|
||||||
|
signature_contents_start_byte += context.PDFReader.XrefInformation.StartPos + int64(len(catalog))
|
||||||
|
|
||||||
|
context.ByteRangeStartByte = byte_range_start_byte
|
||||||
|
context.SignatureContentsStartByte = signature_contents_start_byte
|
||||||
|
|
||||||
|
// Write the new signature object.
|
||||||
|
if _, err := context.OutputFile.Write([]byte(signature_object)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the new start position of the xref table.
|
||||||
|
context.NewXrefStart = context.PDFReader.XrefInformation.StartPos + int64(len(signature_object)) + int64(len(catalog))
|
||||||
|
|
||||||
|
if err := context.writeXref(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := context.writeTrailer(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := context.updateByteRange(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := context.replaceSignature(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@@ -1,306 +1,302 @@
|
|||||||
// http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf
|
package verify
|
||||||
|
|
||||||
package signatures
|
import (
|
||||||
|
"bytes"
|
||||||
import (
|
"crypto/x509"
|
||||||
"bytes"
|
"encoding/asn1"
|
||||||
"crypto/x509"
|
"fmt"
|
||||||
"encoding/asn1"
|
"io"
|
||||||
"fmt"
|
"io/ioutil"
|
||||||
"io"
|
"os"
|
||||||
"os"
|
"time"
|
||||||
"io/ioutil"
|
|
||||||
// "log"
|
"bitbucket.org/digitorus/pdf"
|
||||||
// "strings"
|
"github.com/digitorus/pkcs7"
|
||||||
"time"
|
"github.com/digitorus/timestamp"
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
"bitbucket.org/digitorus/pdf"
|
)
|
||||||
"github.com/digitorus/timestamp"
|
|
||||||
"go.mozilla.org/pkcs7"
|
type RevocationInfoArchival struct {
|
||||||
"golang.org/x/crypto/ocsp"
|
CRL RevCRL `asn1:"tag:0,optional,explicit"`
|
||||||
)
|
OCSP RevOCSP `asn1:"tag:1,optional,explicit"`
|
||||||
|
OtherRevInfo OtherRevInfo `asn1:"tag:2,optional,explicit"`
|
||||||
type RevocationInfoArchival struct {
|
}
|
||||||
CRL RevCRL `asn1:"tag:0,optional,explicit"`
|
|
||||||
OCSP RevOCSP `asn1:"tag:1,optional,explicit"`
|
type RevCRL []asn1.RawValue
|
||||||
OtherRevInfo OtherRevInfo `asn1:"tag:2,optional,explicit"`
|
type RevOCSP []asn1.RawValue
|
||||||
}
|
|
||||||
|
type OtherRevInfo struct {
|
||||||
type RevCRL []asn1.RawValue
|
Type asn1.ObjectIdentifier
|
||||||
type RevOCSP []asn1.RawValue
|
Value []byte
|
||||||
|
}
|
||||||
type OtherRevInfo struct {
|
|
||||||
Type asn1.ObjectIdentifier
|
type Response struct {
|
||||||
Value []byte
|
Error string
|
||||||
}
|
|
||||||
|
DocumentInfo string
|
||||||
type Response struct {
|
Signers []Signer
|
||||||
Error string
|
}
|
||||||
|
|
||||||
DocumentInfo string
|
type Signer struct {
|
||||||
Signers []Signer
|
Name string
|
||||||
}
|
Reason string
|
||||||
|
Location string
|
||||||
type Signer struct {
|
ContactInfo string
|
||||||
Name string
|
ValidSignature bool
|
||||||
Reason string
|
TrustedIssuer bool
|
||||||
Location string
|
RevokedCertificate bool
|
||||||
ContactInfo string
|
Certificates []Certificate
|
||||||
ValidSignature bool
|
TimeStamp *timestamp.Timestamp
|
||||||
TrustedIssuer bool
|
}
|
||||||
RevokedCertificate bool
|
|
||||||
Certificates []Certificate
|
type Certificate struct {
|
||||||
TimeStamp *timestamp.Timestamp
|
Certificate *x509.Certificate
|
||||||
}
|
VerifyError string
|
||||||
|
OCSPResponse *ocsp.Response
|
||||||
type Certificate struct {
|
OCSPEmbedded bool
|
||||||
Certificate *x509.Certificate
|
CRLRevoked time.Time
|
||||||
VerifyError string
|
CRLEmbedded bool
|
||||||
OCSPResponse *ocsp.Response
|
}
|
||||||
OCSPEmbedded bool
|
|
||||||
CRLRevoked time.Time
|
func Verify(file *os.File) (apiResp *Response, err error) {
|
||||||
CRLEmbedded bool
|
defer func() {
|
||||||
}
|
if r := recover(); r != nil {
|
||||||
|
apiResp = nil
|
||||||
func Verify(file *os.File) (apiResp *Response, err error) {
|
err = fmt.Errorf("Failed to verify file (%v)", r)
|
||||||
defer func() {
|
}
|
||||||
if r := recover(); r != nil {
|
}()
|
||||||
apiResp = nil
|
apiResp = &Response{}
|
||||||
err = fmt.Errorf("Failed to verify file (%v)", r)
|
|
||||||
}
|
finfo, _ := file.Stat()
|
||||||
}()
|
size := finfo.Size()
|
||||||
apiResp = &Response{}
|
|
||||||
|
rdr, err := pdf.NewReader(file, size)
|
||||||
finfo, _ := file.Stat()
|
if err != nil {
|
||||||
size := finfo.Size()
|
return nil, fmt.Errorf("Failed to open file")
|
||||||
|
}
|
||||||
rdr, err := pdf.NewReader(file, size)
|
|
||||||
if err != nil {
|
// AcroForm will contain a SigFlags value if the form contains a digital signature
|
||||||
return nil, fmt.Errorf("Failed to open file")
|
t := rdr.Trailer().Key("Root").Key("AcroForm").Key("SigFlags")
|
||||||
}
|
if t.IsNull() {
|
||||||
|
return nil, fmt.Errorf("No digital signature in document")
|
||||||
// AcroForm will contain a SigFlags value if the form contains a digital signature
|
}
|
||||||
t := rdr.Trailer().Key("Root").Key("AcroForm").Key("SigFlags")
|
|
||||||
if t.IsNull() {
|
// Walk over the cross references in the document
|
||||||
return nil, fmt.Errorf("No digital signature in document")
|
for _, x := range rdr.Xref() {
|
||||||
}
|
// Get the xref object Value
|
||||||
|
v := rdr.Resolve(x.Ptr(), x.Ptr())
|
||||||
// Walk over the cross references in the document
|
|
||||||
for _, x := range rdr.Xref() {
|
// We must have a Filter Adobe.PPKLite
|
||||||
// Get the xref object Value
|
if v.Key("Filter").Name() != "Adobe.PPKLite" {
|
||||||
v := rdr.Resolve(x.Ptr(), x.Ptr())
|
continue
|
||||||
|
}
|
||||||
// We must have a Filter Adobe.PPKLite
|
|
||||||
if v.Key("Filter").Name() != "Adobe.PPKLite" {
|
signer := Signer{
|
||||||
continue
|
Name: v.Key("Name").Text(),
|
||||||
}
|
Reason: v.Key("Reason").Text(),
|
||||||
|
Location: v.Key("Location").Text(),
|
||||||
signer := Signer{
|
ContactInfo: v.Key("ContactInfo").Text(),
|
||||||
Name: v.Key("Name").Text(),
|
}
|
||||||
Reason: v.Key("Reason").Text(),
|
|
||||||
Location: v.Key("Location").Text(),
|
// (Required) The signature value. When ByteRange is present, the
|
||||||
ContactInfo: v.Key("ContactInfo").Text(),
|
// value shall be a hexadecimal string (see 7.3.4.3, “Hexadecimal
|
||||||
}
|
// Stringsâ€) representing the value of the byte range digest.
|
||||||
|
// For public-key signatures, Contents should be either a DER-encoded
|
||||||
// (Required) The signature value. When ByteRange is present, the
|
// PKCS#1 binary data object or a DER-encoded PKCS#7 binary data object.
|
||||||
// value shall be a hexadecimal string (see 7.3.4.3, “Hexadecimal
|
// Space for the Contents value must be allocated before the message
|
||||||
// Strings”) representing the value of the byte range digest.
|
// digest is computed. (See 7.3.4, “String Objects“)
|
||||||
// For public-key signatures, Contents should be either a DER-encoded
|
p7, err := pkcs7.Parse([]byte(v.Key("Contents").RawString()))
|
||||||
// PKCS#1 binary data object or a DER-encoded PKCS#7 binary data object.
|
if err != nil {
|
||||||
// Space for the Contents value must be allocated before the message
|
//fmt.Println(err)
|
||||||
// digest is computed. (See 7.3.4, “String Objects“)
|
continue
|
||||||
p7, err := pkcs7.Parse([]byte(v.Key("Contents").RawString()))
|
}
|
||||||
if err != nil {
|
|
||||||
//fmt.Println(err)
|
// An array of pairs of integers (starting byte offset, length in
|
||||||
continue
|
// bytes) that shall describe the exact byte range for the digest
|
||||||
}
|
// calculation. Multiple discontiguous byte ranges shall be used to
|
||||||
|
// describe a digest that does not include the signature value (the
|
||||||
// An array of pairs of integers (starting byte offset, length in
|
// Contents entry) itself.
|
||||||
// bytes) that shall describe the exact byte range for the digest
|
for i := 0; i < v.Key("ByteRange").Len(); i++ {
|
||||||
// calculation. Multiple discontiguous byte ranges shall be used to
|
// As the byte range comes in pairs, we increment one extra
|
||||||
// describe a digest that does not include the signature value (the
|
i++
|
||||||
// Contents entry) itself.
|
|
||||||
for i := 0; i < v.Key("ByteRange").Len(); i++ {
|
// Read the byte range from the raw file and add it to the contents.
|
||||||
// As the byte range comes in pairs, we increment one extra
|
// This content will be hashed with the corresponding algorithm to
|
||||||
i++
|
// verify the signature.
|
||||||
|
content, err := ioutil.ReadAll(io.NewSectionReader(file, v.Key("ByteRange").Index(i-1).Int64(), v.Key("ByteRange").Index(i).Int64()))
|
||||||
// Read the byte range from the raw file and add it to the contents.
|
if err != nil {
|
||||||
// This content will be hashed with the corresponding algorithm to
|
apiResp.Error = fmt.Sprintln("Failed to get ByteRange:", i, err)
|
||||||
// verify the signature.
|
}
|
||||||
content, err := ioutil.ReadAll(io.NewSectionReader(file, v.Key("ByteRange").Index(i-1).Int64(), v.Key("ByteRange").Index(i).Int64()))
|
p7.Content = append(p7.Content, content...)
|
||||||
if err != nil {
|
}
|
||||||
apiResp.Error = fmt.Sprintln("Failed to get ByteRange:", i, err)
|
|
||||||
}
|
// Signer certificate
|
||||||
p7.Content = append(p7.Content, content...)
|
// http://www.alvestrand.no/objectid/1.2.840.113549.1.9.html
|
||||||
}
|
// http://www.alvestrand.no/objectid/1.2.840.113583.1.1.8.html
|
||||||
|
var isn []byte
|
||||||
// Signer certificate
|
for _, s := range p7.Signers {
|
||||||
// http://www.alvestrand.no/objectid/1.2.840.113549.1.9.html
|
isn = s.IssuerAndSerialNumber.IssuerName.FullBytes
|
||||||
// http://www.alvestrand.no/objectid/1.2.840.113583.1.1.8.html
|
//for _, a := range s.AuthenticatedAttributes {
|
||||||
var isn []byte
|
//fmt.Printf("A: %v, %#v\n", s.IssuerAndSerialNumber.SerialNumber, a.Type)
|
||||||
for _, s := range p7.Signers {
|
//}
|
||||||
isn = s.IssuerAndSerialNumber.IssuerName.FullBytes
|
|
||||||
//for _, a := range s.AuthenticatedAttributes {
|
// Timestamp
|
||||||
//fmt.Printf("A: %v, %#v\n", s.IssuerAndSerialNumber.SerialNumber, a.Type)
|
// http://www.alvestrand.no/objectid/1.2.840.113549.1.9.16.2.14.html
|
||||||
//}
|
// Timestamp
|
||||||
|
// 1.2.840.113549.1.9.16.2.14 - RFC 3161 id-aa-timeStampToken
|
||||||
// Timestamp
|
for _, attr := range s.UnauthenticatedAttributes {
|
||||||
// http://www.alvestrand.no/objectid/1.2.840.113549.1.9.16.2.14.html
|
//fmt.Printf("U: %v, %#v\n", s.IssuerAndSerialNumber.SerialNumber, attr.Type)
|
||||||
// Timestamp
|
|
||||||
// 1.2.840.113549.1.9.16.2.14 - RFC 3161 id-aa-timeStampToken
|
if attr.Type.Equal(asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 14}) {
|
||||||
for _, attr := range s.UnauthenticatedAttributes {
|
//fmt.Println("Found timestamp")
|
||||||
//fmt.Printf("U: %v, %#v\n", s.IssuerAndSerialNumber.SerialNumber, attr.Type)
|
|
||||||
|
signer.TimeStamp, err = timestamp.Parse(attr.Value.Bytes)
|
||||||
if attr.Type.Equal(asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 14}) {
|
if err != nil {
|
||||||
//fmt.Println("Found timestamp")
|
apiResp.Error = fmt.Sprintln("Failed to parse timestamp", err)
|
||||||
|
}
|
||||||
signer.TimeStamp, err = timestamp.Parse(attr.Value.Bytes)
|
|
||||||
if err != nil {
|
break
|
||||||
apiResp.Error = fmt.Sprintln("Failed to parse timestamp", err)
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break
|
|
||||||
}
|
// Directory of certificates, including OCSP
|
||||||
}
|
//var ica *x509.Certificate
|
||||||
}
|
certPool := x509.NewCertPool()
|
||||||
|
for _, cert := range p7.Certificates {
|
||||||
// Directory of certificates, including OCSP
|
certPool.AddCert(cert)
|
||||||
//var ica *x509.Certificate
|
if bytes.Equal(isn, cert.RawSubject) {
|
||||||
certPool := x509.NewCertPool()
|
//ica = cert
|
||||||
for _, cert := range p7.Certificates {
|
}
|
||||||
certPool.AddCert(cert)
|
}
|
||||||
if bytes.Equal(isn, cert.RawSubject) {
|
|
||||||
//ica = cert
|
// Verify the digital signature of the pdf file.
|
||||||
}
|
err = p7.VerifyWithChain(certPool)
|
||||||
}
|
if err != nil {
|
||||||
|
err = p7.Verify()
|
||||||
// Verify the digital signature of the pdf file.
|
if err == nil {
|
||||||
err = p7.VerifyWithChain(certPool)
|
signer.ValidSignature = true
|
||||||
if err != nil {
|
signer.TrustedIssuer = false
|
||||||
err = p7.Verify()
|
}
|
||||||
if err == nil {
|
//apiResp.Error = fmt.Sprintln("Failed to verify signature:", err)
|
||||||
signer.ValidSignature = true
|
} else {
|
||||||
signer.TrustedIssuer = false
|
signer.ValidSignature = true
|
||||||
}
|
signer.TrustedIssuer = true
|
||||||
//apiResp.Error = fmt.Sprintln("Failed to verify signature:", err)
|
}
|
||||||
} else {
|
|
||||||
signer.ValidSignature = true
|
// PDF signature certificate revocation information attribute (1.2.840.113583.1.1.8)
|
||||||
signer.TrustedIssuer = true
|
var revInfo RevocationInfoArchival
|
||||||
}
|
p7.UnmarshalSignedAttribute(asn1.ObjectIdentifier{1, 2, 840, 113583, 1, 1, 8}, &revInfo)
|
||||||
|
|
||||||
// PDF signature certificate revocation information attribute (1.2.840.113583.1.1.8)
|
// Parse OCSP response
|
||||||
var revInfo RevocationInfoArchival
|
var ocspStatus = make(map[string]*ocsp.Response)
|
||||||
p7.UnmarshalSignedAttribute(asn1.ObjectIdentifier{1, 2, 840, 113583, 1, 1, 8}, &revInfo)
|
for _, o := range revInfo.OCSP {
|
||||||
|
resp, err := ocsp.ParseResponse(o.FullBytes, nil)
|
||||||
// Parse OCSP response
|
if err != nil {
|
||||||
var ocspStatus = make(map[string]*ocsp.Response)
|
apiResp.Error = fmt.Sprintln("Failed to parse or verify OCSP response", err)
|
||||||
for _, o := range revInfo.OCSP {
|
ocspStatus[fmt.Sprintf("%x", resp.SerialNumber)] = nil
|
||||||
resp, err := ocsp.ParseResponse(o.FullBytes, nil)
|
} else {
|
||||||
if err != nil {
|
ocspStatus[fmt.Sprintf("%x", resp.SerialNumber)] = resp
|
||||||
apiResp.Error = fmt.Sprintln("Failed to parse or verify OCSP response", err)
|
}
|
||||||
ocspStatus[fmt.Sprintf("%x", resp.SerialNumber)] = nil
|
}
|
||||||
} else {
|
|
||||||
ocspStatus[fmt.Sprintf("%x", resp.SerialNumber)] = resp
|
// Build certificate chains and verify revocation status
|
||||||
}
|
for _, cert := range p7.Certificates {
|
||||||
}
|
var c Certificate
|
||||||
|
c.Certificate = cert
|
||||||
// Build certificate chains and verify revocation status
|
|
||||||
for _, cert := range p7.Certificates {
|
chain, err := cert.Verify(x509.VerifyOptions{
|
||||||
var c Certificate
|
Intermediates: certPool,
|
||||||
c.Certificate = cert
|
CurrentTime: cert.NotBefore,
|
||||||
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
||||||
chain, err := cert.Verify(x509.VerifyOptions{
|
})
|
||||||
Intermediates: certPool,
|
|
||||||
CurrentTime: cert.NotBefore,
|
if err != nil {
|
||||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
c.VerifyError = err.Error()
|
||||||
})
|
}
|
||||||
|
|
||||||
if err != nil {
|
if resp, ok := ocspStatus[fmt.Sprintf("%x", cert.SerialNumber)]; ok {
|
||||||
c.VerifyError = err.Error()
|
c.OCSPResponse = resp
|
||||||
}
|
c.OCSPEmbedded = true
|
||||||
|
|
||||||
if resp, ok := ocspStatus[fmt.Sprintf("%x", cert.SerialNumber)]; ok {
|
if resp.Status != ocsp.Good {
|
||||||
c.OCSPResponse = resp
|
signer.RevokedCertificate = true
|
||||||
c.OCSPEmbedded = true
|
}
|
||||||
|
|
||||||
if resp.Status != ocsp.Good {
|
if len(chain) > 1 && len(chain[0]) > 1 {
|
||||||
signer.RevokedCertificate = true
|
issuer := chain[0][1]
|
||||||
}
|
if resp.Certificate != nil {
|
||||||
|
err = resp.Certificate.CheckSignatureFrom(issuer)
|
||||||
if len(chain) > 1 && len(chain[0]) > 1 {
|
if err != nil {
|
||||||
issuer := chain[0][1]
|
apiResp.Error = fmt.Sprintln("OCSP signing cerificate not from certificate issuer:", err)
|
||||||
if resp.Certificate != nil {
|
}
|
||||||
err = resp.Certificate.CheckSignatureFrom(issuer)
|
} else {
|
||||||
if err != nil {
|
// CA Signed response
|
||||||
apiResp.Error = fmt.Sprintln("OCSP signing cerificate not from certificate issuer:", err)
|
err = resp.CheckSignatureFrom(issuer)
|
||||||
}
|
if err != nil {
|
||||||
} else {
|
apiResp.Error = fmt.Sprintln("Failed to verify OCSP response signature:", err)
|
||||||
// CA Signed response
|
}
|
||||||
err = resp.CheckSignatureFrom(issuer)
|
}
|
||||||
if err != nil {
|
}
|
||||||
apiResp.Error = fmt.Sprintln("Failed to verify OCSP response signature:", err)
|
} else {
|
||||||
}
|
// Check OCSP status for certificate out of band
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
// Add certificate to result
|
||||||
// Check OCSP status for certificate out of band
|
signer.Certificates = append(signer.Certificates, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add certificate to result
|
// Certificate revocation lists when included in this document
|
||||||
signer.Certificates = append(signer.Certificates, c)
|
for _, crl := range p7.CRLs {
|
||||||
}
|
//var crlissuer *pkix.Name
|
||||||
|
//crlissuerdr.FillFromRDNSequence(&crl.TBSCertList.Issuer)
|
||||||
// Certificate revocation lists when included in this document
|
if len(crl.TBSCertList.RevokedCertificates) > 0 {
|
||||||
for _, crl := range p7.CRLs {
|
|
||||||
//var crlissuer *pkix.Name
|
}
|
||||||
//crlissuerdr.FillFromRDNSequence(&crl.TBSCertList.Issuer)
|
//apiResp.Error = fmt.Sprintf("CRL %v , with %d entries\n", crl.TBSCertList.Issuer, len(crl.TBSCertList.RevokedCertificates))
|
||||||
if len(crl.TBSCertList.RevokedCertificates) > 0 {
|
// TODO(vanbroup): Check revocation via CRL
|
||||||
|
// signer.RevokedCertificate = true
|
||||||
}
|
}
|
||||||
//apiResp.Error = fmt.Sprintf("CRL %v , with %d entries\n", crl.TBSCertList.Issuer, len(crl.TBSCertList.RevokedCertificates))
|
|
||||||
// TODO(vanbroup): Check revocation via CRL
|
// Parse CRL file
|
||||||
// signer.RevokedCertificate = true
|
for _, c := range revInfo.CRL {
|
||||||
}
|
crl, err := x509.ParseCRL(c.FullBytes)
|
||||||
|
if err != nil {
|
||||||
// Parse CRL file
|
apiResp.Error = fmt.Sprintln("Failed to parse or verify embedded CRL")
|
||||||
for _, c := range revInfo.CRL {
|
}
|
||||||
crl, err := x509.ParseCRL(c.FullBytes)
|
|
||||||
if err != nil {
|
if len(crl.TBSCertList.RevokedCertificates) > 0 {
|
||||||
apiResp.Error = fmt.Sprintln("Failed to parse or verify embedded CRL")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(crl.TBSCertList.RevokedCertificates) > 0 {
|
//var crlissuer *pkix.Name
|
||||||
|
//crlissuerdr.FillFromRDNSequence(&crl.TBSCertList.Issuer)
|
||||||
}
|
//apiResp.Error = fmt.Sprintf("CRL %v , with %d entries\n", crl.TBSCertList.Issuer, len(crl.TBSCertList.RevokedCertificates))
|
||||||
|
// TODO(vanbroup): Check revocation via CRL
|
||||||
//var crlissuer *pkix.Name
|
// signer.RevokedCertificate = true
|
||||||
//crlissuerdr.FillFromRDNSequence(&crl.TBSCertList.Issuer)
|
}
|
||||||
//apiResp.Error = fmt.Sprintf("CRL %v , with %d entries\n", crl.TBSCertList.Issuer, len(crl.TBSCertList.RevokedCertificates))
|
|
||||||
// TODO(vanbroup): Check revocation via CRL
|
// If SubFilter is adbe.pkcs7.detached or adbe.pkcs7.sha1, this entry
|
||||||
// signer.RevokedCertificate = true
|
// shall not be used, and the certificate chain shall be put in the PKCS#7
|
||||||
}
|
// envelope in Contents.
|
||||||
|
//v.Key("Cert").Text()
|
||||||
// If SubFilter is adbe.pkcs7.detached or adbe.pkcs7.sha1, this entry
|
|
||||||
// shall not be used, and the certificate chain shall be put in the PKCS#7
|
apiResp.Signers = append(apiResp.Signers, signer)
|
||||||
// envelope in Contents.
|
}
|
||||||
//v.Key("Cert").Text()
|
|
||||||
|
if apiResp == nil {
|
||||||
apiResp.Signers = append(apiResp.Signers, signer)
|
err = fmt.Errorf("Document looks to have a signature but got no results")
|
||||||
}
|
}
|
||||||
|
|
||||||
if apiResp == nil {
|
return
|
||||||
err = fmt.Errorf("Document looks to have a signature but got no results")
|
}
|
||||||
}
|
|
||||||
|
func walk(t pdf.Value, pad int) {
|
||||||
return
|
for _, k := range t.Keys() {
|
||||||
}
|
v := t.Key(k)
|
||||||
|
if v.Kind() == pdf.Array || v.Kind() == pdf.Dict {
|
||||||
func walk(t pdf.Value, pad int) {
|
pad++
|
||||||
for _, k := range t.Keys() {
|
walk(v, pad)
|
||||||
v := t.Key(k)
|
}
|
||||||
if v.Kind() == pdf.Array || v.Kind() == pdf.Dict {
|
}
|
||||||
pad++
|
}
|
||||||
walk(v, pad)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user