Update cfssl to latest master.

This pulls in the pkcs11key change from
https://github.com/cloudflare/cfssl/pull/330, and updates the Boulder code to
match.

Note: This change overwrites the local changes to our vendored CFSSL made in
https://github.com/letsencrypt/boulder/pull/784. That's intentional: The
upstream changes in https://github.com/cloudflare/cfssl/pull/330 accomplish the
same thing, more cleanly.
This commit is contained in:
Jacob Hoffman-Andrews 2015-09-20 20:27:13 -07:00
parent e201632f60
commit d05b9b833f
29 changed files with 998 additions and 565 deletions

36
Godeps/Godeps.json generated
View File

@ -12,51 +12,63 @@
},
{
"ImportPath": "github.com/cloudflare/cfssl/auth",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/config",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs11key",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs12",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs7",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/csr",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/errors",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/helpers",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/info",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/log",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/ocsp",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/cloudflare/cfssl/signer",
"Rev": "190c5f9713ef6c1460fb31ee785044b43bdb1b09"
"Comment": "1.1.0-56-gfed4546",
"Rev": "fed4546bc16a51b87ea8b18df7c26090c4c5b900"
},
{
"ImportPath": "github.com/codegangsta/cli",

View File

@ -0,0 +1,10 @@
package pkcs11key
// Config contains configuration information required to use a PKCS
// #11 key.
type Config struct {
Module string
TokenLabel string
PIN string
PrivateKeyLabel string
}

View File

@ -0,0 +1,353 @@
// +build !nopkcs11
// Package pkcs11key implements crypto.Signer for PKCS #11 private
// keys. Currently, only RSA keys are supported.
// See ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-11/v2-30/pkcs-11v2-30b-d6.pdf for
// details of the Cryptoki PKCS#11 API.
package pkcs11key
import (
"crypto"
"crypto/rsa"
"errors"
"fmt"
"io"
"math/big"
"sync"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/pkcs11"
)
// from src/pkg/crypto/rsa/pkcs1v15.go
var hashPrefixes = map[crypto.Hash][]byte{
crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
crypto.MD5SHA1: {}, // A special TLS case which doesn't use an ASN1 prefix.
crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14},
}
// Key is an implementation of the crypto.Signer interface using a key stored
// in a PKCS#11 hardware token. This enables the use of PKCS#11 tokens with
// the Go x509 library's methods for signing certificates.
//
// Each Key represents one session. Its session handle is protected internally
// by a mutex, so at most one Sign operation can be active at a time. For best
// performance you may want to instantiate multiple Keys using pkcs11key.Pool.
// Each one will have its own session and can be used concurrently. Note that
// some smartcards like the Yubikey Neo do not support multiple simultaneous
// sessions and will error out on creation of the second Key object.
//
// Note: If you instantiate multiple Keys without using Pool, it is *highly*
// recommended that you create all your Key objects serially, on your main
// thread, checking for errors each time, and then farm them out for use by
// different goroutines. If you fail to do this, your application may attempt
// to login repeatedly with an incorrect PIN, locking the PKCS#11 token.
type Key struct {
// The PKCS#11 library to use
module *pkcs11.Ctx
// The path to the PKCS#11 library
modulePath string
// The label of the token to be used (mandatory).
// We will automatically search for this in the slot list.
tokenLabel string
// The PIN to be used to log in to the device
pin string
// The public key corresponding to the private key.
publicKey rsa.PublicKey
// The an ObjectHandle pointing to the private key on the HSM.
privateKeyHandle pkcs11.ObjectHandle
// A handle to the session used by this Key.
session *pkcs11.SessionHandle
sessionMu sync.Mutex
// True if the private key has the CKA_ALWAYS_AUTHENTICATE attribute set.
alwaysAuthenticate bool
}
var modules = make(map[string]*pkcs11.Ctx)
var modulesMu sync.Mutex
// initialize loads the given PKCS#11 module (shared library) if it is not
// already loaded. It's an error to load a PKCS#11 module multiple times, so we
// maintain a map of loaded modules. Note that there is no facility yet to
// unload a module ("finalize" in PKCS#11 parlance). In general, modules will
// be unloaded at the end of the process. The only place where you are likely
// to need to explicitly unload a module is if you fork your process after a
// Key has already been created, and the child process also needs to use
// that module.
func initialize(modulePath string) (*pkcs11.Ctx, error) {
modulesMu.Lock()
defer modulesMu.Unlock()
module, ok := modules[modulePath]
if ok {
return module, nil
}
module = pkcs11.New(modulePath)
if module == nil {
return nil, fmt.Errorf("unable to load PKCS#11 module")
}
err := module.Initialize()
if err != nil {
return nil, err
}
modules[modulePath] = module
return module, nil
}
// New instantiates a new handle to a PKCS #11-backed key.
func New(modulePath, tokenLabel, pin, privateKeyLabel string) (ps *Key, err error) {
module, err := initialize(modulePath)
if err != nil {
return
}
if module == nil {
err = fmt.Errorf("nil module")
return
}
// Initialize a partial key
ps = &Key{
module: module,
modulePath: modulePath,
tokenLabel: tokenLabel,
pin: pin,
}
// Open a session
ps.sessionMu.Lock()
defer ps.sessionMu.Unlock()
session, err := ps.openSession()
if err != nil {
return
}
ps.session = &session
// Fetch the private key by its label
privateKeyHandle, err := ps.getPrivateKey(module, session, privateKeyLabel)
if err != nil {
ps.module.CloseSession(session)
return
}
ps.privateKeyHandle = privateKeyHandle
publicKey, err := getPublicKey(module, session, privateKeyHandle)
if err != nil {
ps.module.CloseSession(session)
return
}
ps.publicKey = publicKey
return
}
func (ps *Key) getPrivateKey(module *pkcs11.Ctx, session pkcs11.SessionHandle, label string) (pkcs11.ObjectHandle, error) {
var noHandle pkcs11.ObjectHandle
template := []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY),
pkcs11.NewAttribute(pkcs11.CKA_LABEL, label),
}
if err := module.FindObjectsInit(session, template); err != nil {
return noHandle, err
}
objs, _, err := module.FindObjects(session, 2)
if err != nil {
return noHandle, err
}
if err = module.FindObjectsFinal(session); err != nil {
return noHandle, err
}
if len(objs) == 0 {
return noHandle, fmt.Errorf("private key not found")
}
privateKeyHandle := objs[0]
// Check whether the key has the CKA_ALWAYS_AUTHENTICATE attribute.
// If so, fail: we don't want to have to re-authenticate for each sign
// operation.
attributes, err := module.GetAttributeValue(session, privateKeyHandle, []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_ALWAYS_AUTHENTICATE, false),
})
if err != nil {
return noHandle, err
}
for _, attribute := range attributes {
if len(attribute.Value) > 0 && attribute.Value[0] == 1 {
ps.alwaysAuthenticate = true
}
}
return privateKeyHandle, nil
}
// Get the public key matching a private key
// TODO: Add support for non-RSA keys, switching on CKA_KEY_TYPE
func getPublicKey(module *pkcs11.Ctx, session pkcs11.SessionHandle, privateKeyHandle pkcs11.ObjectHandle) (rsa.PublicKey, error) {
var noKey rsa.PublicKey
template := []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil),
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil),
}
attr, err := module.GetAttributeValue(session, privateKeyHandle, template)
if err != nil {
return noKey, err
}
n := big.NewInt(0)
e := int(0)
gotModulus, gotExponent := false, false
for _, a := range attr {
if a.Type == pkcs11.CKA_MODULUS {
n.SetBytes(a.Value)
gotModulus = true
} else if a.Type == pkcs11.CKA_PUBLIC_EXPONENT {
bigE := big.NewInt(0)
bigE.SetBytes(a.Value)
e = int(bigE.Int64())
gotExponent = true
}
}
if !gotModulus || !gotExponent {
return noKey, errors.New("public key missing either modulus or exponent")
}
return rsa.PublicKey{
N: n,
E: e,
}, nil
}
// Destroy tears down a Key by closing the session. It should be
// called before the key gets GC'ed, to avoid leaving dangling sessions.
func (ps *Key) Destroy() error {
if ps.session != nil {
// NOTE: We do not want to call module.Logout here. module.Logout applies
// application-wide. So if there are multiple sessions active, the other ones
// would be logged out as well, causing CKR_OBJECT_HANDLE_INVALID next
// time they try to sign something. It's also unnecessary to log out explicitly:
// module.CloseSession will log out once the last session in the application is
// closed.
ps.sessionMu.Lock()
defer ps.sessionMu.Unlock()
err := ps.module.CloseSession(*ps.session)
ps.session = nil
if err != nil {
return err
}
}
return nil
}
func (ps *Key) openSession() (pkcs11.SessionHandle, error) {
var noSession pkcs11.SessionHandle
slots, err := ps.module.GetSlotList(true)
if err != nil {
return noSession, err
}
for _, slot := range slots {
// Check that token label matches.
tokenInfo, err := ps.module.GetTokenInfo(slot)
if err != nil {
return noSession, err
}
if tokenInfo.Label != ps.tokenLabel {
continue
}
// Open session
session, err := ps.module.OpenSession(slot, pkcs11.CKF_SERIAL_SESSION)
if err != nil {
return session, err
}
// Login
// Note: Logged-in status is application-wide, not per session. But in
// practice it appears to be okay to login to a token multiple times with the same
// credentials.
if err = ps.module.Login(session, pkcs11.CKU_USER, ps.pin); err != nil {
ps.module.CloseSession(session)
return session, err
}
return session, err
}
return noSession, fmt.Errorf("No slot found matching token label '%s'", ps.tokenLabel)
}
// Public returns the public key for the PKCS #11 key.
func (ps *Key) Public() crypto.PublicKey {
return &ps.publicKey
}
// Sign performs a signature using the PKCS #11 key.
func (ps *Key) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
ps.sessionMu.Lock()
defer ps.sessionMu.Unlock()
if ps.session == nil {
return nil, errors.New("Session was nil")
}
// When the alwaysAuthenticate bit is true (e.g. on a Yubikey NEO in PIV mode),
// each Sign has to include a Logout/Login, or the next Sign request will get
// CKR_USER_NOT_LOGGED_IN. This is very slow, but on the NEO it's not possible
// to clear the CKA_ALWAYS_AUTHENTICATE bit, so this is the only available
// workaround.
// Also, since logged in / logged out is application state rather than session
// state, we take a global lock while we do the logout and login, and during
// the signing.
if ps.alwaysAuthenticate {
modulesMu.Lock()
defer modulesMu.Unlock()
if err := ps.module.Logout(*ps.session); err != nil {
return nil, fmt.Errorf("logout: %s", err)
}
if err = ps.module.Login(*ps.session, pkcs11.CKU_USER, ps.pin); err != nil {
return nil, fmt.Errorf("login: %s", err)
}
}
// Verify that the length of the hash is as expected
hash := opts.HashFunc()
hashLen := hash.Size()
if len(msg) != hashLen {
err = errors.New("input size does not match hash function output size")
return
}
// Add DigestInfo prefix
// TODO: Switch mechanisms based on CKA_KEY_TYPE
mechanism := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil)}
prefix, ok := hashPrefixes[hash]
if !ok {
err = errors.New("unknown hash function")
return
}
signatureInput := append(prefix, msg...)
// Perform the sign operation
err = ps.module.SignInit(*ps.session, mechanism, ps.privateKeyHandle)
if err != nil {
return nil, fmt.Errorf("sign init: %s", err)
}
signature, err = ps.module.Sign(*ps.session, signatureInput)
if err != nil {
return nil, fmt.Errorf("sign: %s", err)
}
return
}

