@@ -2,26 +2,21 @@ package main
22
33import (
44 "crypto/x509"
5+ "encoding/base64"
56 "encoding/json"
67 "flag"
78 "fmt"
89 "io/ioutil"
10+ "os"
11+ "time"
912
1013 "github.com/letsencrypt/boulder/cmd"
14+ "github.com/letsencrypt/boulder/core"
1115
1216 "github.com/letsencrypt/pkcs11key"
1317 "golang.org/x/crypto/ocsp"
1418)
1519
16- // PKCS11Config defines how to load a module for an HSM.
17- // XXX(rlb@ipv.sx) Copied from certificate-authority.go
18- type PKCS11Config struct {
19- Module string
20- TokenLabel string
21- PrivateKeyLabel string
22- PIN string
23- }
24-
2520const usage = `
2621name:
2722 single-ocsp - Creates a single OCSP response
@@ -31,82 +26,45 @@ usage:
3126
3227description:
3328 According to the BRs, the OCSP responses for intermediate certificate must
34- be issued once per year. So there's a need to issue OCSP responses for
35- these certificates, but it doesn't make sense to use all the infrastructure
29+ be issued once per year. So we need to issue OCSP responses for these
30+ certificates, but it doesn't make sense to use all the infrastructure
3631 that the "ocsp-updater" tool requires. This tool allows an administrator
3732 to manually generate an OCSP response for an intermediate certificate.
38- `
3933
40- const templateUsage = `
41- OCSP template file (JSON), e.g.:
42-
43- {
44- "Status": 0, // Good
45- "ThisUpdate": "2015-08-26T00:00:00Z",
46- "NextUpdate": "2016-08-26T00:00:00Z"
47- }
48-
49- {
50- "Status": 1, // Revoked
51- "ThisUpdate": "2015-08-26T00:00:00Z",
52- "NextUpdate": "2016-08-26T00:00:00Z",
53- "RevokedAt": "2015-08-20T00:00:00Z",
54- "RevocationReason": 1 // Key compromise
55- }
34+ This will write a base64-encoded DER format OCSP response to the file
35+ specified with -out, ending in a newline. This output can be directly
36+ appended to the flat file used by ocsp-responder for intermediate and
37+ root OCSP responses.
5638`
5739
5840const pkcs11Usage = `
5941PKCS#11 configuration (JSON), e.g.:
6042
6143{
62- "Module ": "/Library/OpenSC /lib/opensc-pkcs11 .so",
63- "Token ": "Yubico Yubikey NEO CCID ",
64- "Label ": "PIV AUTH key ",
65- "PIN ": "123456 "
44+ "module ": "/usr/local /lib/libpkcs11-proxy .so",
45+ "tokenLabel ": "intermediate ",
46+ "pin ": "5678 ",
47+ "privateKeyLabel ": "intermediate_key "
6648}
49+
50+ Note: These values should *not* be the same as the ones in the CA's config JSON, which point at a differen HSM partition.
6751`
6852
69- func readFiles (issuerFileName , responderFileName , targetFileName , templateFileName , pkcs11FileName string ) (issuer , responder , target * x509.Certificate , template ocsp. Response , pkcs11 PKCS11Config , err error ) {
53+ func readFiles (issuerFileName , responderFileName , targetFileName , pkcs11FileName string ) (issuer , responder , target * x509.Certificate , pkcs11Config pkcs11key. Config , err error ) {
7054 // Issuer certificate
71- issuerBytes , err := ioutil .ReadFile (issuerFileName )
72- if err != nil {
73- return
74- }
75-
76- issuer , err = x509 .ParseCertificate (issuerBytes )
55+ issuer , err = core .LoadCert (issuerFileName )
7756 if err != nil {
7857 return
7958 }
8059
8160 // Responder certificate
82- responderBytes , err := ioutil .ReadFile (responderFileName )
83- if err != nil {
84- return
85- }
86-
87- responder , err = x509 .ParseCertificate (responderBytes )
61+ responder , err = core .LoadCert (responderFileName )
8862 if err != nil {
8963 return
9064 }
9165
9266 // Target certificate
93- targetBytes , err := ioutil .ReadFile (targetFileName )
94- if err != nil {
95- return
96- }
97-
98- target , err = x509 .ParseCertificate (targetBytes )
99- if err != nil {
100- return
101- }
102-
103- // OCSP template
104- templateBytes , err := ioutil .ReadFile (templateFileName )
105- if err != nil {
106- return
107- }
108-
109- err = json .Unmarshal (templateBytes , & template )
67+ target , err = core .LoadCert (targetFileName )
11068 if err != nil {
11169 return
11270 }
@@ -117,38 +75,71 @@ func readFiles(issuerFileName, responderFileName, targetFileName, templateFileNa
11775 return
11876 }
11977
120- err = json .Unmarshal (pkcs11Bytes , & pkcs11 )
78+ err = json .Unmarshal (pkcs11Bytes , & pkcs11Config )
79+ if pkcs11Config .Module == "" ||
80+ pkcs11Config .TokenLabel == "" ||
81+ pkcs11Config .PIN == "" ||
82+ pkcs11Config .PrivateKeyLabel == "" {
83+ err = fmt .Errorf ("Missing a field in pkcs11Config %#v" , pkcs11Config )
84+ return
85+ }
12186 return
12287}
12388
12489func main () {
125- issuerFile := flag .String ("issuer" , "" , "Issuer certificate (DER )" )
90+ issuerFile := flag .String ("issuer" , "" , "Issuer certificate (PEM )" )
12691 responderFile := flag .String ("responder" , "" , "OCSP responder certificate (DER)" )
127- targetFile := flag .String ("target" , "" , "Certificate whose status is being reported (DER)" )
128- templateFile := flag .String ("template" , "" , templateUsage )
92+ targetFile := flag .String ("target" , "" , "Certificate whose status is being reported (PEM)" )
12993 pkcs11File := flag .String ("pkcs11" , "" , pkcs11Usage )
13094 outFile := flag .String ("out" , "" , "File to which the OCSP response will be written" )
95+ thisUpdateString := flag .String ("thisUpdate" , "" , "Time for ThisUpdate field, RFC3339 format (e.g. 2016-09-02T00:00:00Z)" )
96+ nextUpdateString := flag .String ("nextUpdate" , "" , "Time for NextUpdate field, RFC3339 format" )
97+ status := flag .Int ("status" , 0 , "Status for response (0 = good, 1 = revoked)" )
98+ flag .Usage = func () {
99+ fmt .Fprint (os .Stderr , usage )
100+ flag .PrintDefaults ()
101+ }
131102 flag .Parse ()
132103
133- issuer , responder , target , template , pkcs11 , err := readFiles (* issuerFile , * responderFile , * targetFile , * templateFile , * pkcs11File )
104+ if len (* outFile ) == 0 {
105+ cmd .FailOnError (fmt .Errorf ("No output file provided" ), "" )
106+ }
107+ thisUpdate , err := time .Parse (time .RFC3339 , * thisUpdateString )
108+ cmd .FailOnError (err , "Parsing thisUpdate flag" )
109+ nextUpdate , err := time .Parse (time .RFC3339 , * nextUpdateString )
110+ cmd .FailOnError (err , "Parsing nextUpdate flag" )
111+
112+ issuer , responder , target , pkcs11 , err := readFiles (* issuerFile , * responderFile , * targetFile , * pkcs11File )
134113 cmd .FailOnError (err , "Failed to read files" )
135114
136115 // Instantiate the private key from PKCS11
137116 priv , err := pkcs11key .New (pkcs11 .Module , pkcs11 .TokenLabel , pkcs11 .PIN , pkcs11 .PrivateKeyLabel )
138117 cmd .FailOnError (err , "Failed to load PKCS#11 key" )
139118
140119 // Populate the remaining fields in the template
141- template .SerialNumber = target .SerialNumber
142- template .Certificate = responder
120+ template := ocsp.Response {
121+ SerialNumber : target .SerialNumber ,
122+ Certificate : responder ,
123+ Status : * status ,
124+ ThisUpdate : thisUpdate ,
125+ NextUpdate : nextUpdate ,
126+ }
127+
128+ if ! core .KeyDigestEquals (responder .PublicKey , priv .Public ()) {
129+ cmd .FailOnError (fmt .Errorf ("PKCS#11 pubkey does not match pubkey " +
130+ "in responder certificate" ), "loading keys" )
131+ }
143132
144133 // Sign the OCSP response
145134 responseBytes , err := ocsp .CreateResponse (issuer , responder , template , priv )
146135 cmd .FailOnError (err , "Failed to sign OCSP response" )
147136
137+ _ , err = ocsp .ParseResponse (responseBytes , nil )
138+ cmd .FailOnError (err , "Failed to parse signed response" )
139+
140+ responseBytesBase64 := base64 .StdEncoding .EncodeToString (responseBytes ) + "\n "
141+
148142 // Write the OCSP response to stdout
149- if len (* outFile ) == 0 {
150- cmd .FailOnError (fmt .Errorf ("" ), "No output file provided" )
151- }
152- err = ioutil .WriteFile (* outFile , responseBytes , 0666 )
143+ err = ioutil .WriteFile (* outFile , []byte (responseBytesBase64 ), 0666 )
153144 cmd .FailOnError (err , "Failed to write output file" )
154145}
0 commit comments