diff --git a/ocsp/ocsp.go b/ocsp/ocsp.go index c022ea2..68e0235 100644 --- a/ocsp/ocsp.go +++ b/ocsp/ocsp.go @@ -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 - } + ret.TBSResponseData = basicResp.TBSResponseData.Raw + ret.Signature = basicResp.Signature.RightAlign() + ret.SignatureAlgorithm = getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm) - if ret.Certificate.PublicKeyAlgorithm != x509.RSA || !basicResp.SignatureAlgorithm.Algorithm.Equal(idSHA1WithRSA) { - return nil, x509.ErrUnsupportedAlgorithm - } + if len(basicResp.Certificates) > 0 { + ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes) + if err != nil { + return nil, err + } - hashType := crypto.SHA1 - h := hashType.New() - - pub := ret.Certificate.PublicKey.(*rsa.PublicKey) - h.Write(basicResp.TBSResponseData.Raw) - digest := h.Sum(nil) - signature := basicResp.Signature.RightAlign() - - 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, + }, + }, + }, + }, + }) +} diff --git a/ocsp/ocsp_test.go b/ocsp/ocsp_test.go index f0e9f94..26c0d23 100644 --- a/ocsp/ocsp_test.go +++ b/ocsp/ocsp_test.go @@ -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. @@ -6,7 +6,9 @@ package ocsp import ( "bytes" + "crypto/x509" "encoding/hex" + "math/big" "reflect" "testing" "time" @@ -21,7 +23,7 @@ func TestOCSPDecode(t *testing.T) { expected := Response{ Status: 0, - SerialNumber: []byte{0x1, 0xd0, 0xfa}, + SerialNumber: big.NewInt(0x1d0fa), RevocationReason: 0, ThisUpdate: time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC), NextUpdate: time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC), @@ -39,7 +41,7 @@ func TestOCSPDecode(t *testing.T) { t.Errorf("resp.Status: got %d, want %d", resp.Status, expected.Status) } - if !bytes.Equal(resp.SerialNumber, expected.SerialNumber) { + if resp.SerialNumber.Cmp(expected.SerialNumber) != 0 { t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, expected.SerialNumber) } @@ -48,6 +50,38 @@ func TestOCSPDecode(t *testing.T) { } } +func TestOCSPDecodeWithoutCert(t *testing.T) { + responseBytes, _ := hex.DecodeString(ocspResponseWithoutCertHex) + _, err := ParseResponse(responseBytes) + if err != nil { + t.Error(err) + } +} + +func TestOCSPRequest(t *testing.T) { + leafCert, _ := hex.DecodeString(leafCertHex) + cert, err := x509.ParseCertificate(leafCert) + if err != nil { + t.Fatal(err) + } + + issuerCert, _ := hex.DecodeString(issuerCertHex) + issuer, err := x509.ParseCertificate(issuerCert) + if err != nil { + t.Fatal(err) + } + + request, err := CreateRequest(cert, issuer, nil) + if err != nil { + t.Fatal(err) + } + + expected, _ := hex.DecodeString(ocspRequestHex) + if !bytes.Equal(request, expected) { + t.Errorf("got %x, wanted %x", request, expected) + } +} + // This OCSP response was taken from Thawte's public OCSP responder. // To recreate: // $ openssl s_client -tls1 -showcerts -servername www.google.com -connect www.google.com:443 @@ -105,3 +139,77 @@ const ocspResponseHex = "308206bc0a0100a08206b5308206b106092b0601050507300101048 "f763bf4d707bc8841d7ad9385ee2a4244469260b6f2bf085977af9074796048ecc2f9d48" + "a1d24ce16e41a9941568fec5b42771e118f16c106a54ccc339a4b02166445a167902e75e" + "6d8620b0825dcd18a069b90fd851d10fa8effd409deec02860d26d8d833f304b10669b42" + +const ocspResponseWithoutCertHex = "308201d40a0100a08201cd308201c906092b0601050507300101048201ba3082" + + "01b630819fa2160414884451ff502a695e2d88f421bad90cf2cecbea7c180f3230313330" + + "3631383037323434335a30743072304a300906052b0e03021a0500041448b60d38238df8" + + "456e4ee5843ea394111802979f0414884451ff502a695e2d88f421bad90cf2cecbea7c02" + + "1100f78b13b946fc9635d8ab49de9d2148218000180f3230313330363138303732343433" + + "5aa011180f32303133303632323037323434335a300d06092a864886f70d010105050003" + + "82010100103e18b3d297a5e7a6c07a4fc52ac46a15c0eba96f3be17f0ffe84de5b8c8e05" + + "5a8f577586a849dc4abd6440eb6fedde4622451e2823c1cbf3558b4e8184959c9fe96eff" + + "8bc5f95866c58c6d087519faabfdae37e11d9874f1bc0db292208f645dd848185e4dd38b" + + "6a8547dfa7b74d514a8470015719064d35476b95bebb03d4d2845c5ca15202d2784878f2" + + "0f904c24f09736f044609e9c271381713400e563023d212db422236440c6f377bbf24b2b" + + "9e7dec8698e36a8df68b7592ad3489fb2937afb90eb85d2aa96b81c94c25057dbd4759d9" + + "20a1a65c7f0b6427a224b3c98edd96b9b61f706099951188b0289555ad30a216fb774651" + + "5a35fca2e054dfa8" + +const ocspRequestHex = "30563054a003020100304d304b3049300906052b0e03021a05000414c0fe0278fc991888" + + "91b3f212e9c7e1b21ab7bfc004140dfc1df0a9e0f01ce7f2b213177e6f8d157cd4f60210" + + "017f77deb3bcbb235d44ccc7dba62e72" + +const leafCertHex = "308203c830820331a0030201020210017f77deb3bcbb235d44ccc7dba62e72300d06092a" + + "864886f70d01010505003081ba311f301d060355040a1316566572695369676e20547275" + + "7374204e6574776f726b31173015060355040b130e566572695369676e2c20496e632e31" + + "333031060355040b132a566572695369676e20496e7465726e6174696f6e616c20536572" + + "766572204341202d20436c617373203331493047060355040b13407777772e7665726973" + + "69676e2e636f6d2f43505320496e636f72702e6279205265662e204c494142494c495459" + + "204c54442e286329393720566572695369676e301e170d3132303632313030303030305a" + + "170d3133313233313233353935395a3068310b3009060355040613025553311330110603" + + "550408130a43616c69666f726e6961311230100603550407130950616c6f20416c746f31" + + "173015060355040a130e46616365626f6f6b2c20496e632e311730150603550403140e2a" + + "2e66616365626f6f6b2e636f6d30819f300d06092a864886f70d010101050003818d0030" + + "818902818100ae94b171e2deccc1693e051063240102e0689ae83c39b6b3e74b97d48d7b" + + "23689100b0b496ee62f0e6d356bcf4aa0f50643402f5d1766aa972835a7564723f39bbef" + + "5290ded9bcdbf9d3d55dfad23aa03dc604c54d29cf1d4b3bdbd1a809cfae47b44c7eae17" + + "c5109bee24a9cf4a8d911bb0fd0415ae4c3f430aa12a557e2ae10203010001a382011e30" + + "82011a30090603551d130402300030440603551d20043d303b3039060b6086480186f845" + + "01071703302a302806082b06010505070201161c68747470733a2f2f7777772e76657269" + + "7369676e2e636f6d2f727061303c0603551d1f043530333031a02fa02d862b687474703a" + + "2f2f535652496e746c2d63726c2e766572697369676e2e636f6d2f535652496e746c2e63" + + "726c301d0603551d250416301406082b0601050507030106082b06010505070302300b06" + + "03551d0f0404030205a0303406082b0601050507010104283026302406082b0601050507" + + "30018618687474703a2f2f6f6373702e766572697369676e2e636f6d30270603551d1104" + + "20301e820e2a2e66616365626f6f6b2e636f6d820c66616365626f6f6b2e636f6d300d06" + + "092a864886f70d0101050500038181005b6c2b75f8ed30aa51aad36aba595e555141951f" + + "81a53b447910ac1f76ff78fc2781616b58f3122afc1c87010425e9ed43df1a7ba6498060" + + "67e2688af03db58c7df4ee03309a6afc247ccb134dc33e54c6bc1d5133a532a73273b1d7" + + "9cadc08e7e1a83116d34523340b0305427a21742827c98916698ee7eaf8c3bdd71700817" + +const issuerCertHex = "30820383308202eca003020102021046fcebbab4d02f0f926098233f93078f300d06092a" + + "864886f70d0101050500305f310b300906035504061302555331173015060355040a130e" + + "566572695369676e2c20496e632e31373035060355040b132e436c617373203320507562" + + "6c6963205072696d6172792043657274696669636174696f6e20417574686f7269747930" + + "1e170d3937303431373030303030305a170d3136313032343233353935395a3081ba311f" + + "301d060355040a1316566572695369676e205472757374204e6574776f726b3117301506" + + "0355040b130e566572695369676e2c20496e632e31333031060355040b132a5665726953" + + "69676e20496e7465726e6174696f6e616c20536572766572204341202d20436c61737320" + + "3331493047060355040b13407777772e766572697369676e2e636f6d2f43505320496e63" + + "6f72702e6279205265662e204c494142494c495459204c54442e28632939372056657269" + + "5369676e30819f300d06092a864886f70d010101050003818d0030818902818100d88280" + + "e8d619027d1f85183925a2652be1bfd405d3bce6363baaf04c6c5bb6e7aa3c734555b2f1" + + "bdea9742ed9a340a15d4a95cf54025ddd907c132b2756cc4cabba3fe56277143aa63f530" + + "3e9328e5faf1093bf3b74d4e39f75c495ab8c11dd3b28afe70309542cbfe2b518b5a3c3a" + + "f9224f90b202a7539c4f34e7ab04b27b6f0203010001a381e33081e0300f0603551d1304" + + "0830060101ff02010030440603551d20043d303b3039060b6086480186f8450107010130" + + "2a302806082b06010505070201161c68747470733a2f2f7777772e766572697369676e2e" + + "636f6d2f43505330340603551d25042d302b06082b0601050507030106082b0601050507" + + "030206096086480186f8420401060a6086480186f845010801300b0603551d0f04040302" + + "0106301106096086480186f842010104040302010630310603551d1f042a30283026a024" + + "a0228620687474703a2f2f63726c2e766572697369676e2e636f6d2f706361332e63726c" + + "300d06092a864886f70d010105050003818100408e4997968a73dd8e4def3e61b7caa062" + + "adf40e0abb753de26ed82cc7bff4b98c369bcaa2d09c724639f6a682036511c4bcbf2da6" + + "f5d93b0ab598fab378b91ef22b4c62d5fdb27a1ddf33fd73f9a5d82d8c2aead1fcb028b6" + + "e94948134b838a1b487b24f738de6f4154b8ab576b06dfc7a2d4a9f6f136628088f28b75" + + "d68071"