View File

@ -1,247 +0,0 @@
// +build !nopkcs11
// Package pkcs11key implements crypto.Signer for PKCS #11 private
// keys. Currently, only RSA keys are support.
package pkcs11key
import (
"crypto"
"crypto/rsa"
"errors"
"fmt"
"io"
"math/big"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/pkcs11"
)
// from src/pkg/crypto/rsa/pkcs1v15.go
var hashPrefixes = map[crypto.Hash][]byte{
crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
crypto.MD5SHA1: {}, // A special TLS case which doesn't use an ASN1 prefix.
crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14},
}
// PKCS11Key is an implementation of the crypto.Signer interface
// using a key stored in a PKCS#11 hardware token. This enables
// the use of PKCS#11 tokens with the Go x509 library's methods
// for signing certificates.
type PKCS11Key struct {
// The PKCS#11 library to use
module *pkcs11.Ctx
// The slot id of the token to be used. Both this and tokenLabel must match to
// be used.
slotID int
// The label of the token to be used.
// We will automatically search for this in the slot list.
tokenLabel string
// The PIN to be used to log in to the device
pin string
// The public key corresponding to the private key.
publicKey rsa.PublicKey
// The an ObjectHandle pointing to the private key on the HSM.
privateKeyHandle pkcs11.ObjectHandle
}
// New instantiates a new handle to a PKCS #11-backed key.
func New(module, tokenLabel, pin, privLabel string, slotID int) (ps *PKCS11Key, err error) {
// Set up a new pkcs11 object and initialize it
p := pkcs11.New(module)
if p == nil {
err = errors.New("unable to load PKCS#11 module")
return
}
if err = p.Initialize(); err != nil {
return
}
// Initialize a partial key
ps = &PKCS11Key{
module: p,
slotID: slotID,
tokenLabel: tokenLabel,
pin: pin,
}
// Look up the private key
session, err := ps.openSession()
if err != nil {
ps.Destroy()
return
}
defer ps.closeSession(session)
template := []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY),
pkcs11.NewAttribute(pkcs11.CKA_LABEL, privLabel),
}
if err = p.FindObjectsInit(session, template); err != nil {
ps.Destroy()
return
}
objs, _, err := p.FindObjects(session, 2)
if err != nil {
ps.Destroy()
return
}
if err = p.FindObjectsFinal(session); err != nil {
ps.Destroy()
return
}
if len(objs) == 0 {
err = errors.New("private key not found")
ps.Destroy()
return
}
ps.privateKeyHandle = objs[0]
// Populate the pubic key from the private key
// TODO: Add support for non-RSA keys, switching on CKA_KEY_TYPE
template = []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil),
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil),
}
attr, err := p.GetAttributeValue(session, ps.privateKeyHandle, template)
if err != nil {
ps.Destroy()
return
}
n := big.NewInt(0)
e := int(0)
gotModulus, gotExponent := false, false
for _, a := range attr {
if a.Type == pkcs11.CKA_MODULUS {
n.SetBytes(a.Value)
gotModulus = true
} else if a.Type == pkcs11.CKA_PUBLIC_EXPONENT {
bigE := big.NewInt(0)
bigE.SetBytes(a.Value)
e = int(bigE.Int64())
gotExponent = true
}
}
if !gotModulus || !gotExponent {
ps.Destroy()
return
}
ps.publicKey = rsa.PublicKey{
N: n,
E: e,
}
return
}
// Destroy tears down a PKCS11Key.
//
// This method must be called before the PKCS11Key is GC'ed, in order
// to ensure that the PKCS#11 module itself is properly finalized and
// destroyed.
//
// The idiomatic way to do this (assuming no need for a long-lived
// signer) is as follows:
//
// ps, err := NewPKCS11Signer(...)
// if err != nil { ... }
// defer ps.Destroy()
func (ps *PKCS11Key) Destroy() {
if ps.module != nil {
ps.module.Finalize()
ps.module.Destroy()
}
}
func (ps *PKCS11Key) openSession() (session pkcs11.SessionHandle, err error) {
// Check if there is a PCKS11 token with slots. It has side-effects that
// allow the rest of the code here to work.
_, err = ps.module.GetSlotList(true)
if err != nil {
return
}
slotID := uint(ps.slotID)
// Look up slot by id
tokenInfo, err := ps.module.GetTokenInfo(slotID)
if err != nil {
return
}
// Check that token label matches.
if tokenInfo.Label != ps.tokenLabel {
err = fmt.Errorf("Token label in slot %d was '%s', expected '%s'",
slotID, tokenInfo.Label, ps.tokenLabel)
return
}
// Open session
session, err = ps.module.OpenSession(slotID, pkcs11.CKF_SERIAL_SESSION)
if err != nil {
return session, err
}
// Login
if err = ps.module.Login(session, pkcs11.CKU_USER, ps.pin); err != nil {
return session, err
}
return session, err
}
func (ps *PKCS11Key) closeSession(session pkcs11.SessionHandle) {
ps.module.Logout(session)
ps.module.CloseSession(session)
}
// Public returns the public key for the PKCS #11 key.
func (ps *PKCS11Key) Public() crypto.PublicKey {
return &ps.publicKey
}
// Sign performs a signature using the PKCS #11 key.
func (ps *PKCS11Key) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
// Verify that the length of the hash is as expected
hash := opts.HashFunc()
hashLen := hash.Size()
if len(msg) != hashLen {
err = errors.New("input size does not match hash function output size")
return
}
// Add DigestInfo prefix
// TODO: Switch mechanisms based on CKA_KEY_TYPE
mechanism := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil)}
prefix, ok := hashPrefixes[hash]
if !ok {
err = errors.New("unknown hash function")
return
}
signatureInput := append(prefix, msg...)
// Open a session
session, err := ps.openSession()
if err != nil {
return
}
defer ps.closeSession(session)
// Perform the sign operation
err = ps.module.SignInit(session, mechanism, ps.privateKeyHandle)
if err != nil {
return
}
signature, err = ps.module.Sign(session, signatureInput)
return
}

View File

@ -0,0 +1,88 @@
package pkcs11key
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"flag"
"math/big"
"runtime"
"testing"
"time"
)
var module = flag.String("module", "", "Path to PKCS11 module")
var tokenLabel = flag.String("tokenLabel", "", "Token label")
var pin = flag.String("pin", "", "PIN")
var privateKeyLabel = flag.String("privateKeyLabel", "", "Private key label")
var sessionCount = flag.Int("sessions", runtime.GOMAXPROCS(-1), `Number of PKCS#11 sessions to use.
For SoftHSM, GOMAXPROCS is appropriate, but for an external HSM the optimum session count depends on the HSM's parallelism.`)
// BenchmarkPKCS11 signs a certificate repeatedly using a PKCS11 token and
// measures speed. To run (with SoftHSM):
// go test -bench=. -benchtime 5s ./crypto/pkcs11key/ \
// -module /usr/lib/softhsm/libsofthsm.so -token-label "softhsm token" \
// -pin 1234 -private-key-label "my key" -cpu 4
// You can adjust benchtime if you want to run for longer or shorter, and change
// the number of CPUs to select the parallelism you want.
func BenchmarkPKCS11(b *testing.B) {
if *module == "" || *tokenLabel == "" || *pin == "" || *privateKeyLabel == "" {
b.Fatal("Must pass all flags: module, tokenLabel, pin, and privateKeyLabel")
return
}
// A minimal, bogus certificate to be signed.
// Note: we choose a large N to make up for some of the missing fields in the
// bogus certificate, so we wind up something approximately the size of a real
// certificate.
N := big.NewInt(1)
N.Lsh(N, 6000)
template := x509.Certificate{
SerialNumber: big.NewInt(1),
PublicKeyAlgorithm: x509.RSA,
NotBefore: time.Now(),
NotAfter: time.Now(),
PublicKey: &rsa.PublicKey{
N: N,
E: 1 << 17,
},
}
pool, err := NewPool(*sessionCount, *module, *tokenLabel, *pin, *privateKeyLabel)
if err != nil {
b.Fatal(err)
return
}
defer pool.Destroy()
instance := pool.get()
if instance.alwaysAuthenticate {
b.Log("WARNING: Token has CKA_ALWAYS_AUTHENTICATE attribute, which makes signing slow.")
}
pool.put(instance)
// Reset the benchmarking timer so we don't include setup time.
b.ResetTimer()
// Start recording total time. Go's benchmarking code is interested in
// nanoseconds per op, but we're also interested in the total throughput.
start := time.Now()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, err = x509.CreateCertificate(rand.Reader, &template, &template, template.PublicKey, pool)
if err != nil {
b.Fatal(err)
return
}
}
})
elapsedTime := time.Now().Sub(start)
b.Logf("Time, count, ops / second: %s, %d, %g", elapsedTime, b.N, float64(b.N)*float64(time.Second)/float64(elapsedTime))
}
// Dummy test to avoid getting "warning: no tests found"
func TestNothing(t *testing.T) {
}

