a685e3fc98
Vndr has a simpler configuration and allows pointing to forked packages. Additionally other docker projects are now using vndr making vendoring in distribution more consistent. Updates letsencrypt to use fork. No longer uses sub-vendored packages. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
593 lines
18 KiB
Go
593 lines
18 KiB
Go
// Copyright 2013 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package ocsp parses OCSP responses as specified in RFC 2560. OCSP responses
|
|
// are signed messages attesting to the validity of a certificate for a small
|
|
// period of time. This is used to manage revocation for X.509 certificates.
|
|
package ocsp // import "golang.org/x/crypto/ocsp"
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha1"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"errors"
|
|
"math/big"
|
|
"time"
|
|
)
|
|
|
|
var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1})
|
|
|
|
// These are internal structures that reflect the ASN.1 structure of an OCSP
|
|
// response. See RFC 2560, section 4.2.
|
|
|
|
const (
|
|
ocspSuccess = 0
|
|
ocspMalformed = 1
|
|
ocspInternalError = 2
|
|
ocspTryLater = 3
|
|
ocspSigRequired = 4
|
|
ocspUnauthorized = 5
|
|
)
|
|
|
|
type certID struct {
|
|
HashAlgorithm pkix.AlgorithmIdentifier
|
|
NameHash []byte
|
|
IssuerKeyHash []byte
|
|
SerialNumber *big.Int
|
|
}
|
|
|
|
// https://tools.ietf.org/html/rfc2560#section-4.1.1
|
|
type ocspRequest struct {
|
|
TBSRequest tbsRequest
|
|
}
|
|
|
|
type tbsRequest struct {
|
|
Version int `asn1:"explicit,tag:0,default:0,optional"`
|
|
RequestorName pkix.RDNSequence `asn1:"explicit,tag:1,optional"`
|
|
RequestList []request
|
|
}
|
|
|
|
type request struct {
|
|
Cert certID
|
|
}
|
|
|
|
type responseASN1 struct {
|
|
Status asn1.Enumerated
|
|
Response responseBytes `asn1:"explicit,tag:0"`
|
|
}
|
|
|
|
type responseBytes struct {
|
|
ResponseType asn1.ObjectIdentifier
|
|
Response []byte
|
|
}
|
|
|
|
type basicResponse struct {
|
|
TBSResponseData responseData
|
|
SignatureAlgorithm pkix.AlgorithmIdentifier
|
|
Signature asn1.BitString
|
|
Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
|
|
}
|
|
|
|
type responseData struct {
|
|
Raw asn1.RawContent
|
|
Version int `asn1:"optional,default:1,explicit,tag:0"`
|
|
RawResponderName asn1.RawValue `asn1:"optional,explicit,tag:1"`
|
|
KeyHash []byte `asn1:"optional,explicit,tag:2"`
|
|
ProducedAt time.Time `asn1:"generalized"`
|
|
Responses []singleResponse
|
|
}
|
|
|
|
type singleResponse struct {
|
|
CertID certID
|
|
Good asn1.Flag `asn1:"tag:0,optional"`
|
|
Revoked revokedInfo `asn1:"explicit,tag:1,optional"`
|
|
Unknown asn1.Flag `asn1:"tag:2,optional"`
|
|
ThisUpdate time.Time `asn1:"generalized"`
|
|
NextUpdate time.Time `asn1:"generalized,explicit,tag:0,optional"`
|
|
}
|
|
|
|
type revokedInfo struct {
|
|
RevocationTime time.Time `asn1:"generalized"`
|
|
Reason int `asn1:"explicit,tag:0,optional"`
|
|
}
|
|
|
|
var (
|
|
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
|
|
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
|
|
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
|
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
|
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
|
|
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
|
|
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
|
|
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}
|
|
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
|
|
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
|
|
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
|
|
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
|
|
)
|
|
|
|
var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{
|
|
crypto.SHA1: asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}),
|
|
crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}),
|
|
crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}),
|
|
crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}),
|
|
}
|
|
|
|
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
|
|
var signatureAlgorithmDetails = []struct {
|
|
algo x509.SignatureAlgorithm
|
|
oid asn1.ObjectIdentifier
|
|
pubKeyAlgo x509.PublicKeyAlgorithm
|
|
hash crypto.Hash
|
|
}{
|
|
{x509.MD2WithRSA, oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
|
|
{x509.MD5WithRSA, oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
|
|
{x509.SHA1WithRSA, oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
|
|
{x509.SHA256WithRSA, oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
|
|
{x509.SHA384WithRSA, oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
|
|
{x509.SHA512WithRSA, oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
|
|
{x509.DSAWithSHA1, oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
|
|
{x509.DSAWithSHA256, oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
|
|
{x509.ECDSAWithSHA1, oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
|
|
{x509.ECDSAWithSHA256, oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
|
|
{x509.ECDSAWithSHA384, oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
|
|
{x509.ECDSAWithSHA512, oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
|
|
}
|
|
|
|
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
|
|
func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) {
|
|
var pubType x509.PublicKeyAlgorithm
|
|
|
|
switch pub := pub.(type) {
|
|
case *rsa.PublicKey:
|
|
pubType = x509.RSA
|
|
hashFunc = crypto.SHA256
|
|
sigAlgo.Algorithm = oidSignatureSHA256WithRSA
|
|
sigAlgo.Parameters = asn1.RawValue{
|
|
Tag: 5,
|
|
}
|
|
|
|
case *ecdsa.PublicKey:
|
|
pubType = x509.ECDSA
|
|
|
|
switch pub.Curve {
|
|
case elliptic.P224(), elliptic.P256():
|
|
hashFunc = crypto.SHA256
|
|
sigAlgo.Algorithm = oidSignatureECDSAWithSHA256
|
|
case elliptic.P384():
|
|
hashFunc = crypto.SHA384
|
|
sigAlgo.Algorithm = oidSignatureECDSAWithSHA384
|
|
case elliptic.P521():
|
|
hashFunc = crypto.SHA512
|
|
sigAlgo.Algorithm = oidSignatureECDSAWithSHA512
|
|
default:
|
|
err = errors.New("x509: unknown elliptic curve")
|
|
}
|
|
|
|
default:
|
|
err = errors.New("x509: only RSA and ECDSA keys supported")
|
|
}
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if requestedSigAlgo == 0 {
|
|
return
|
|
}
|
|
|
|
found := false
|
|
for _, details := range signatureAlgorithmDetails {
|
|
if details.algo == requestedSigAlgo {
|
|
if details.pubKeyAlgo != pubType {
|
|
err = errors.New("x509: requested SignatureAlgorithm does not match private key type")
|
|
return
|
|
}
|
|
sigAlgo.Algorithm, hashFunc = details.oid, details.hash
|
|
if hashFunc == 0 {
|
|
err = errors.New("x509: cannot sign with hash function requested")
|
|
return
|
|
}
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
err = errors.New("x509: unknown SignatureAlgorithm")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// TODO(agl): this is taken from crypto/x509 and so should probably be exported
|
|
// from crypto/x509 or crypto/x509/pkix.
|
|
func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgorithm {
|
|
for _, details := range signatureAlgorithmDetails {
|
|
if oid.Equal(details.oid) {
|
|
return details.algo
|
|
}
|
|
}
|
|
return x509.UnknownSignatureAlgorithm
|
|
}
|
|
|
|
// TODO(rlb): This is not taken from crypto/x509, but it's of the same general form.
|
|
func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash {
|
|
for hash, oid := range hashOIDs {
|
|
if oid.Equal(target) {
|
|
return hash
|
|
}
|
|
}
|
|
return crypto.Hash(0)
|
|
}
|
|
|
|
// This is the exposed reflection of the internal OCSP structures.
|
|
|
|
const (
|
|
// Good means that the certificate is valid.
|
|
Good = iota
|
|
// Revoked means that the certificate has been deliberately revoked.
|
|
Revoked = iota
|
|
// Unknown means that the OCSP responder doesn't know about the certificate.
|
|
Unknown = iota
|
|
// ServerFailed means that the OCSP responder failed to process the request.
|
|
ServerFailed = iota
|
|
)
|
|
|
|
// Request represents an OCSP request. See RFC 2560.
|
|
type Request struct {
|
|
HashAlgorithm crypto.Hash
|
|
IssuerNameHash []byte
|
|
IssuerKeyHash []byte
|
|
SerialNumber *big.Int
|
|
}
|
|
|
|
// Response represents an OCSP response. See RFC 2560.
|
|
type Response struct {
|
|
// Status is one of {Good, Revoked, Unknown, ServerFailed}
|
|
Status int
|
|
SerialNumber *big.Int
|
|
ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time
|
|
RevocationReason int
|
|
Certificate *x509.Certificate
|
|
// TBSResponseData contains the raw bytes of the signed response. If
|
|
// Certificate is nil then this can be used to verify Signature.
|
|
TBSResponseData []byte
|
|
Signature []byte
|
|
SignatureAlgorithm x509.SignatureAlgorithm
|
|
}
|
|
|
|
// These are pre-serialized error responses for the various non-success codes
|
|
// defined by OCSP. The Unauthorized code in particular can be used by an OCSP
|
|
// responder that supports only pre-signed responses as a response to requests
|
|
// for certificates with unknown status. See RFC 5019.
|
|
var (
|
|
MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
|
|
InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
|
|
TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
|
|
SigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
|
|
UnauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
|
|
)
|
|
|
|
// CheckSignatureFrom checks that the signature in resp is a valid signature
|
|
// from issuer. This should only be used if resp.Certificate is nil. Otherwise,
|
|
// the OCSP response contained an intermediate certificate that created the
|
|
// signature. That signature is checked by ParseResponse and only
|
|
// resp.Certificate remains to be validated.
|
|
func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error {
|
|
return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature)
|
|
}
|
|
|
|
// ParseError results from an invalid OCSP response.
|
|
type ParseError string
|
|
|
|
func (p ParseError) Error() string {
|
|
return string(p)
|
|
}
|
|
|
|
// ParseRequest parses an OCSP request in DER form. It only supports
|
|
// requests for a single certificate. Signed requests are not supported.
|
|
// If a request includes a signature, it will result in a ParseError.
|
|
func ParseRequest(bytes []byte) (*Request, error) {
|
|
var req ocspRequest
|
|
rest, err := asn1.Unmarshal(bytes, &req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(rest) > 0 {
|
|
return nil, ParseError("trailing data in OCSP request")
|
|
}
|
|
|
|
if len(req.TBSRequest.RequestList) == 0 {
|
|
return nil, ParseError("OCSP request contains no request body")
|
|
}
|
|
innerRequest := req.TBSRequest.RequestList[0]
|
|
|
|
hashFunc := getHashAlgorithmFromOID(innerRequest.Cert.HashAlgorithm.Algorithm)
|
|
if hashFunc == crypto.Hash(0) {
|
|
return nil, ParseError("OCSP request uses unknown hash function")
|
|
}
|
|
|
|
return &Request{
|
|
HashAlgorithm: hashFunc,
|
|
IssuerNameHash: innerRequest.Cert.NameHash,
|
|
IssuerKeyHash: innerRequest.Cert.IssuerKeyHash,
|
|
SerialNumber: innerRequest.Cert.SerialNumber,
|
|
}, nil
|
|
}
|
|
|
|
// ParseResponse parses an OCSP response in DER form. It only supports
|
|
// responses for a single certificate. If the response contains a certificate
|
|
// then the signature over the response is checked. If issuer is not nil then
|
|
// it will be used to validate the signature or embedded certificate. Invalid
|
|
// signatures or parse failures will result in a ParseError.
|
|
func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
|
|
var resp responseASN1
|
|
rest, err := asn1.Unmarshal(bytes, &resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(rest) > 0 {
|
|
return nil, ParseError("trailing data in OCSP response")
|
|
}
|
|
|
|
ret := new(Response)
|
|
if resp.Status != ocspSuccess {
|
|
ret.Status = ServerFailed
|
|
return ret, nil
|
|
}
|
|
|
|
if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) {
|
|
return nil, ParseError("bad OCSP response type")
|
|
}
|
|
|
|
var basicResp basicResponse
|
|
rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(basicResp.Certificates) > 1 {
|
|
return nil, ParseError("OCSP response contains bad number of certificates")
|
|
}
|
|
|
|
if len(basicResp.TBSResponseData.Responses) != 1 {
|
|
return nil, ParseError("OCSP response contains bad number of responses")
|
|
}
|
|
|
|
ret.TBSResponseData = basicResp.TBSResponseData.Raw
|
|
ret.Signature = basicResp.Signature.RightAlign()
|
|
ret.SignatureAlgorithm = getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm)
|
|
|
|
if len(basicResp.Certificates) > 0 {
|
|
ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
|
|
return nil, ParseError("bad OCSP signature")
|
|
}
|
|
|
|
if issuer != nil {
|
|
if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil {
|
|
return nil, ParseError("bad signature on embedded certificate")
|
|
}
|
|
}
|
|
} else if issuer != nil {
|
|
if err := ret.CheckSignatureFrom(issuer); err != nil {
|
|
return nil, ParseError("bad OCSP signature")
|
|
}
|
|
}
|
|
|
|
r := basicResp.TBSResponseData.Responses[0]
|
|
|
|
ret.SerialNumber = r.CertID.SerialNumber
|
|
|
|
switch {
|
|
case bool(r.Good):
|
|
ret.Status = Good
|
|
case bool(r.Unknown):
|
|
ret.Status = Unknown
|
|
default:
|
|
ret.Status = Revoked
|
|
ret.RevokedAt = r.Revoked.RevocationTime
|
|
ret.RevocationReason = r.Revoked.Reason
|
|
}
|
|
|
|
ret.ProducedAt = basicResp.TBSResponseData.ProducedAt
|
|
ret.ThisUpdate = r.ThisUpdate
|
|
ret.NextUpdate = r.NextUpdate
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
// RequestOptions contains options for constructing OCSP requests.
|
|
type RequestOptions struct {
|
|
// Hash contains the hash function that should be used when
|
|
// constructing the OCSP request. If zero, SHA-1 will be used.
|
|
Hash crypto.Hash
|
|
}
|
|
|
|
func (opts *RequestOptions) hash() crypto.Hash {
|
|
if opts == nil || opts.Hash == 0 {
|
|
// SHA-1 is nearly universally used in OCSP.
|
|
return crypto.SHA1
|
|
}
|
|
return opts.Hash
|
|
}
|
|
|
|
// CreateRequest returns a DER-encoded, OCSP request for the status of cert. If
|
|
// opts is nil then sensible defaults are used.
|
|
func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte, error) {
|
|
hashFunc := opts.hash()
|
|
|
|
// OCSP seems to be the only place where these raw hash identifiers are
|
|
// used. I took the following from
|
|
// http://msdn.microsoft.com/en-us/library/ff635603.aspx
|
|
var hashOID asn1.ObjectIdentifier
|
|
hashOID, ok := hashOIDs[hashFunc]
|
|
if !ok {
|
|
return nil, x509.ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
if !hashFunc.Available() {
|
|
return nil, x509.ErrUnsupportedAlgorithm
|
|
}
|
|
h := opts.hash().New()
|
|
|
|
var publicKeyInfo struct {
|
|
Algorithm pkix.AlgorithmIdentifier
|
|
PublicKey asn1.BitString
|
|
}
|
|
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h.Write(publicKeyInfo.PublicKey.RightAlign())
|
|
issuerKeyHash := h.Sum(nil)
|
|
|
|
h.Reset()
|
|
h.Write(issuer.RawSubject)
|
|
issuerNameHash := h.Sum(nil)
|
|
|
|
return asn1.Marshal(ocspRequest{
|
|
tbsRequest{
|
|
Version: 0,
|
|
RequestList: []request{
|
|
{
|
|
Cert: certID{
|
|
pkix.AlgorithmIdentifier{
|
|
Algorithm: hashOID,
|
|
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
|
|
},
|
|
issuerNameHash,
|
|
issuerKeyHash,
|
|
cert.SerialNumber,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// CreateResponse returns a DER-encoded OCSP response with the specified contents.
|
|
// The fields in the response are populated as follows:
|
|
//
|
|
// The responder cert is used to populate the ResponderName field, and the certificate
|
|
// itself is provided alongside the OCSP response signature.
|
|
//
|
|
// The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields.
|
|
// (SHA-1 is used for the hash function; this is not configurable.)
|
|
//
|
|
// The template is used to populate the SerialNumber, RevocationStatus, RevokedAt,
|
|
// RevocationReason, ThisUpdate, and NextUpdate fields.
|
|
//
|
|
// The ProducedAt date is automatically set to the current date, to the nearest minute.
|
|
func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer) ([]byte, error) {
|
|
var publicKeyInfo struct {
|
|
Algorithm pkix.AlgorithmIdentifier
|
|
PublicKey asn1.BitString
|
|
}
|
|
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h := sha1.New()
|
|
h.Write(publicKeyInfo.PublicKey.RightAlign())
|
|
issuerKeyHash := h.Sum(nil)
|
|
|
|
h.Reset()
|
|
h.Write(issuer.RawSubject)
|
|
issuerNameHash := h.Sum(nil)
|
|
|
|
innerResponse := singleResponse{
|
|
CertID: certID{
|
|
HashAlgorithm: pkix.AlgorithmIdentifier{
|
|
Algorithm: hashOIDs[crypto.SHA1],
|
|
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
|
|
},
|
|
NameHash: issuerNameHash,
|
|
IssuerKeyHash: issuerKeyHash,
|
|
SerialNumber: template.SerialNumber,
|
|
},
|
|
ThisUpdate: template.ThisUpdate.UTC(),
|
|
NextUpdate: template.NextUpdate.UTC(),
|
|
}
|
|
|
|
switch template.Status {
|
|
case Good:
|
|
innerResponse.Good = true
|
|
case Unknown:
|
|
innerResponse.Unknown = true
|
|
case Revoked:
|
|
innerResponse.Revoked = revokedInfo{
|
|
RevocationTime: template.RevokedAt.UTC(),
|
|
Reason: template.RevocationReason,
|
|
}
|
|
}
|
|
|
|
responderName := asn1.RawValue{
|
|
Class: 2, // context-specific
|
|
Tag: 1, // explicit tag
|
|
IsCompound: true,
|
|
Bytes: responderCert.RawSubject,
|
|
}
|
|
tbsResponseData := responseData{
|
|
Version: 0,
|
|
RawResponderName: responderName,
|
|
ProducedAt: time.Now().Truncate(time.Minute).UTC(),
|
|
Responses: []singleResponse{innerResponse},
|
|
}
|
|
|
|
tbsResponseDataDER, err := asn1.Marshal(tbsResponseData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
responseHash := hashFunc.New()
|
|
responseHash.Write(tbsResponseDataDER)
|
|
signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response := basicResponse{
|
|
TBSResponseData: tbsResponseData,
|
|
SignatureAlgorithm: signatureAlgorithm,
|
|
Signature: asn1.BitString{
|
|
Bytes: signature,
|
|
BitLength: 8 * len(signature),
|
|
},
|
|
}
|
|
if template.Certificate != nil {
|
|
response.Certificates = []asn1.RawValue{
|
|
asn1.RawValue{FullBytes: template.Certificate.Raw},
|
|
}
|
|
}
|
|
responseDER, err := asn1.Marshal(response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return asn1.Marshal(responseASN1{
|
|
Status: ocspSuccess,
|
|
Response: responseBytes{
|
|
ResponseType: idPKIXOCSPBasic,
|
|
Response: responseDER,
|
|
},
|
|
})
|
|
}
|