func main() { issuerFile := flag.String("issuer", "", "Issuer certificate (PEM)") responderFile := flag.String("responder", "", "OCSP responder certificate (DER)") targetFile := flag.String("target", "", "Certificate whose status is being reported (PEM)") pkcs11File := flag.String("pkcs11", "", pkcs11Usage) outFile := flag.String("out", "", "File to which the OCSP response will be written") thisUpdateString := flag.String("thisUpdate", "", "Time for ThisUpdate field, RFC3339 format (e.g. 2016-09-02T00:00:00Z)") nextUpdateString := flag.String("nextUpdate", "", "Time for NextUpdate field, RFC3339 format") status := flag.Int("status", 0, "Status for response (0 = good, 1 = revoked)") flag.Usage = func() { fmt.Fprint(os.Stderr, usage) flag.PrintDefaults() } flag.Parse() if len(*outFile) == 0 { cmd.FailOnError(fmt.Errorf("No output file provided"), "") } thisUpdate, err := time.Parse(time.RFC3339, *thisUpdateString) cmd.FailOnError(err, "Parsing thisUpdate flag") nextUpdate, err := time.Parse(time.RFC3339, *nextUpdateString) cmd.FailOnError(err, "Parsing nextUpdate flag") issuer, responder, target, pkcs11, err := readFiles(*issuerFile, *responderFile, *targetFile, *pkcs11File) cmd.FailOnError(err, "Failed to read files") // Instantiate the private key from PKCS11 priv, err := pkcs11key.New(pkcs11.Module, pkcs11.TokenLabel, pkcs11.PIN, pkcs11.PrivateKeyLabel) cmd.FailOnError(err, "Failed to load PKCS#11 key") // Populate the remaining fields in the template template := ocsp.Response{ SerialNumber: target.SerialNumber, Certificate: responder, Status: *status, ThisUpdate: thisUpdate, NextUpdate: nextUpdate, } if !core.KeyDigestEquals(responder.PublicKey, priv.Public()) { cmd.FailOnError(fmt.Errorf("PKCS#11 pubkey does not match pubkey "+ "in responder certificate"), "loading keys") } // Sign the OCSP response responseBytes, err := ocsp.CreateResponse(issuer, responder, template, priv) cmd.FailOnError(err, "Failed to sign OCSP response") _, err = ocsp.ParseResponse(responseBytes, nil) cmd.FailOnError(err, "Failed to parse signed response") responseBytesBase64 := base64.StdEncoding.EncodeToString(responseBytes) + "\n" // Write the OCSP response to stdout err = ioutil.WriteFile(*outFile, []byte(responseBytesBase64), 0666) cmd.FailOnError(err, "Failed to write output file") }
// Sign is used with an OCSP signer to request the issuance of // an OCSP response. func (s StandardSigner) Sign(req SignRequest) ([]byte, error) { if req.Certificate == nil { return nil, cferr.New(cferr.OCSPError, cferr.ReadFailed) } // Verify that req.Certificate is issued under s.issuer if bytes.Compare(req.Certificate.RawIssuer, s.issuer.RawSubject) != 0 { return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch) } if req.Certificate.CheckSignatureFrom(s.issuer) != nil { return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch) } // Round thisUpdate times down to the nearest hour thisUpdate := time.Now().Truncate(time.Hour) nextUpdate := thisUpdate.Add(s.interval) status, ok := StatusCode[req.Status] if !ok { return nil, cferr.New(cferr.OCSPError, cferr.InvalidStatus) } // If the OCSP responder is the same as the issuer, there is no need to // include any certificate in the OCSP response, which decreases the byte size // of OCSP responses dramatically. certificate := s.responder if s.issuer == s.responder || bytes.Equal(s.issuer.Raw, s.responder.Raw) { certificate = nil } template := ocsp.Response{ Status: status, SerialNumber: req.Certificate.SerialNumber, ThisUpdate: thisUpdate, NextUpdate: nextUpdate, Certificate: certificate, ExtraExtensions: req.Extensions, IssuerHash: req.IssuerHash, } if status == ocsp.Revoked { template.RevokedAt = req.RevokedAt template.RevocationReason = req.Reason } return ocsp.CreateResponse(s.issuer, s.responder, template, s.key) }
// Sign is used with an OCSP signer to request the issuance of // an OCSP response. func (s StandardSigner) Sign(req SignRequest) ([]byte, error) { if req.Certificate == nil { return nil, cferr.New(cferr.OCSPError, cferr.ReadFailed) } // Verify that req.Certificate is issued under s.issuer if bytes.Compare(req.Certificate.RawIssuer, s.issuer.RawSubject) != 0 { return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch) } if req.Certificate.CheckSignatureFrom(s.issuer) != nil { return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch) } // Round thisUpdate times down to the nearest hour thisUpdate := time.Now().Truncate(time.Hour) nextUpdate := thisUpdate.Add(s.interval) status, ok := statusCode[req.Status] if !ok { return nil, cferr.New(cferr.OCSPError, cferr.InvalidStatus) } template := ocsp.Response{ Status: status, SerialNumber: req.Certificate.SerialNumber, ThisUpdate: thisUpdate, NextUpdate: nextUpdate, Certificate: s.responder, } if status == ocsp.Revoked { template.RevokedAt = req.RevokedAt template.RevocationReason = req.Reason } return ocsp.CreateResponse(s.issuer, s.responder, template, s.key) }
func main() { app := cli.NewApp() app.Name = "single-ocsp" app.Usage = `Creates a single OCSP response. According to the BRs, the OCSP responses for intermediate certificate must be issued once per year. So there's a need to issue OCSP responses for these certificates, but it doesn't make sense to use all the infrastructure that the "ocsp-updater" tool requires. This tool allows an administrator to manually generate an OCSP response for an intermediate certificate. ` app.Version = cmd.Version() app.Author = "Boulder contributors" app.Email = "*****@*****.**" app.Flags = []cli.Flag{ cli.StringFlag{ Name: "issuer", Usage: "Issuer certificate (DER)", }, cli.StringFlag{ Name: "responder", Usage: "OCSP responder certificate (DER)", }, cli.StringFlag{ Name: "target", Usage: "Certificate whose status is being reported (DER)", }, cli.StringFlag{ Name: "template", Usage: `OCSP template file (JSON), e.g.: { "Status": 0, // Good "ThisUpdate": "2015-08-26T00:00:00Z", "NextUpdate": "2016-08-26T00:00:00Z" } { "Status": 1, // Revoked "ThisUpdate": "2015-08-26T00:00:00Z", "NextUpdate": "2016-08-26T00:00:00Z", "RevokedAt": "2015-08-20T00:00:00Z", "RevocationReason": 1 // Key compromise } `, }, cli.StringFlag{ Name: "pkcs11", Usage: `PKCS#11 configuration (JSON), e.g.: { "Module": "/Library/OpenSC/lib/opensc-pkcs11.so", "Token": "Yubico Yubikey NEO CCID", "Label": "PIV AUTH key", "PIN": "123456" } `, }, cli.StringFlag{ Name: "out", Usage: "File to which the OCSP response will be written", }, } app.Action = func(c *cli.Context) { issuer, responder, target, template, pkcs11, err := readFiles(c) cmd.FailOnError(err, "Failed to read files") // Instantiate the private key from PKCS11 priv, err := pkcs11key.New(pkcs11.Module, pkcs11.TokenLabel, pkcs11.PIN, pkcs11.PrivateKeyLabel) cmd.FailOnError(err, "Failed to load PKCS#11 key") // Populate the remaining fields in the template template.SerialNumber = target.SerialNumber template.Certificate = responder // Sign the OCSP response responseBytes, err := ocsp.CreateResponse(issuer, responder, template, priv) cmd.FailOnError(err, "Failed to sign OCSP response") // Write the OCSP response to stdout outFile := c.GlobalString("out") if len(outFile) == 0 { cmd.FailOnError(fmt.Errorf(""), "No output file provided") } err = ioutil.WriteFile(outFile, responseBytes, 0666) cmd.FailOnError(err, "Failed to write output file") } err := app.Run(os.Args) cmd.FailOnError(err, "Failed to run application") }