View File

@ -0,0 +1,100 @@
package pkcs11key
import (
"crypto"
"fmt"
"io"
"sync"
)
// Pool is a pool of Keys suitable for high performance parallel work. Key
// on its own is suitable for multi-threaded use because it has built-in
// locking, but one Key can have at most one operation inflight at a time.
// If you are using an HSM that supports multiple sessions, you may want to
// use a Pool instead, which contains multiple signers. Pool satisfies the
// Signer interface just as Key does, and farms out work to multiple sessions
// under the hood. This assumes you are calling Sign from multiple goroutines
// (as would be common in an RPC or HTTP environment). If you only call Sign
// from a single goroutine, you will only ever get single-session performance.
type Pool struct {
// This slice acts more or less like a concurrent stack. Keys are popped off
// the top for use, and then pushed back on when they are no longer in use.
signers []*Key
// The initial length of signers, before any are popped off for use.
totalCount int
// This variable signals the condition that there are Keys available to be
// used.
cond *sync.Cond
}
func (p *Pool) get() *Key {
p.cond.L.Lock()
for len(p.signers) == 0 {
p.cond.Wait()
}
instance := p.signers[len(p.signers)-1]
p.signers = p.signers[:len(p.signers)-1]
p.cond.L.Unlock()
return instance
}
func (p *Pool) put(instance *Key) {
p.cond.L.Lock()
p.signers = append(p.signers, instance)
p.cond.Signal()
p.cond.L.Unlock()
}
// Sign performs a signature using an available PKCS #11 key. If there is no key
// available, it blocks until there is.
func (p *Pool) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
instance := p.get()
defer p.put(instance)
return instance.Sign(rand, msg, opts)
}
// Public returns the public key of any one of the signers in the pool. Since
// they were all created with the same arguments, the public key should be the
// same for each one.
func (p *Pool) Public() crypto.PublicKey {
instance := p.get()
defer p.put(instance)
return instance.Public()
}
// NewPool creates a pool of Keys of size n.
func NewPool(n int, modulePath, tokenLabel, pin, privateKeyLabel string) (*Pool, error) {
var err error
signers := make([]*Key, n)
for i := 0; i < n; i++ {
signers[i], err = New(modulePath, tokenLabel, pin, privateKeyLabel)
// If any of the signers fail, exit early. This could be, e.g., a bad PIN,
// and we want to make sure not to lock the token.
if err != nil {
for j := 0; j < i; j++ {
signers[j].Destroy()
}
return nil, fmt.Errorf("pkcs11key: problem making Key: %s", err)
}
}
var mutex sync.Mutex
return &Pool{
signers: signers,
totalCount: len(signers),
cond: sync.NewCond(&mutex),
}, nil
}
// Destroy calls destroy for each of the member keys, shutting down their
// sessions.
func (p *Pool) Destroy() error {
for i := 0; i < p.totalCount; i++ {
err := p.get().Destroy()
if err != nil {
return fmt.Errorf("destroy error: %s", err)
}
}
return nil
}

View File

