Switched module path and imports to gitea.tryanks.com. Removed GitHub-specific files like workflows, dependabot, and devcontainer as part of migration. This streamlines the codebase for the new hosting environment.
231 lines
5.3 KiB
Go
231 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"time"
|
|
|
|
"gitea.tryanks.com/Tryanks/pdfsign/sign"
|
|
"gitea.tryanks.com/Tryanks/pdfsign/verify"
|
|
)
|
|
|
|
var (
|
|
infoName, infoLocation, infoReason, infoContact, tsa string
|
|
certType string
|
|
)
|
|
|
|
func usage() {
|
|
flag.PrintDefaults()
|
|
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%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 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()
|
|
|
|
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()
|
|
}
|
|
|
|
switch method {
|
|
case "verify":
|
|
verifyPDF(input)
|
|
case "sign":
|
|
signPDF(input)
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
if len(flag.Args()) < 5 {
|
|
usage()
|
|
}
|
|
|
|
output := flag.Arg(2)
|
|
if len(output) == 0 {
|
|
usage()
|
|
}
|
|
|
|
cert, pkey, certificateChains := loadCertificatesAndKey(flag.Arg(3), flag.Arg(4), flag.Arg(5))
|
|
|
|
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: 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)
|
|
}
|
|
}
|