diff --git a/sign.go b/sign.go index 0980cfc..5e50cb2 100644 --- a/sign.go +++ b/sign.go @@ -4,12 +4,22 @@ import ( "flag" "log" "os" + "time" + + "crypto/x509" + "encoding/pem" + "errors" + "io/ioutil" "fmt" + "crypto" + "bitbucket.org/digitorus/pdfsign/config" + "bitbucket.org/digitorus/pdfsign/revocation" "bitbucket.org/digitorus/pdfsign/sign" "bitbucket.org/digitorus/pdfsign/verify" + "bitbucket.org/digitorus/pkcs11" ) // usage is a usage function for the flags package. @@ -124,18 +134,64 @@ Description os.Exit(2) } - s, err := newSigner(*crtPath, *keyPath, *crtChainPath) + cert, signer, err := getCertSignerPair(*crtPath, *keyPath) if err != nil { log.Fatal(err) } - if err := s.sign(*inputPath, *outputPath, signData); err != nil { + certificate_chains, err := getCertificateChains(*crtChainPath, cert) + if err != nil { + log.Fatal(err) + } + + signData.Signer = signer + signData.Certificate = cert + signData.CertificateChains = certificate_chains + if err := signWithConfig(*inputPath, *outputPath, signData); err != nil { log.Println(err) } else { log.Println("Signed PDF written to " + *outputPath) } } +func getCertSignerPair(crtPath, keyPath string) (*x509.Certificate, crypto.Signer, error) { + var certificate *x509.Certificate + var signer crypto.Signer + + // Set certificate + certificate_data, err := ioutil.ReadFile(crtPath) + if err != nil { + return certificate, signer, err + log.Fatal(err) + } + certificate_data_block, _ := pem.Decode(certificate_data) + if certificate_data_block == nil { + return certificate, signer, errors.New("failed to parse PEM block containing the certificate") + } + cert, err := x509.ParseCertificate(certificate_data_block.Bytes) + if err != nil { + return certificate, signer, err + } + certificate = cert + + // Set key + key_data, err := ioutil.ReadFile(keyPath) + if err != nil { + return certificate, signer, err + } + key_data_block, _ := pem.Decode(key_data) + if key_data_block == nil { + return certificate, signer, errors.New("failed to parse PEM block containing the private key") + } + pkey, err := x509.ParsePKCS1PrivateKey(key_data_block.Bytes) + if err != nil { + return certificate, signer, err + } + signer = pkey + + return certificate, signer, nil +} + func p11sign() { signCommand := flag.NewFlagSet("sign", flag.ExitOnError) configPath := signCommand.String("config", "", "Path to config file") @@ -148,9 +204,9 @@ func p11sign() { output := signCommand.String("out", "", "Output PDF file") libPath := signCommand.String("lib", "", "Path to PKCS11 library") pass := signCommand.String("pass", "", "PKCS11 password") - crtChainPath := signCommand.String("chain", "", "Certificate chain") + crtChain := signCommand.String("chain", "", "Certificate chain") help := signCommand.Bool("help", false, "Show this help") - signData := getSignDataFlags(signCommand) + signCommand.Parse(os.Args[2:]) usageText := `Usage: pdfsign sign -in input.pdf -out output.pdf -pass pkcs11-password [-chain chain.crt]") @@ -169,14 +225,108 @@ Description os.Exit(2) } - s, err := newP11Signer(*libPath, *pass, *crtChainPath) + cert, signer, err := getP11CertSignerPair(*libPath, *pass) if err != nil { log.Fatal(err) } - if err := s.sign(*input, *output, signData); err != nil { + certificate_chains, err := getCertificateChains(*crtChain, cert) + if err != nil { + log.Fatal(err) + } + + var data sign.SignData + data.Certificate = cert + data.Signer = signer + data.CertificateChains = certificate_chains + if err := signWithConfig(*input, *output, data); err != nil { log.Println(err) } else { log.Println("Signed PDF written to " + *output) } } + +func getP11CertSignerPair(libPath, pass string) (*x509.Certificate, crypto.Signer, error) { + var certificate *x509.Certificate + var signer crypto.Signer + + // pkcs11 key + lib, err := pkcs11.FindLib(libPath) + if err != nil { + return certificate, signer, err + } + + // Load Library + ctx := pkcs11.New(lib) + if ctx == nil { + return certificate, signer, errors.New("Failed to load library") + } + err = ctx.Initialize() + if err != nil { + return certificate, signer, err + } + // login + session, err := pkcs11.CreateSession(ctx, 0, pass, false) + if err != nil { + return certificate, signer, err + } + // select the first certificate + cert, ckaId, err := pkcs11.GetCert(ctx, session, nil) + if err != nil { + return certificate, signer, err + } + certificate = cert + + // private key + pkey, err := pkcs11.InitPrivateKey(ctx, session, ckaId) + if err != nil { + return certificate, signer, err + } + signer = pkey + + return certificate, signer, nil +} + +func getCertificateChains(crtChain string, cert *x509.Certificate) ([][]*x509.Certificate, error) { + certificate_chains := make([][]*x509.Certificate, 0) + if crtChain == "" { + return certificate_chains, nil + } + + chain_data, err := ioutil.ReadFile(crtChain) + if err != nil { + log.Fatal(err) + } + certificate_pool := x509.NewCertPool() + certificate_pool.AppendCertsFromPEM(chain_data) + certificate_chains, err = cert.Verify(x509.VerifyOptions{ + Intermediates: certificate_pool, + CurrentTime: cert.NotBefore, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + }) + + return certificate_chains, err +} + +func signWithConfig(input, output string, d sign.SignData) error { + err := sign.SignFile(input, output, sign.SignData{ + Signature: sign.SignDataSignature{ + Info: sign.SignDataSignatureInfo{ + Name: d.Signature.Info.Name, + Location: d.Signature.Info.Location, + Reason: d.Signature.Info.Reason, + ContactInfo: d.Signature.Info.ContactInfo, + Date: time.Now().Local(), + }, + CertType: d.Signature.CertType, + Approval: d.Signature.Approval, + }, + Signer: d.Signer, + Certificate: d.Certificate, + CertificateChains: d.CertificateChains, + TSA: d.TSA, + RevocationData: revocation.InfoArchival{}, + RevocationFunction: sign.DefaultEmbedRevocationStatusFunction, + }) + return err +} diff --git a/signer.go b/signer.go deleted file mode 100644 index abc75b9..0000000 --- a/signer.go +++ /dev/null @@ -1,154 +0,0 @@ -package main - -import ( - "crypto" - "crypto/x509" - "encoding/pem" - "errors" - "io/ioutil" - "log" - "time" - - "bitbucket.org/digitorus/pdfsign/revocation" - "bitbucket.org/digitorus/pdfsign/sign" - "bitbucket.org/digitorus/pkcs11" -) - -type signer struct { - certificate *x509.Certificate - signer crypto.Signer - certificateChains [][]*x509.Certificate -} - -func newSigner(crtPath, keyPath, crtChainPath string) (*signer, error) { - var s signer - - // Set certificate - certificate_data, err := ioutil.ReadFile(crtPath) - if err != nil { - return &s, err - log.Fatal(err) - } - certificate_data_block, _ := pem.Decode(certificate_data) - if certificate_data_block == nil { - return &s, errors.New("failed to parse PEM block containing the certificate") - } - cert, err := x509.ParseCertificate(certificate_data_block.Bytes) - if err != nil { - return &s, err - } - s.certificate = cert - - // Set key - key_data, err := ioutil.ReadFile(keyPath) - if err != nil { - return &s, err - } - key_data_block, _ := pem.Decode(key_data) - if key_data_block == nil { - return &s, errors.New("failed to parse PEM block containing the private key") - } - pkey, err := x509.ParsePKCS1PrivateKey(key_data_block.Bytes) - if err != nil { - return &s, err - } - s.signer = pkey - - certificate_chains, err := getCertificateChains(crtChainPath, cert) - if err != nil { - return &s, err - } - s.certificateChains = certificate_chains - - return &s, nil -} - -func newP11Signer(libPath, pass, crtChainPath string) (*signer, error) { - var s signer - - // pkcs11 key - lib, err := pkcs11.FindLib(libPath) - if err != nil { - return &s, err - } - - // Load Library - ctx := pkcs11.New(lib) - if ctx == nil { - return &s, errors.New("Failed to load library") - } - err = ctx.Initialize() - if err != nil { - return &s, err - } - // login - session, err := pkcs11.CreateSession(ctx, 0, pass, false) - if err != nil { - return &s, err - } - // select the first certificate - cert, ckaId, err := pkcs11.GetCert(ctx, session, nil) - if err != nil { - return &s, err - } - s.certificate = cert - - // private key - pkey, err := pkcs11.InitPrivateKey(ctx, session, ckaId) - if err != nil { - return &s, err - } - s.signer = pkey - - certificate_chains, err := getCertificateChains(crtChainPath, cert) - if err != nil { - return &s, err - } - s.certificateChains = certificate_chains - - return &s, nil -} - -func getCertificateChains(crtChainPath string, cert *x509.Certificate) ([][]*x509.Certificate, error) { - certificate_chains := make([][]*x509.Certificate, 0) - if crtChainPath == "" { - return certificate_chains, nil - } - - chain_data, err := ioutil.ReadFile(crtChainPath) - if err != nil { - log.Fatal(err) - } - certificate_pool := x509.NewCertPool() - certificate_pool.AppendCertsFromPEM(chain_data) - certificate_chains, err = cert.Verify(x509.VerifyOptions{ - Intermediates: certificate_pool, - CurrentTime: cert.NotBefore, - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, - }) - - return certificate_chains, err -} - -func (s *signer) sign(input, output string, d sign.SignData) error { - err := sign.SignFile(input, output, sign.SignData{ - Signature: sign.SignDataSignature{ - Info: sign.SignDataSignatureInfo{ - Name: d.Signature.Info.Name, - Location: d.Signature.Info.Location, - Reason: d.Signature.Info.Reason, - ContactInfo: d.Signature.Info.ContactInfo, - Date: time.Now().Local(), - }, - CertType: d.Signature.CertType, - Approval: d.Signature.Approval, - }, - Signer: s.signer, - Certificate: s.certificate, - CertificateChains: s.certificateChains, - TSA: d.TSA, - RevocationData: revocation.InfoArchival{}, - RevocationFunction: sign.DefaultEmbedRevocationStatusFunction, - }) - return err -}