|
|
@@ -1,4 +1,4 @@
|
|
|
-// Copyright 2010 The Go Authors. All rights reserved.
|
|
|
+// 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.
|
|
|
|
|
|
@@ -9,16 +9,15 @@ package ocsp
|
|
|
|
|
|
import (
|
|
|
"crypto"
|
|
|
- "crypto/rsa"
|
|
|
_ "crypto/sha1"
|
|
|
"crypto/x509"
|
|
|
"crypto/x509/pkix"
|
|
|
"encoding/asn1"
|
|
|
+ "math/big"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1})
|
|
|
-var idSHA1WithRSA = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 1, 5})
|
|
|
|
|
|
// These are internal structures that reflect the ASN.1 structure of an OCSP
|
|
|
// response. See RFC 2560, section 4.2.
|
|
|
@@ -36,7 +35,7 @@ type certID struct {
|
|
|
HashAlgorithm pkix.AlgorithmIdentifier
|
|
|
NameHash []byte
|
|
|
IssuerKeyHash []byte
|
|
|
- SerialNumber asn1.RawValue
|
|
|
+ SerialNumber *big.Int
|
|
|
}
|
|
|
|
|
|
type responseASN1 struct {
|
|
|
@@ -79,6 +78,53 @@ type revokedInfo struct {
|
|
|
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}
|
|
|
+)
|
|
|
+
|
|
|
+// 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 {
|
|
|
+ switch {
|
|
|
+ case oid.Equal(oidSignatureMD2WithRSA):
|
|
|
+ return x509.MD2WithRSA
|
|
|
+ case oid.Equal(oidSignatureMD5WithRSA):
|
|
|
+ return x509.MD5WithRSA
|
|
|
+ case oid.Equal(oidSignatureSHA1WithRSA):
|
|
|
+ return x509.SHA1WithRSA
|
|
|
+ case oid.Equal(oidSignatureSHA256WithRSA):
|
|
|
+ return x509.SHA256WithRSA
|
|
|
+ case oid.Equal(oidSignatureSHA384WithRSA):
|
|
|
+ return x509.SHA384WithRSA
|
|
|
+ case oid.Equal(oidSignatureSHA512WithRSA):
|
|
|
+ return x509.SHA512WithRSA
|
|
|
+ case oid.Equal(oidSignatureDSAWithSHA1):
|
|
|
+ return x509.DSAWithSHA1
|
|
|
+ case oid.Equal(oidSignatureDSAWithSHA256):
|
|
|
+ return x509.DSAWithSHA256
|
|
|
+ case oid.Equal(oidSignatureECDSAWithSHA1):
|
|
|
+ return x509.ECDSAWithSHA1
|
|
|
+ case oid.Equal(oidSignatureECDSAWithSHA256):
|
|
|
+ return x509.ECDSAWithSHA256
|
|
|
+ case oid.Equal(oidSignatureECDSAWithSHA384):
|
|
|
+ return x509.ECDSAWithSHA384
|
|
|
+ case oid.Equal(oidSignatureECDSAWithSHA512):
|
|
|
+ return x509.ECDSAWithSHA512
|
|
|
+ }
|
|
|
+ return x509.UnknownSignatureAlgorithm
|
|
|
+}
|
|
|
+
|
|
|
// This is the exposed reflection of the internal OCSP structures.
|
|
|
|
|
|
const (
|
|
|
@@ -96,10 +142,24 @@ const (
|
|
|
type Response struct {
|
|
|
// Status is one of {Good, Revoked, Unknown, ServerFailed}
|
|
|
Status int
|
|
|
- SerialNumber []byte
|
|
|
+ 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
|
|
|
+}
|
|
|
+
|
|
|
+// 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.
|
|
|
@@ -110,9 +170,9 @@ func (p ParseError) Error() string {
|
|
|
}
|
|
|
|
|
|
// ParseResponse parses an OCSP response in DER form. It only supports
|
|
|
-// responses for a single certificate and only those using RSA signatures.
|
|
|
-// Non-RSA responses will result in x509.ErrUnsupportedAlgorithm.
|
|
|
-// Signature errors or parse failures will result in a ParseError.
|
|
|
+// responses for a single certificate. If the response contains a certificate
|
|
|
+// then the signature over the response is checked. Invalid signatures or parse
|
|
|
+// failures will result in a ParseError.
|
|
|
func ParseResponse(bytes []byte) (*Response, error) {
|
|
|
var resp responseASN1
|
|
|
rest, err := asn1.Unmarshal(bytes, &resp)
|
|
|
@@ -139,7 +199,7 @@ func ParseResponse(bytes []byte) (*Response, error) {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- if len(basicResp.Certificates) != 1 {
|
|
|
+ if len(basicResp.Certificates) > 1 {
|
|
|
return nil, ParseError("OCSP response contains bad number of certificates")
|
|
|
}
|
|
|
|
|
|
@@ -147,30 +207,24 @@ func ParseResponse(bytes []byte) (*Response, error) {
|
|
|
return nil, ParseError("OCSP response contains bad number of responses")
|
|
|
}
|
|
|
|
|
|
- ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- if ret.Certificate.PublicKeyAlgorithm != x509.RSA || !basicResp.SignatureAlgorithm.Algorithm.Equal(idSHA1WithRSA) {
|
|
|
- return nil, x509.ErrUnsupportedAlgorithm
|
|
|
- }
|
|
|
-
|
|
|
- hashType := crypto.SHA1
|
|
|
- h := hashType.New()
|
|
|
+ ret.TBSResponseData = basicResp.TBSResponseData.Raw
|
|
|
+ ret.Signature = basicResp.Signature.RightAlign()
|
|
|
+ ret.SignatureAlgorithm = getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm)
|
|
|
|
|
|
- pub := ret.Certificate.PublicKey.(*rsa.PublicKey)
|
|
|
- h.Write(basicResp.TBSResponseData.Raw)
|
|
|
- digest := h.Sum(nil)
|
|
|
- signature := basicResp.Signature.RightAlign()
|
|
|
+ if len(basicResp.Certificates) > 0 {
|
|
|
+ ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
|
|
|
- if rsa.VerifyPKCS1v15(pub, hashType, digest, signature) != nil {
|
|
|
- return nil, ParseError("bad OCSP signature")
|
|
|
+ if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
|
|
|
+ return nil, ParseError("bad OCSP signature")
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
r := basicResp.TBSResponseData.Responses[0]
|
|
|
|
|
|
- ret.SerialNumber = r.CertID.SerialNumber.Bytes
|
|
|
+ ret.SerialNumber = r.CertID.SerialNumber
|
|
|
|
|
|
switch {
|
|
|
case bool(r.Good):
|
|
|
@@ -189,3 +243,94 @@ func ParseResponse(bytes []byte) (*Response, error) {
|
|
|
|
|
|
return ret, nil
|
|
|
}
|
|
|
+
|
|
|
+// 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"`
|
|
|
+ RequestList []request
|
|
|
+}
|
|
|
+
|
|
|
+type request struct {
|
|
|
+ Cert certID
|
|
|
+}
|
|
|
+
|
|
|
+// RequestOptions contains options for constructing OCSP requests.
|
|
|
+type RequestOptions struct {
|
|
|
+ // Hash contains the hash function that should be used when
|
|
|
+ // constructing the OCSP request.
|
|
|
+ Hash crypto.Hash
|
|
|
+}
|
|
|
+
|
|
|
+func (opts *RequestOptions) hash() crypto.Hash {
|
|
|
+ if opts == nil {
|
|
|
+ // 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
|
|
|
+ switch hashFunc {
|
|
|
+ case crypto.SHA1:
|
|
|
+ hashOID = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26})
|
|
|
+ case crypto.SHA256:
|
|
|
+ hashOID = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1})
|
|
|
+ case crypto.SHA384:
|
|
|
+ hashOID = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2})
|
|
|
+ case crypto.SHA512:
|
|
|
+ hashOID = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3})
|
|
|
+ default:
|
|
|
+ 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,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ })
|
|
|
+}
|