boulder/cmd/single-ocsp/main.go

184 lines
4.9 KiB
Go

// Copyright 2015 ISRG. All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package main
import (
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/codegangsta/cli"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/golang.org/x/crypto/ocsp"
)
// PKCS11Config defines how to load a module for an HSM.
// XXX(rlb@ipv.sx) Copied from certificate-authority.go
type PKCS11Config struct {
Module string
TokenLabel string
PrivateKeyLabel string
PIN string
SlotID float64
}
func readFiles(c *cli.Context) (issuer, responder, target *x509.Certificate, template ocsp.Response, pkcs11 PKCS11Config, err error) {
// Issuer certificate
issuerFileName := c.GlobalString("issuer")
issuerBytes, err := ioutil.ReadFile(issuerFileName)
if err != nil {
return
}
issuer, err = x509.ParseCertificate(issuerBytes)
if err != nil {
return
}
// Responder certificate
responderFileName := c.GlobalString("responder")
responderBytes, err := ioutil.ReadFile(responderFileName)
if err != nil {
return
}
responder, err = x509.ParseCertificate(responderBytes)
if err != nil {
return
}
// Target certificate
targetFileName := c.GlobalString("target")
targetBytes, err := ioutil.ReadFile(targetFileName)
if err != nil {
return
}
target, err = x509.ParseCertificate(targetBytes)
if err != nil {
return
}
// OCSP template
templateFileName := c.GlobalString("template")
templateBytes, err := ioutil.ReadFile(templateFileName)
if err != nil {
return
}
err = json.Unmarshal(templateBytes, &template)
if err != nil {
return
}
// PKCS#11 config
pkcs11FileName := c.GlobalString("pkcs11")
pkcs11Bytes, err := ioutil.ReadFile(pkcs11FileName)
if err != nil {
return
}
err = json.Unmarshal(pkcs11Bytes, &pkcs11)
return
}
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 = "ca-dev@letsencrypt.org"
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, int(pkcs11.SlotID))
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")
}
ioutil.WriteFile(outFile, responseBytes, 0666)
}
err := app.Run(os.Args)
cmd.FailOnError(err, "Failed to run application")
}