@ -2,6 +2,7 @@
package csr
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@ -14,6 +15,7 @@ import (
"strings"
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
)
@ -32,33 +34,51 @@ type Name struct {
OU string // OrganisationalUnitName
}
// A KeyRequest contains the algorithm and key size for a new private
// key.
type KeyRequest struct {
Algo string `json:"algo"`
Size int `json:"size"`
// A KeyRequest is a generic request for a new key.
type KeyRequest interface {
Algo() string
Size() int
Generate() (crypto.PrivateKey, error)
SigAlgo() x509.SignatureAlgorithm
}
// The DefaultKeyRequest is used when no key request data is provided
// in the request. This should be a safe default.
var DefaultKeyRequest = KeyRequest{
Algo: "ecdsa",
Size: curveP256,
// A BasicKeyRequest contains the algorithm and key size for a new private key.
type BasicKeyRequest struct {
A string `json:"algo"`
S int `json:"size"`
}
// NewBasicKeyRequest returns a default BasicKeyRequest.
func NewBasicKeyRequest() *BasicKeyRequest {
return &BasicKeyRequest{"ecdsa", curveP256}
}
// Algo returns the requested key algorithm represented as a string.
func (kr *BasicKeyRequest) Algo() string {
return kr.A
}
// Size returns the requested key size.
func (kr *BasicKeyRequest) Size() int {
return kr.S
}
// Generate generates a key as specified in the request. Currently,
// only ECDSA and RSA are supported.
func (kr *KeyRequest) Generate() (interface{}, error) {
log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo, kr.Size)
switch kr.Algo {
func (kr *BasicKeyRequest) Generate() (crypto.PrivateKey, error) {
log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo(), kr.Size())
switch kr.Algo() {
case "rsa":
if kr.Size < 2048 {
if kr.Size() < 2048 {
return nil, errors.New("RSA key is too weak")
}
return rsa.GenerateKey(rand.Reader, kr.Size)
if kr.Size() > 8192 {
return nil, errors.New("RSA key size too large")
}
return rsa.GenerateKey(rand.Reader, kr.Size())
case "ecdsa":
var curve elliptic.Curve
switch kr.Size {
switch kr.Size() {
case curveP256:
curve = elliptic.P256()
case curveP384:
@ -76,26 +96,26 @@ func (kr *KeyRequest) Generate() (interface{}, error) {
// SigAlgo returns an appropriate X.509 signature algorithm given the
// key request's type and size.
func (kr *KeyRequest) SigAlgo() x509.SignatureAlgorithm {
switch kr.Algo {
func (kr *BasicKeyRequest) SigAlgo() x509.SignatureAlgorithm {
switch kr.Algo() {
case "rsa":
switch {
case kr.Size >= 4096:
case kr.Size() >= 4096:
return x509.SHA512WithRSA
case kr.Size >= 3072:
case kr.Size() >= 3072:
return x509.SHA384WithRSA
case kr.Size >= 2048:
case kr.Size() >= 2048:
return x509.SHA256WithRSA
default:
return x509.SHA1WithRSA
}
case "ecdsa":
switch {
case kr.Size == curveP521:
switch kr.Size() {
case curveP521:
return x509.ECDSAWithSHA512
case kr.Size == curveP384:
case curveP384:
return x509.ECDSAWithSHA384
case kr.Size == curveP256:
case curveP256:
return x509.ECDSAWithSHA256
default:
return x509.ECDSAWithSHA1
@ -115,10 +135,18 @@ type CAConfig struct {
// certificate request functionality.
type CertificateRequest struct {
CN string
Names []Name `json:"names"`
Hosts []string `json:"hosts"`
KeyRequest *KeyRequest `json:"key,omitempty"`
CA *CAConfig `json:"ca,omitempty"`
Names []Name `json:"names"`
Hosts []string `json:"hosts"`
KeyRequest KeyRequest `json:"key,omitempty"`
CA *CAConfig `json:"ca,omitempty"`
}
// New returns a new, empty CertificateRequest with a
// BasicKeyRequest.
func New() *CertificateRequest {
return &CertificateRequest{
KeyRequest: &BasicKeyRequest{},
}
}
// appendIf appends to a if s is not an empty string.
@ -152,13 +180,10 @@ func (cr *CertificateRequest) Name() pkix.Name {
func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) {
log.Info("received CSR")
if req.KeyRequest == nil {
req.KeyRequest = &KeyRequest{
Algo: DefaultKeyRequest.Algo,
Size: DefaultKeyRequest.Size,
}
req.KeyRequest = NewBasicKeyRequest()
}
log.Infof("generating key: %s-%d", req.KeyRequest.Algo, req.KeyRequest.Size)
log.Infof("generating key: %s-%d", req.KeyRequest.Algo(), req.KeyRequest.Size())
priv, err := req.KeyRequest.Generate()
if err != nil {
err = cferr.Wrap(cferr.PrivateKeyError, cferr.GenerationFailed, err)
@ -249,3 +274,53 @@ func IsNameEmpty(n Name) bool {
}
return false
}
// Regenerate uses the provided CSR as a template for signing a new
// CSR using priv.
func Regenerate(priv crypto.Signer, csr []byte) ([]byte, error) {
req, extra, err := helpers.ParseCSR(csr)
if err != nil {
return nil, err
} else if len(extra) > 0 {
return nil, errors.New("csr: trailing data in certificate request")
}
return x509.CreateCertificateRequest(rand.Reader, req, priv)
}
// Generate creates a new CSR from a CertificateRequest structure and
// an existing key. The KeyRequest field is ignored.
func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err error) {
sigAlgo := helpers.SignerAlgo(priv, crypto.SHA256)
if sigAlgo == x509.UnknownSignatureAlgorithm {
return nil, cferr.New(cferr.PrivateKeyError, cferr.Unavailable)
}
var tpl = x509.CertificateRequest{
Subject: req.Name(),
SignatureAlgorithm: sigAlgo,
}
for i := range req.Hosts {
if ip := net.ParseIP(req.Hosts[i]); ip != nil {
tpl.IPAddresses = append(tpl.IPAddresses, ip)
} else {
tpl.DNSNames = append(tpl.DNSNames, req.Hosts[i])
}
}
csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv)
if err != nil {
log.Errorf("failed to generate a CSR: %v", err)
err = cferr.Wrap(cferr.CSRError, cferr.BadRequest, err)
return
}
block := pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csr,
}
log.Info("encoded CSR")
csr = pem.EncodeToMemory(&block)
return
}

View File

@ -1,6 +1,7 @@
package csr
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
@ -9,13 +10,13 @@ import (
"testing"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
)
// TestKeyRequest ensures that key generation returns the same type of
// key specified in the KeyRequest.
func TestKeyRequest(t *testing.T) {
var kr = &KeyRequest{"ecdsa", 256}
// TestBasicKeyRequest ensures that key generation returns the same type of
// key specified in the BasicKeyRequest.
func TestBasicKeyRequest(t *testing.T) {
kr := NewBasicKeyRequest()
priv, err := kr.Generate()
if err != nil {
t.Fatalf("%v", err)
@ -23,11 +24,11 @@ func TestKeyRequest(t *testing.T) {
switch priv.(type) {
case *rsa.PrivateKey:
if kr.Algo != "rsa" {
if kr.Algo() != "rsa" {
t.Fatal("RSA key generated, but expected", kr.Algo)
}
case *ecdsa.PrivateKey:
if kr.Algo != "ecdsa" {
if kr.Algo() != "ecdsa" {
t.Fatal("ECDSA key generated, but expected", kr.Algo)
}
}
@ -36,7 +37,6 @@ func TestKeyRequest(t *testing.T) {
// TestPKIXName validates building a pkix.Name structure from a
// CertificateRequest.
func TestPKIXName(t *testing.T) {
var kr = KeyRequest{"ecdsa", 256}
var cr = &CertificateRequest{
CN: "Test Common Name",
Names: []Name{
@ -56,7 +56,7 @@ func TestPKIXName(t *testing.T) {
},
},
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: &kr,
KeyRequest: NewBasicKeyRequest(),
}
name := cr.Name()
@ -78,7 +78,6 @@ func TestPKIXName(t *testing.T) {
// TestParseRequest ensures that a valid certificate request does not
// error.
func TestParseRequest(t *testing.T) {
var kr = KeyRequest{"ecdsa", 256}
var cr = &CertificateRequest{
CN: "Test Common Name",
Names: []Name{
@ -98,7 +97,7 @@ func TestParseRequest(t *testing.T) {
},
},
Hosts: []string{"cloudflare.com", "www.cloudflare.com", "192.168.0.1"},
KeyRequest: &kr,
KeyRequest: NewBasicKeyRequest(),
}
_, _, err := ParseRequest(cr)
@ -126,7 +125,7 @@ func TestECGeneration(t *testing.T) {
var eckey *ecdsa.PrivateKey
for _, sz := range []int{256, 384, 521} {
kr := &KeyRequest{Algo: "ecdsa", Size: sz}
kr := &BasicKeyRequest{"ecdsa", sz}
priv, err := kr.Generate()
if err != nil {
t.Fatalf("%v", err)
@ -145,13 +144,13 @@ func TestRSAKeyGeneration(t *testing.T) {
var rsakey *rsa.PrivateKey
for _, sz := range []int{2048, 3072, 4096} {
kr := &KeyRequest{Algo: "rsa", Size: sz}
kr := &BasicKeyRequest{"rsa", sz}
priv, err := kr.Generate()
if err != nil {
t.Fatalf("%v", err)
}
rsakey = priv.(*rsa.PrivateKey)
if rsakey.PublicKey.N.BitLen() != kr.Size {
if rsakey.PublicKey.N.BitLen() != kr.Size() {
t.Fatal("Generated key has wrong size.")
}
if sa := kr.SigAlgo(); sa == x509.UnknownSignatureAlgorithm {
@ -160,26 +159,26 @@ func TestRSAKeyGeneration(t *testing.T) {
}
}
// TestBadKeyRequest ensures that generating a key from a KeyRequest
// TestBadBasicKeyRequest ensures that generating a key from a BasicKeyRequest
// fails with an invalid algorithm, or an invalid RSA or ECDSA key
// size. An invalid ECDSA key size is any size other than 256, 384, or
// 521; an invalid RSA key size is any size less than 2048 bits.
func TestBadKeyRequest(t *testing.T) {
kr := &KeyRequest{Algo: "yolocrypto", Size: 1024}
func TestBadBasicKeyRequest(t *testing.T) {
kr := &BasicKeyRequest{"yolocrypto", 1024}
if _, err := kr.Generate(); err == nil {
t.Fatal("Key generation should fail with invalid algorithm")
} else if sa := kr.SigAlgo(); sa != x509.UnknownSignatureAlgorithm {
t.Fatal("The wrong signature algorithm was returned from SigAlgo!")
}
kr.Algo = "ecdsa"
kr.A = "ecdsa"
if _, err := kr.Generate(); err == nil {
t.Fatal("Key generation should fail with invalid key size")
} else if sa := kr.SigAlgo(); sa != x509.ECDSAWithSHA1 {
t.Fatal("The wrong signature algorithm was returned from SigAlgo!")
}
kr.Algo = "rsa"
kr.A = "rsa"
if _, err := kr.Generate(); err == nil {
t.Fatal("Key generation should fail with invalid key size")
} else if sa := kr.SigAlgo(); sa != x509.SHA1WithRSA {
@ -188,9 +187,9 @@ func TestBadKeyRequest(t *testing.T) {
}
// TestDefaultKeyRequest makes sure that certificate requests without
// TestDefaultBasicKeyRequest makes sure that certificate requests without
// explicit key requests fall back to the default key request.
func TestDefaultKeyRequest(t *testing.T) {
func TestDefaultBasicKeyRequest(t *testing.T) {
var req = &CertificateRequest{
Names: []Name{
{
@ -215,13 +214,14 @@ func TestDefaultKeyRequest(t *testing.T) {
t.Fatal("Bad private key was generated!")
}
DefaultKeyRequest := NewBasicKeyRequest()
switch block.Type {
case "RSA PRIVATE KEY":
if DefaultKeyRequest.Algo != "rsa" {
if DefaultKeyRequest.Algo() != "rsa" {
t.Fatal("Invalid default key request.")
}
case "EC PRIVATE KEY":
if DefaultKeyRequest.Algo != "ecdsa" {
if DefaultKeyRequest.Algo() != "ecdsa" {
t.Fatal("Invalid default key request.")
}
}
@ -240,12 +240,9 @@ func TestRSACertRequest(t *testing.T) {
OU: "Systems Engineering",
},
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: &KeyRequest{
Algo: "rsa",
Size: 2048,
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: &BasicKeyRequest{"rsa", 2048},
}
_, _, err := ParseRequest(req)
if err != nil {
@ -265,12 +262,9 @@ func TestBadCertRequest(t *testing.T) {
OU: "Systems Engineering",
},
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: &KeyRequest{
Algo: "yolo-crypto",
Size: 2048,
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: &BasicKeyRequest{"yolo-crypto", 2048},
}
_, _, err := ParseRequest(req)
if err == nil {
@ -303,12 +297,9 @@ func TestGenerator(t *testing.T) {
OU: "Systems Engineering",
},
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com", "192.168.0.1"},
KeyRequest: &KeyRequest{
Algo: "rsa",
Size: 2048,
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com", "192.168.0.1"},
KeyRequest: &BasicKeyRequest{"rsa", 2048},
}
csrBytes, _, err := g.ProcessRequest(req)
@ -355,11 +346,8 @@ func TestBadGenerator(t *testing.T) {
},
},
// Missing CN
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: &KeyRequest{
Algo: "rsa",
Size: 2048,
},
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: &BasicKeyRequest{"rsa", 2048},
}
_, _, err := g.ProcessRequest(missingCN)
@ -379,12 +367,9 @@ func TestWeakCSR(t *testing.T) {
OU: "Systems Engineering",
},
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: &KeyRequest{
Algo: "rsa",
Size: 1024,
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: &BasicKeyRequest{"rsa", 1024},
}
g := &Generator{testValidator}
@ -431,3 +416,76 @@ func TestIsNameEmpty(t *testing.T) {
}
}
}
func TestGenerate(t *testing.T) {
var req = &CertificateRequest{
Names: []Name{
{
C: "US",
ST: "California",
L: "San Francisco",
O: "CloudFlare",
OU: "Systems Engineering",
},
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com", "192.168.0.1"},
KeyRequest: &BasicKeyRequest{"ecdsa", 256},
}
key, err := req.KeyRequest.Generate()
if err != nil {
t.Fatalf("%v", err)
}
priv, ok := key.(crypto.Signer)
if !ok {
t.Fatal("Private key is not a signer.")
}
csrPEM, err := Generate(priv, req)
if err != nil {
t.Fatalf("%v", err)
}
_, _, err = helpers.ParseCSR(csrPEM)
if err != nil {
t.Fatalf("%v", err)
}
}
func TestReGenerate(t *testing.T) {
var req = &CertificateRequest{
Names: []Name{
{
C: "US",
ST: "California",
L: "San Francisco",
O: "CloudFlare",
OU: "Systems Engineering",
},
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com", "192.168.0.1"},
KeyRequest: &BasicKeyRequest{"ecdsa", 256},
}
csr, key, err := ParseRequest(req)
if err != nil {
t.Fatalf("%v", err)
}
priv, err := helpers.ParsePrivateKeyPEM(key)
if err != nil {
t.Fatalf("%v", err)
}
csr, err = Generate(priv, req)
if err != nil {
t.Fatalf("%v", err)
}
if _, _, err = helpers.ParseCSR(csr); err != nil {
t.Fatalf("%v", err)
}
}

View File

@ -8,8 +8,10 @@ import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"errors"
"math/big"
//"fmt"
"strings"
"time"
@ -231,6 +233,7 @@ func ParseSelfSignedCertificatePEM(certPEM []byte) (*x509.Certificate, error) {
if err != nil {
return nil, err
}
if err := cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature); err != nil {
return nil, cferr.Wrap(cferr.CertificateError, cferr.VerifyFailed, err)
}
@ -312,3 +315,105 @@ func GetKeyDERFromPEM(in []byte) ([]byte, error) {
return nil, cferr.New(cferr.PrivateKeyError, cferr.DecodeFailed)
}
// CheckSignature verifies a signature made by the key on a CSR, such
// as on the CSR itself.
func CheckSignature(csr *x509.CertificateRequest, algo x509.SignatureAlgorithm, signed, signature []byte) error {
var hashType crypto.Hash
switch algo {
case x509.SHA1WithRSA, x509.ECDSAWithSHA1:
hashType = crypto.SHA1
case x509.SHA256WithRSA, x509.ECDSAWithSHA256:
hashType = crypto.SHA256
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
hashType = crypto.SHA384
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
hashType = crypto.SHA512
default:
return x509.ErrUnsupportedAlgorithm
}
if !hashType.Available() {
return x509.ErrUnsupportedAlgorithm
}
h := hashType.New()
h.Write(signed)
digest := h.Sum(nil)
switch pub := csr.PublicKey.(type) {
case *rsa.PublicKey:
return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
case *ecdsa.PublicKey:
ecdsaSig := new(struct{ R, S *big.Int })
if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
return err
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
return errors.New("x509: ECDSA signature contained zero or negative values")
}
if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
return errors.New("x509: ECDSA verification failure")
}
return nil
}
return x509.ErrUnsupportedAlgorithm
}
// ParseCSR parses a PEM- or DER-encoded PKCS #10 certificate signing request.
func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error) {
in = bytes.TrimSpace(in)
p, rest := pem.Decode(in)
if p != nil {
if p.Type != "CERTIFICATE REQUEST" {
return nil, rest, cferr.New(cferr.CSRError, cferr.BadRequest)
}
csr, err = x509.ParseCertificateRequest(p.Bytes)
} else {
csr, err = x509.ParseCertificateRequest(in)
}
if err != nil {
return nil, rest, err
}
err = CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
if err != nil {
return nil, rest, err
}
return csr, rest, nil
}
// SignerAlgo returns an X.509 signature algorithm corresponding to
// the crypto.Hash provided from a crypto.Signer.
func SignerAlgo(priv crypto.Signer, h crypto.Hash) x509.SignatureAlgorithm {
switch priv.Public().(type) {
case *rsa.PublicKey:
switch h {
case crypto.SHA512:
return x509.SHA512WithRSA
case crypto.SHA384:
return x509.SHA384WithRSA
case crypto.SHA256:
return x509.SHA256WithRSA
default:
return x509.SHA1WithRSA
}
case *ecdsa.PublicKey:
switch h {
case crypto.SHA512:
return x509.ECDSAWithSHA512
case crypto.SHA384:
return x509.ECDSAWithSHA384
case crypto.SHA256:
return x509.ECDSAWithSHA256
default:
return x509.ECDSAWithSHA1
}
default:
return x509.UnknownSignatureAlgorithm
}
}

View File

@ -356,3 +356,25 @@ func TestParsePrivateKeyPEM(t *testing.T) {
}
}
// Imported from signers/local/testdata/
const ecdsaTestCSR = "testdata/ecdsa256.csr"
func TestParseCSRPEM(t *testing.T) {
in, err := ioutil.ReadFile(ecdsaTestCSR)
if err != nil {
t.Fatalf("%v", err)
}
_, _, err = ParseCSR(in)
if err != nil {
t.Fatalf("%v", err)
}
in[12]++
_, _, err = ParseCSR(in)
if err == nil {
t.Fatalf("Expected an invalid CSR.")
}
in[12]--
}

View File

@ -10,8 +10,8 @@ import (
"net/url"
"strings"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/pkcs11"
)
func setIfPresent(val url.Values, k string, target *string) {
@ -27,13 +27,13 @@ var ErrInvalidURI = errors.New(errors.PrivateKeyError, errors.ParseFailed)
// ParsePKCS11URI parses a PKCS #11 URI into a PKCS #11
// configuration. Note that the module path will override the module
// name if present.
func ParsePKCS11URI(uri string) (*pkcs11.Config, error) {
func ParsePKCS11URI(uri string) (*pkcs11key.Config, error) {
u, err := url.Parse(uri)
if err != nil || u.Scheme != "pkcs11" {
return nil, ErrInvalidURI
}
c := new(pkcs11.Config)
c := new(pkcs11key.Config)
pk11PAttr, err := url.ParseQuery(u.Opaque)
if err != nil {
@ -44,11 +44,11 @@ func ParsePKCS11URI(uri string) (*pkcs11.Config, error) {
if err != nil {
return nil, ErrInvalidURI
}
setIfPresent(pk11PAttr, "token", &c.Token)
setIfPresent(pk11PAttr, "token", &c.TokenLabel)
setIfPresent(pk11PAttr, "object", &c.PrivateKeyLabel)
setIfPresent(pk11QAttr, "module-name", &c.Module)
setIfPresent(pk11QAttr, "module-path", &c.Module)
setIfPresent(pk11QAttr, "pin-value", &c.PIN)
setIfPresent(pk11PAttr, "slot-description", &c.Label)
var pinSourceURI string
setIfPresent(pk11QAttr, "pin-source", &pinSourceURI)

View File

@ -4,15 +4,15 @@ import (
"fmt"
"testing"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/pkcs11"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
)
type pkcs11UriTest struct {
URI string
Config *pkcs11.Config
Config *pkcs11key.Config
}
func cmpConfigs(a, b *pkcs11.Config) bool {
func cmpConfigs(a, b *pkcs11key.Config) bool {
if a == nil {
if b == nil {
return true
@ -25,12 +25,12 @@ func cmpConfigs(a, b *pkcs11.Config) bool {
}
return (a.Module == b.Module) &&
(a.Token == b.Token) &&
(a.TokenLabel == b.TokenLabel) &&
(a.PIN == b.PIN) &&
(a.Label == b.Label)
(a.PrivateKeyLabel == b.PrivateKeyLabel)
}
func diffConfigs(want, have *pkcs11.Config) {
func diffConfigs(want, have *pkcs11key.Config) {
if have == nil && want != nil {
fmt.Printf("Expected config, have nil.")
return
@ -45,9 +45,9 @@ func diffConfigs(want, have *pkcs11.Config) {
}
diff("Module", want.Module, have.Module)
diff("Token", want.Token, have.Token)
diff("TokenLabel", want.TokenLabel, have.TokenLabel)
diff("PIN", want.PIN, have.PIN)
diff("Label", want.Label, have.Label)
diff("PrivateKeyLabel", want.PrivateKeyLabel, have.PrivateKeyLabel)
}
/* Config from PKCS #11 signer
@ -61,20 +61,20 @@ type Config struct {
var pkcs11UriCases = []pkcs11UriTest{
{"pkcs11:token=Software%20PKCS%2311%20softtoken;manufacturer=Snake%20Oil,%20Inc.?pin-value=the-pin",
&pkcs11.Config{
Token: "Software PKCS#11 softtoken",
PIN: "the-pin",
&pkcs11key.Config{
TokenLabel: "Software PKCS#11 softtoken",
PIN: "the-pin",
}},
{"pkcs11:slot-description=Sun%20Metaslot",
&pkcs11.Config{
Label: "Sun Metaslot",
{"pkcs11:token=Sun%20Token",
&pkcs11key.Config{
TokenLabel: "Sun Token",
}},
{"pkcs11:slot-description=test-label;token=test-token?pin-source=file:testdata/pin&module-name=test-module",
&pkcs11.Config{
Label: "test-label",
Token: "test-token",
PIN: "123456",
Module: "test-module",
{"pkcs11:object=test-privkey;token=test-token?pin-source=file:testdata/pin&module-name=test-module",
&pkcs11key.Config{
PrivateKeyLabel: "test-privkey",
TokenLabel: "test-token",
PIN: "123456",
Module: "test-module",
}},
}

View File

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBgTCCASgCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl
LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBn9Ldie6BOcMHezn2dPuYqW
z/NoLYMLGNBqhOxUyEidYClI0JW2pWyUgT3A2UazFp1WgE94y7Z+2YlfRz+vcrKg
PzA9BgkqhkiG9w0BCQ4xMDAuMCwGA1UdEQQlMCOCDmNsb3VkZmxhcmUuY29tghF3
d3djbG91ZGZsYXJlLmNvbTAKBggqhkjOPQQDAgNHADBEAiBM+QRxe8u6rkdr10Jy
cxbR6NxrGrNeg5QqiOqF96JEmgIgDbtjd5e3y3I8W/+ih2us3WtMxgnTXfqPd48i
VLcv28Q=
-----END CERTIFICATE REQUEST-----

View File

@ -1 +1 @@
{"cert":"-----BEGIN CERTIFICATE-----\nMIIEBjCCAvCgAwIBAgIIcoeD7FP4p0gwCwYJKoZIhvcNAQELMIGQMQswCQYDVQQG\nEwJVUzEeMBwGA1UEChMVSW50ZXJuZXQgV2lkZ2V0cywgTExDMR4wHAYDVQQLExVD\nZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR\nBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAMTC2V4YW1wbGUuY29tMB4XDTE1MDMy\nMzIwMTcwMloXDTE1MDMyMzIwMjIwMlowgZAxCzAJBgNVBAYTAlVTMR4wHAYDVQQK\nExVJbnRlcm5ldCBXaWRnZXRzLCBMTEMxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1\ndGhvcml0eTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv\ncm5pYTEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQC/S+yLMwj5TEIWSujzOf3VBMCyiW+KM17fvSK6Wp14OElFs53G\nNNRVJDkIOyx66I29YqvYTO28WyLX3X8z8tS3hH8C9Bp/lG5X644hiQpl5vHSJfxa\nQdvtJZevY6lwnz9JDha1X0P7XzHeyJVC8u3Uq8i4X7t2GC23p0Jz5hEs6YBn6cQa\nui1I40U9teNUPE4ofUOt2EUx2uDiRs0+IOy5gM/GH7XDv88eWwSm/loqwYp5CdAv\n0/jnPqisqen0KXEQNFgZJZSzZy2a1GE4Ie/yc9R8udnkUd5pgIk26T4UDSpNVnDA\n5N7mH2MpcQ4l1ErTVk8WWrM+6X5UVIkWA56LAgMBAAGjZjBkMA4GA1UdDwEB/wQE\nAwIABjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBR8CfC68fIBAkJcjFOD\n6SDYLUTanTAfBgNVHSMEGDAWgBR8CfC68fIBAkJcjFOD6SDYLUTanTALBgkqhkiG\n9w0BAQsDggEBACZdSOd9C+ZiVGSqK113CvvQWMVxgEiprbQ+p2jJ+tmcZdAEDgDO\nEEdjPs1ygdhwJwiQL1C5Ak4Xc91aamP2dZtMlM02UVzTrjsOyC7FShq238pyaeQX\nzXyOOSnriMeuJvipLiStk1WhrbRkq1Zfy5CmX6FYd1dfuljwoDgfAY9GtCi0T6MY\n6vu9f8/S2ei4sTtqkF2/KbBq3K17WqsMmDxCYMWxBaY1z92i01lypPw3tM3k4Gkn\nJnil6RYlqg9odhazZcFXsNNywxg2S2Q2pJ1JLGDypgBpqzMQjd7zP6WOz9MTP8yK\n+V2sF/dDlAQCvl8EPvigqCCOXtL6uHYo0Fw=\n-----END CERTIFICATE-----\n","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAv0vsizMI+UxCFkro8zn91QTAsolvijNe370iulqdeDhJRbOd\nxjTUVSQ5CDsseuiNvWKr2EztvFsi191/M/LUt4R/AvQaf5RuV+uOIYkKZebx0iX8\nWkHb7SWXr2OpcJ8/SQ4WtV9D+18x3siVQvLt1KvIuF+7dhgtt6dCc+YRLOmAZ+nE\nGrotSONFPbXjVDxOKH1DrdhFMdrg4kbNPiDsuYDPxh+1w7/PHlsEpv5aKsGKeQnQ\nL9P45z6orKnp9ClxEDRYGSWUs2ctmtRhOCHv8nPUfLnZ5FHeaYCJNuk+FA0qTVZw\nwOTe5h9jKXEOJdRK01ZPFlqzPul+VFSJFgOeiwIDAQABAoIBAEzKaZYnUn7gwda5\nE3Iv4VlVoxUIXqIXpQojnoE4RuYvkhpM1BTwuBvFgq9vMZfVKrDPnzQhDX4DB6Wn\n4Jw16nYQ1fuVid2U9AaPJGsJ3Aa0AnbdkMOalHJKGO3rD+WJx5nH8g20xFFwEVDh\nLCnH4vjwTI7WvyXU5uVGW0KzJEnmbpjupMo+3IKSUdZyZspAc2vZby+bU18U7PO2\nfsyW7aZjQ0b2XFKbWpw+RD/HOskBImOqoL9AAzvXEaxFR3mz4OQBtUlzEiogr0/6\ngBNTbyIVSE+0hodtLxJMhi+xIFeoGCyYdpONU3mqKcYjnV4mHfLX2+Sn4hTJtaLg\n7VHyiKECgYEAwC+4RK1E/UU9nvEgaUowpmAtnk40g31ACylOmpAiHaBaJfZ2JToe\nSwd60oFZ5QEA/zoch1niUwgtj4Bn+Iv/sRD3N60DuLg5F+S+0qH7viZb5jAsIYmP\ntF7+nMd6iID+vXhXqL/ElJczGldKm+wKPV9n8KcdvHwDySvsy//WC0cCgYEA/tCR\nHbiLbMyA+stu7i5AdVWsSxmNipD8pjrI6509zerwnwvwX5nxagO3hBmev/jJgJKu\nL3ptZotR5CpcS/PxmNdRHumeQzvN5AnUefB037tQOtocPIAWwnh4a+hf4QtdXRmm\nbtSZPDBaIdEL+6XEpCfHdSktj/8nJ0aftW39rJ0CgYA7hgwiaS/26Z0ePzx0n9/V\nh/BZGYu2NfLCAjcwM/f9CQ8ituT8vrVnM5fc3udCwD7YzdzMJxCr+tpxmamaF3JI\nGyMWgWWnrpcwSU642iaoTCUmdEEW2g5CJTHiyP2wjZNYh49O2qY+B15yiBq5lC/P\ntl7w8DGLkTVy90HOJRzcPQKBgF1rY7iMvvkNZMANs0a5SEJ8PWVvIdhKXYYoCR9w\nj4cd+kelHeQ+0SY35KEWr3/cGyv7GscnUFKHA2fuK9ZgwhNx5Ti7F524oDxZaj+m\na4LW7owVCMMZhr6XYjbVg8lC3GDUlZMOeHt7kp/RP15sINK6DsMibKTdO6KHnufq\nQlV1AoGBAKeRFcw0jSB0PWEVHPDF4pCSz/vhoX/hy0OQN13QWLFhgt/rmTWOCFuJ\nSvIde8oM2pD5f8ZmxzMGkknEnrdX7zz/tE4roImep9G49BbetHGdWVu/TvvDyvXM\nzi//6WQzpjjqQxrl4q7B1tJ4A71fKN6V2u+qSdYg/t31EfE47lCQ\n-----END RSA PRIVATE KEY-----\n"}
{"cert":"-----BEGIN CERTIFICATE-----\nMIIECjCCAvKgAwIBAgIIIrWBQ2reGCQwDQYJKoZIhvcNAQELBQAwgZAxCzAJBgNV\nBAYTAlVTMR4wHAYDVQQKExVJbnRlcm5ldCBXaWRnZXRzLCBMTEMxHjAcBgNVBAsT\nFUNlcnRpZmljYXRlIEF1dGhvcml0eTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTUw\nOTA5MjExNjAwWhcNMTUwOTA5MjExNjAwWjCBkDELMAkGA1UEBhMCVVMxHjAcBgNV\nBAoTFUludGVybmV0IFdpZGdldHMsIExMQzEeMBwGA1UECxMVQ2VydGlmaWNhdGUg\nQXV0aG9yaXR5MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxp\nZm9ybmlhMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBALOFN1p4aaMQKqInRanzmf05CD4kYKKVygggOHhD3ZgwFgIG\n20Z0dlTHStY/UvY+2qkpB1Bp9trmys9YvPzt5ElX2szZ2deZc0TWvzZeuZDQVsbx\n0Ea6RGxRnEEoMrpaRFoe2TOKSVKW+SzLC/eoNUoxsZVo6cNsU4BeGqExOWCDFzjd\nCRLJeXqafYeL1dUiXZ028tOZVWLIjaLu3FKENHeDB36gr99KckFdeDnqaAVvu66V\nbF5QGvIv2RmK9y3cq7rIizYfayi/dBS8/AY31OOHcEGsVTZYjqB/s9aIocCOzfvQ\nV5++QdkDiYhIwokB3fT5b4wIb8CLr0GUnHnslnMCAwEAAaNmMGQwDgYDVR0PAQH/\nBAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFCL83Uiwa3VGSXwD\nf8XKgu1l+nX8MB8GA1UdIwQYMBaAFCL83Uiwa3VGSXwDf8XKgu1l+nX8MA0GCSqG\nSIb3DQEBCwUAA4IBAQCRP7HK4JHxY4uYE3VBkx5SSQouZrnpQCOnUhSof3vi5nk0\nlhfIU7nbS6HVyQC4mY47gDrBlKwTr++Kw9BKyK9EgfVWhct8vszcRexZk5mmO3yi\n3s4kqD158SP0t5AMahHGK/VviD/Id4kaX1PIPHW3TfU3Ly4LP6d/NlloaXc8qrsg\nKJO+AXTy9xnEkjtq7lvnP3JLwA1Y+nozqkp46kQv0K+Nz+MBqN5De12I9J7T0k6h\nij4MsiraPdeij6d1cp+OEW8DPe+MCIBYAvbJXtQY5zYPB2/F6JJHMeQ9AmluvUFn\nD3ipCSVbw1Tgpp8xcx+4rt9Vq6Oo8pGi4JR3C7TN\n-----END CERTIFICATE-----\n","csr":"-----BEGIN CERTIFICATE REQUEST-----\nMIIDAjCCAeoCAQAwgZAxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVJbnRlcm5ldCBX\naWRnZXRzLCBMTEMxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0eTEWMBQG\nA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UE\nAxMLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz\nhTdaeGmjECqiJ0Wp85n9OQg+JGCilcoIIDh4Q92YMBYCBttGdHZUx0rWP1L2Ptqp\nKQdQafba5srPWLz87eRJV9rM2dnXmXNE1r82XrmQ0FbG8dBGukRsUZxBKDK6WkRa\nHtkziklSlvksywv3qDVKMbGVaOnDbFOAXhqhMTlggxc43QkSyXl6mn2Hi9XVIl2d\nNvLTmVViyI2i7txShDR3gwd+oK/fSnJBXXg56mgFb7uulWxeUBryL9kZivct3Ku6\nyIs2H2sov3QUvPwGN9Tjh3BBrFU2WI6gf7PWiKHAjs370FefvkHZA4mISMKJAd30\n+W+MCG/Ai69BlJx57JZzAgMBAAGgLDAqBgkqhkiG9w0BCQ4xHTAbMBkGA1UdEQQS\nMBCCDmNhLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA2E/lSvOtUZjOj\ncOAcALpCVLLTSRdqF7745jSqIrHV8RuB+V6HFi55qoFzCKfJbc81SUqX7DhzoUtP\nNjdq5RfhEh/kWw3GeIN90Y5thVDm0i19A7GY+maJXMjw3LVWlKIo9a7rCOQvCi3O\nkA0QGO1Rc1d6/MneoWUR2tUHjJ83FvO089RaDUQhYpInBXV3GQByJGJeF8USJ/wS\nXy+SLDkaG9mC0TJwWNpldGoZQ57h/QeLtl2wfK3sLga5eVuQO0Lnzt6W3tYvvSAO\nckN/hrTMFMJ/vXHFUPXk8AYUBt0FqgYYHR5JxA9Wl2+Fr+Duw7Qee7/kcbwhbo1M\nLe9GgT/c\n-----END CERTIFICATE REQUEST-----\n","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAs4U3WnhpoxAqoidFqfOZ/TkIPiRgopXKCCA4eEPdmDAWAgbb\nRnR2VMdK1j9S9j7aqSkHUGn22ubKz1i8/O3kSVfazNnZ15lzRNa/Nl65kNBWxvHQ\nRrpEbFGcQSgyulpEWh7ZM4pJUpb5LMsL96g1SjGxlWjpw2xTgF4aoTE5YIMXON0J\nEsl5epp9h4vV1SJdnTby05lVYsiNou7cUoQ0d4MHfqCv30pyQV14OepoBW+7rpVs\nXlAa8i/ZGYr3LdyrusiLNh9rKL90FLz8BjfU44dwQaxVNliOoH+z1oihwI7N+9BX\nn75B2QOJiEjCiQHd9PlvjAhvwIuvQZSceeyWcwIDAQABAoIBAQCYD0cLwuL31EHI\niCtiAY12CFELEhUoomg26dPrStDwkAqUPOqPiyaQOR8SyyCipCrjDnW7j4YhdUxk\nxW2PcJHl7hzPV9hF3xzcOmpMy3+zQkW+ziT1Q+GhPp90MXCGmN881tRz67af1nHJ\nl1PTuw8ogV9Ch1M6zQ8NtKgp0WAgdn2YlkVrYv9/JaOZdh7ewNQPs+V68bC8tmmz\nEj0kuH06Lzr3zxfxOgAq8Xrv8b5GmFNjcRPOu5WWCRKt26FSwAcI/U5AXRuqVr8o\nthMiXKPA+RMSSy7bDF1EOFYdNyrWaLEzyrKLSxTXTvOT5kIfwVWKTTzdNug/BaYy\neesqkgtBAoGBAOafPTvJHYLHuyb11X2FYblvkLaw+orWvdy1E2sV62M3YiloOp5R\neWteEptvsEjo0ZXZyYqOK5WHsbkdTbPE7QPctKIYdVdztPb+3wWHM9hP1kGWuohW\n+S/kVIwf+ijH10Q3wQPgtsIGrj0Q/rkizD6qqmKz8gNizRwX2k3kkp/1AoGBAMdG\nabBuC9XHxFtVIEzHSXbVn38Z6tjxURcUK7hob8iRfl4JXbHtEKkoM2iIpY/a4YrA\nvjLBy0tNWnurTdT/Md7L3pqU4wVlMY0i5ysae7/vm2WV9ePdcHM1Oa4gFTSL3dW1\nLI3TLwxDD3vlqhP/Js3CMq7wtvTE1T1sWxjiLOPHAoGBAI9t49Bl8SOgAoZliARL\nyw5gE3l7siZdDbHRMCV+eWm4TSWBfEtwUnpzBFGFcfa2TXrL+ytf5j1WKjL9xZCT\nXfDz96eEXJ4qCRdARgYTIyxZ4t/h1Vrr9IhTkj2fuZ5ZQ0la/4Dg5ejf7Mdv5WvQ\n94PV8qf2UALJFNVvBEdDa+ltAoGAVjSYvhEtH4M254fQ3EYN+tF5GSPeG5FxUfmQ\n3EqZqPt/3jBRDwqN/Y99hcgvTycSENNGtHBvgJjq/rrhhbYMHeS13Mtx1kCrifHC\nbTwcsrB12iFgaP2/iqdI15HbeorTIYMpzgTAwp40EZYN2G61m6daA6Hwk7yevt6k\nHgSiBLsCgYEAoou8wIMi0+zyUgIo8MXYhexJFuYvEdHeyYvWN/FA4j/DZelixgW9\no4a0M9yOXStvjUUpG/TpaceJGqtwXk9bMBZML05fT7Im/3Qmh+44fF/a0q0Ard2N\nzdlIuaKlvcK5a6jD+mcN9vzOi4iuGUm1OhfTvHD/IS/rG4r6XhahHm4=\n-----END RSA PRIVATE KEY-----\n"}

View File

@ -27,9 +27,9 @@ const (
)
var (
keyRequest = csr.KeyRequest{
Algo: "rsa",
Size: 2048,
keyRequest = csr.BasicKeyRequest{
A: "rsa",
S: 2048,
}
CAConfig = csr.CAConfig{
PathLength: 1,
@ -249,33 +249,8 @@ func TestCreateSelfSignedCert(t *testing.T) {
// --- TEST: Create a self-signed certificate from a CSR. --- //
// Make the request we will use to generate the certificate.
keyRequest := csr.KeyRequest{
Algo: "rsa",
Size: 2048,
}
CAConfig := csr.CAConfig{
PathLength: 1,
Expiry: "1/1/2015",
}
request := csr.CertificateRequest{
CN: "example.com",
Names: []csr.Name{
{
C: "US",
ST: "California",
L: "San Francisco",
O: "Internet Widgets, LLC",
OU: "Certificate Authority",
},
},
Hosts: []string{"ca.example.com"},
KeyRequest: &keyRequest,
CA: &CAConfig,
}
// Generate a self-signed certificate from the request.
encodedCertFromCode, _, err := CreateSelfSignedCert(request)
encodedCertFromCode, _, err := CreateSelfSignedCert(CARequest)
checkError(err, t)
// Now compare to a pre-made certificate made using a JSON file with the

View File

@ -3,6 +3,7 @@
package config
import (
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
"time"
)
@ -10,19 +11,9 @@ import (
// signer. If PKCS11.Module is non-empty, PKCS11 signing will be used.
// Otherwise signing from a key file will be used.
type Config struct {
CACertFile string
CACertFile string
ResponderCertFile string
KeyFile string
Interval time.Duration
PKCS11 PKCS11Config
KeyFile string
Interval time.Duration
PKCS11 pkcs11key.Config
}
// PKCS11Config contains information specific to setting up a PKCS11 OCSP
// signer.
type PKCS11Config struct {
Module string
Token string
PIN string
Label string
}

View File

@ -20,7 +20,9 @@ import (
"github.com/letsencrypt/boulder/Godeps/_workspace/src/golang.org/x/crypto/ocsp"
)
var statusCode = map[string]int{
// StatusCode is a map between string statuses sent by cli/api
// to ocsp int statuses
var StatusCode = map[string]int{
"good": ocsp.Good,
"revoked": ocsp.Revoked,
"unknown": ocsp.Unknown,
@ -122,7 +124,7 @@ func (s StandardSigner) Sign(req SignRequest) ([]byte, error) {
thisUpdate := time.Now().Truncate(time.Hour)
nextUpdate := thisUpdate.Add(s.interval)
status, ok := statusCode[req.Status]
status, ok := StatusCode[req.Status]
if !ok {
return nil, cferr.New(cferr.OCSPError, cferr.InvalidStatus)
}

View File

@ -31,8 +31,11 @@ func NewPKCS11Signer(cfg ocspConfig.Config) (ocsp.Signer, error) {
}
PKCS11 := cfg.PKCS11
priv, err := pkcs11key.New(PKCS11.Module, PKCS11.Token, PKCS11.PIN,
PKCS11.Label)
priv, err := pkcs11key.New(
PKCS11.Module,
PKCS11.TokenLabel,
PKCS11.PIN,
PKCS11.PrivateKeyLabel)
if err != nil {
return nil, errors.New(errors.PrivateKeyError, errors.ReadFailed)
}

View File

@ -52,6 +52,10 @@ func NewSourceFromFile(responseFile string) (Source, error) {
responsesB64 := regexp.MustCompile("\\s").Split(string(fileContents), -1)
src := InMemorySource{}
for _, b64 := range responsesB64 {
// if the line/space is empty just skip
if b64 == "" {
continue
}
der, tmpErr := base64.StdEncoding.DecodeString(b64)
if tmpErr != nil {
log.Errorf("Base64 decode error on: %s", b64)

View File

@ -14,20 +14,11 @@ import (
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
)
// Config contains configuration information required to use a PKCS
// #11 key.
type Config struct {
Module string
Token string
PIN string
Label string
}
// Enabled is set to true if PKCS #11 support is present.
const Enabled = true
// New returns a new PKCS #11 signer.
func New(caCertFile string, policy *config.Signing, cfg *Config) (signer.Signer, error) {
func New(caCertFile string, policy *config.Signing, cfg *pkcs11key.Config) (signer.Signer, error) {
if cfg == nil {
return nil, errors.New(errors.PrivateKeyError, errors.ReadFailed)
}
@ -43,7 +34,7 @@ func New(caCertFile string, policy *config.Signing, cfg *Config) (signer.Signer,
return nil, err
}
priv, err := pkcs11key.New(cfg.Module, cfg.Token, cfg.PIN, cfg.Label)
priv, err := pkcs11key.New(cfg.Module, cfg.TokenLabel, cfg.PIN, cfg.PrivateKeyLabel)
if err != nil {
return nil, errors.New(errors.PrivateKeyError, errors.ReadFailed)
}

View File

@ -21,6 +21,7 @@ import (
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/info"
)
@ -147,7 +148,7 @@ func ParseCertificateRequest(s Signer, csrBytes []byte) (template *x509.Certific
return
}
err = CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
err = helpers.CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err)
return
@ -165,51 +166,6 @@ func ParseCertificateRequest(s Signer, csrBytes []byte) (template *x509.Certific
return
}
// CheckSignature verifies a signature made by the key on a CSR, such
// as on the CSR itself.
func CheckSignature(csr *x509.CertificateRequest, algo x509.SignatureAlgorithm, signed, signature []byte) error {
var hashType crypto.Hash
switch algo {
case x509.SHA1WithRSA, x509.ECDSAWithSHA1:
hashType = crypto.SHA1
case x509.SHA256WithRSA, x509.ECDSAWithSHA256:
hashType = crypto.SHA256
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
hashType = crypto.SHA384
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
hashType = crypto.SHA512
default:
return x509.ErrUnsupportedAlgorithm
}
if !hashType.Available() {
return x509.ErrUnsupportedAlgorithm
}
h := hashType.New()
h.Write(signed)
digest := h.Sum(nil)
switch pub := csr.PublicKey.(type) {
case *rsa.PublicKey:
return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
case *ecdsa.PublicKey:
ecdsaSig := new(struct{ R, S *big.Int })
if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
return err
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
return errors.New("x509: ECDSA signature contained zero or negative values")
}
if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
return errors.New("x509: ECDSA verification failure")
}
return nil
}
return x509.ErrUnsupportedAlgorithm
}
type subjectPublicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
SubjectPublicKey asn1.BitString

View File

@ -3,6 +3,7 @@ package universal
import (
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
@ -38,12 +39,12 @@ func fileBackedSigner(root *Root, policy *config.Signing) (signer.Signer, bool,
// options in the root.
func pkcs11Signer(root *Root, policy *config.Signing) (signer.Signer, bool, error) {
module := root.Config["pkcs11-module"]
token := root.Config["pkcs11-token"]
label := root.Config["pkcs11-label"]
tokenLabel := root.Config["pkcs11-token-label"]
privateKeyLabel := root.Config["pkcs11-private-key-label"]
userPIN := root.Config["pkcs11-user-pin"]
certFile := root.Config["cert-file"]
if module == "" && token == "" && label == "" && userPIN == "" {
if module == "" && tokenLabel == "" && privateKeyLabel == "" && userPIN == "" {
return nil, false, nil
}
@ -51,11 +52,11 @@ func pkcs11Signer(root *Root, policy *config.Signing) (signer.Signer, bool, erro
return nil, true, cferr.New(cferr.PrivateKeyError, cferr.Unavailable)
}
conf := pkcs11.Config{
Module: module,
Token: token,
Label: label,
PIN: userPIN,
conf := pkcs11key.Config{
Module: module,
TokenLabel: tokenLabel,
PrivateKeyLabel: privateKeyLabel,
PIN: userPIN,
}
s, err := pkcs11.New(certFile, policy, &conf)

View File

@ -75,4 +75,4 @@ rpm:
test/boulder-config.json sa/_db ca/_db $(foreach var,$(OBJECTS), $(OBJDIR)/$(var))
bin/pkcs11bench: pre
go test -o ./bin/pkcs11bench -c ./test/pkcs11bench/
go test -o ./bin/pkcs11bench -c ./Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key/

View File

@ -161,13 +161,12 @@ func loadKey(keyConfig cmd.KeyConfig) (priv crypto.Signer, err error) {
if pkcs11Config.Module == "" ||
pkcs11Config.TokenLabel == "" ||
pkcs11Config.PIN == "" ||
pkcs11Config.PrivateKeyLabel == "" ||
pkcs11Config.SlotID == nil {
pkcs11Config.PrivateKeyLabel == "" {
err = fmt.Errorf("Missing a field in pkcs11Config %#v", pkcs11Config)
return
}
priv, err = pkcs11key.New(pkcs11Config.Module,
pkcs11Config.TokenLabel, pkcs11Config.PIN, pkcs11Config.PrivateKeyLabel, *pkcs11Config.SlotID)
pkcs11Config.TokenLabel, pkcs11Config.PIN, pkcs11Config.PrivateKeyLabel)
return
}

View File

@ -237,7 +237,6 @@ type KeyConfig struct {
type PKCS11Config struct {
Module string
TokenLabel string
SlotID *int
PIN string
PrivateKeyLabel string
}

View File

@ -26,7 +26,6 @@ type PKCS11Config struct {
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) {
@ -159,7 +158,7 @@ func main() {
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))
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

View File

@ -56,14 +56,12 @@ func main() {
TokenLabel string
PIN string
PrivateKeyLabel string
SlotID float64
}
pkcs11Bytes, err := ioutil.ReadFile(pkcs11FileName)
panicOnError(err)
err = json.Unmarshal(pkcs11Bytes, &pkcs11)
panicOnError(err)
slotID := int(pkcs11.SlotID)
p11key, err := pkcs11key.New(pkcs11.Module, pkcs11.TokenLabel, pkcs11.PIN, pkcs11.PrivateKeyLabel, slotID)
p11key, err := pkcs11key.New(pkcs11.Module, pkcs11.TokenLabel, pkcs11.PIN, pkcs11.PrivateKeyLabel)
panicOnError(err)
// All of the certificates start and end at the same time

View File

@ -1,7 +1,6 @@
{
"Module": "/usr/local/Cellar/opensc/0.14.0_1/lib/opensc-pkcs11.so",
"TokenLabel": "PIV_II (PIV Card Holder pin)",
"SlotID": 1,
"PrivateKeyLabel": "SIGN key",
"PIN": "163246"
}

View File

@ -1,71 +0,0 @@
package pkcs11bench
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"flag"
"math/big"
"testing"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
)
var module = flag.String("module", "", "Path to PKCS11 module")
var tokenLabel = flag.String("tokenLabel", "", "Token label")
var pin = flag.String("pin", "", "PIN")
var privateKeyLabel = flag.String("privateKeyLabel", "", "Private key label")
var slotID = flag.Int("slotID", -1, "Slot")
// BenchmarkPKCS11 signs a certificate repeatedly using a PKCS11 token and
// measures speed. To run:
// go test -bench=. -benchtime 1m ./test/pkcs11bench/ \
// -module /usr/lib/softhsm/libsofthsm.so -token-label "softhsm token" \
// -pin 1234 -private-key-label "my key" -slot-id 7 -v
// You can adjust benchtime if you want to run for longer or shorter.
// TODO: Parallel benchmarking. Currently if you try this with a Yubikey Neo,
// you will get a bunch of CKR_USER_ALREADY_LOGGED_IN errors. This is because
// pkcs11key logs into the token before each signing operation (which is probably a
// performance bug). Also note that some PKCS11 modules (opensc) are not
// threadsafe.
func BenchmarkPKCS11(b *testing.B) {
if *module == "" || *tokenLabel == "" || *pin == "" || *privateKeyLabel == "" || *slotID == -1 {
b.Fatal("Must pass all flags: module, tokenLabel, pin, privateKeyLabel, and slotID")
return
}
// NOTE: To run this test, you will need to edit the following values to match
// your PKCS11 token.
p, err := pkcs11key.New(*module, *tokenLabel, *pin, *privateKeyLabel, *slotID)
if err != nil {
b.Fatal(err)
return
}
defer p.Destroy()
N := big.NewInt(1)
N.Lsh(N, 6000)
// A minimal, bogus certificate to be signed.
template := x509.Certificate{
SerialNumber: big.NewInt(1),
PublicKeyAlgorithm: x509.RSA,
NotBefore: time.Now(),
NotAfter: time.Now(),
PublicKey: &rsa.PublicKey{
N: N,
E: 1 << 17,
},
}
// Reset the benchmarking timer so we don't include setup time.
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err = x509.CreateCertificate(rand.Reader, &template, &template, template.PublicKey, p)
if err != nil {
b.Fatal(err)
return
}
}
}