Fixes and support for ApprovalSignature and TimeStampSignature
This commit is contained in:
274
sign.go
274
sign.go
@@ -2,40 +2,57 @@ package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
|
||||
"github.com/digitorus/pdfsign/sign"
|
||||
"github.com/digitorus/pdfsign/verify"
|
||||
)
|
||||
|
||||
var (
|
||||
infoName, infoLocation, infoReason, infoContact, tsa string
|
||||
certType string
|
||||
)
|
||||
|
||||
func usage() {
|
||||
flag.PrintDefaults()
|
||||
fmt.Println()
|
||||
fmt.Println("Example usage:")
|
||||
fmt.Println("\nExample usage:")
|
||||
fmt.Printf("\t%s -name \"Jon Doe\" sign input.pdf output.pdf certificate.crt private_key.key [chain.crt]\n", os.Args[0])
|
||||
fmt.Printf("\t%sverify input.pdf\n", os.Args[0])
|
||||
fmt.Printf("\t%s -certType \"CertificationSignature\" -name \"Jon Doe\" sign input.pdf output.pdf certificate.crt private_key.key [chain.crt]\n", os.Args[0])
|
||||
fmt.Printf("\t%s -certType \"TimeStampSignature\" input.pdf output.pdf\n", os.Args[0])
|
||||
fmt.Printf("\t%s verify input.pdf\n", os.Args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func parseCertType(s string) (sign.CertType, error) {
|
||||
switch s {
|
||||
case sign.CertificationSignature.String():
|
||||
return sign.CertificationSignature, nil
|
||||
case sign.ApprovalSignature.String():
|
||||
return sign.ApprovalSignature, nil
|
||||
case sign.UsageRightsSignature.String():
|
||||
return sign.UsageRightsSignature, nil
|
||||
case sign.TimeStampSignature.String():
|
||||
return sign.TimeStampSignature, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid certType value")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.StringVar(&infoName, "name", "", "Name of the signatory")
|
||||
flag.StringVar(&infoLocation, "location", "", "Location of the signatory")
|
||||
flag.StringVar(&infoReason, "reason", "", "Reason for signig")
|
||||
flag.StringVar(&infoReason, "reason", "", "Reason for signing")
|
||||
flag.StringVar(&infoContact, "contact", "", "Contact information for signatory")
|
||||
flag.StringVar(&tsa, "tsa", "https://freetsa.org/tsr", "URL for Time-Stamp Authority")
|
||||
flag.StringVar(&certType, "certType", "CertificationSignature", "Type of the certificate (CertificationSignature, ApprovalSignature, UsageRightsSignature, TimeStampSignature)")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
@@ -53,114 +70,161 @@ func main() {
|
||||
usage()
|
||||
}
|
||||
|
||||
if method == "verify" {
|
||||
input_file, err := os.Open(input)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer input_file.Close()
|
||||
switch method {
|
||||
case "verify":
|
||||
verifyPDF(input)
|
||||
case "sign":
|
||||
signPDF(input)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := verify.File(input_file)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
jsonData, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(string(jsonData))
|
||||
return
|
||||
func verifyPDF(input string) {
|
||||
inputFile, err := os.Open(input)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer inputFile.Close()
|
||||
|
||||
resp, err := verify.File(inputFile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if method == "sign" {
|
||||
if len(flag.Args()) < 5 {
|
||||
usage()
|
||||
}
|
||||
jsonData, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(string(jsonData))
|
||||
}
|
||||
|
||||
func signPDF(input string) {
|
||||
certTypeValue, err := parseCertType(certType)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if certTypeValue == sign.TimeStampSignature {
|
||||
output := flag.Arg(2)
|
||||
if len(output) == 0 {
|
||||
usage()
|
||||
}
|
||||
timeStampPDF(input, output, tsa)
|
||||
return
|
||||
}
|
||||
|
||||
certificate_data, err := os.ReadFile(flag.Arg(3))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
if len(flag.Args()) < 5 {
|
||||
usage()
|
||||
}
|
||||
|
||||
}
|
||||
certificate_data_block, _ := pem.Decode(certificate_data)
|
||||
if certificate_data_block == nil {
|
||||
log.Fatal(errors.New("failed to parse PEM block containing the certificate"))
|
||||
}
|
||||
output := flag.Arg(2)
|
||||
if len(output) == 0 {
|
||||
usage()
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(certificate_data_block.Bytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cert, pkey, certificateChains := loadCertificatesAndKey(flag.Arg(3), flag.Arg(4), flag.Arg(5))
|
||||
|
||||
key_data, err := os.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.ParsePKCS1PrivateKey(key_data_block.Bytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
certificate_chains := make([][]*x509.Certificate, 0)
|
||||
|
||||
if flag.Arg(5) != "" {
|
||||
certificate_pool := x509.NewCertPool()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
chain_data, err := os.ReadFile(flag.Arg(5))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
certificate_pool.AppendCertsFromPEM(chain_data)
|
||||
certificate_chains, err = cert.Verify(x509.VerifyOptions{
|
||||
Intermediates: certificate_pool,
|
||||
CurrentTime: cert.NotBefore,
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
err = sign.SignFile(input, output, sign.SignData{
|
||||
Signature: sign.SignDataSignature{
|
||||
Info: sign.SignDataSignatureInfo{
|
||||
Name: infoName,
|
||||
Location: infoLocation,
|
||||
Reason: infoReason,
|
||||
ContactInfo: infoContact,
|
||||
Date: time.Now().Local(),
|
||||
},
|
||||
CertType: sign.CertificationSignature,
|
||||
DocMDPPerm: sign.AllowFillingExistingFormFieldsAndSignaturesPerms,
|
||||
err = sign.SignFile(input, output, sign.SignData{
|
||||
Signature: sign.SignDataSignature{
|
||||
Info: sign.SignDataSignatureInfo{
|
||||
Name: infoName,
|
||||
Location: infoLocation,
|
||||
Reason: infoReason,
|
||||
ContactInfo: infoContact,
|
||||
Date: time.Now().Local(),
|
||||
},
|
||||
Signer: pkey,
|
||||
DigestAlgorithm: crypto.SHA256,
|
||||
Certificate: cert,
|
||||
CertificateChains: certificate_chains,
|
||||
TSA: sign.TSA{
|
||||
URL: tsa,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Println("Signed PDF written to " + output)
|
||||
}
|
||||
CertType: certTypeValue,
|
||||
DocMDPPerm: sign.AllowFillingExistingFormFieldsAndSignaturesPerms,
|
||||
},
|
||||
Signer: pkey,
|
||||
DigestAlgorithm: crypto.SHA256,
|
||||
Certificate: cert,
|
||||
CertificateChains: certificateChains,
|
||||
TSA: sign.TSA{
|
||||
URL: tsa,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Println("Signed PDF written to " + output)
|
||||
}
|
||||
}
|
||||
|
||||
func loadCertificatesAndKey(certPath, keyPath, chainPath string) (*x509.Certificate, crypto.Signer, [][]*x509.Certificate) {
|
||||
certData, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
certBlock, _ := pem.Decode(certData)
|
||||
if certBlock == nil {
|
||||
log.Fatal(errors.New("failed to parse PEM block containing the certificate"))
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
keyData, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
keyBlock, _ := pem.Decode(keyData)
|
||||
if keyBlock == nil {
|
||||
log.Fatal(errors.New("failed to parse PEM block containing the private key"))
|
||||
}
|
||||
|
||||
pkey, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var certificateChains [][]*x509.Certificate
|
||||
if chainPath != "" {
|
||||
certificateChains = loadCertificateChain(chainPath, cert)
|
||||
}
|
||||
|
||||
return cert, pkey, certificateChains
|
||||
}
|
||||
|
||||
func loadCertificateChain(chainPath string, cert *x509.Certificate) [][]*x509.Certificate {
|
||||
chainData, err := os.ReadFile(chainPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
certificatePool := x509.NewCertPool()
|
||||
certificatePool.AppendCertsFromPEM(chainData)
|
||||
|
||||
certificateChains, err := cert.Verify(x509.VerifyOptions{
|
||||
Intermediates: certificatePool,
|
||||
CurrentTime: cert.NotBefore,
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return certificateChains
|
||||
}
|
||||
|
||||
func timeStampPDF(input, output, tsa string) {
|
||||
err := sign.SignFile(input, output, sign.SignData{
|
||||
Signature: sign.SignDataSignature{
|
||||
CertType: sign.TimeStampSignature,
|
||||
},
|
||||
DigestAlgorithm: crypto.SHA256,
|
||||
TSA: sign.TSA{
|
||||
URL: tsa,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Println("Signed PDF written to " + output)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user