Skip to content

Commit 8286ee4

Browse files
committed
crypto/ocsp: add package to parse OCSP responses.
OCSP is the preferred X.509 revocation mechanism. X.509 certificates can contain a URL from which can be fetched a signed response saying "this certificate is valid until $x" (where $x is usually 7 days in the future). These are called OCSP responses and they can also be included in the TLS handshake itself ("OCSP stapling") R=rsc, r CC=golang-dev https://golang.org/cl/1875043
1 parent 8975d36 commit 8286ee4

File tree

2 files changed

+300
-0
lines changed

2 files changed

+300
-0
lines changed

src/pkg/crypto/ocsp/ocsp.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// Copyright 2010 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// This package parses OCSP responses as specified in RFC 2560. OCSP responses
6+
// are signed messages attesting to the validity of a certificate for a small
7+
// period of time. This is used to manage revocation for X.509 certificates.
8+
package ocsp
9+
10+
import (
11+
"asn1"
12+
"crypto/rsa"
13+
"crypto/sha1"
14+
"crypto/x509"
15+
"os"
16+
"time"
17+
)
18+
19+
var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1})
20+
var idSHA1WithRSA = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 1, 5})
21+
22+
// These are internal structures that reflect the ASN.1 structure of an OCSP
23+
// response. See RFC 2560, section 4.2.
24+
25+
const (
26+
ocspSuccess = 0
27+
ocspMalformed = 1
28+
ocspInternalError = 2
29+
ocspTryLater = 3
30+
ocspSigRequired = 4
31+
ocspUnauthorized = 5
32+
)
33+
34+
type rdnSequence []relativeDistinguishedNameSET
35+
36+
type relativeDistinguishedNameSET []attributeTypeAndValue
37+
38+
type attributeTypeAndValue struct {
39+
Type asn1.ObjectIdentifier
40+
Value interface{}
41+
}
42+
43+
type algorithmIdentifier struct {
44+
Algorithm asn1.ObjectIdentifier
45+
}
46+
47+
type certID struct {
48+
HashAlgorithm algorithmIdentifier
49+
NameHash []byte
50+
IssuerKeyHash []byte
51+
SerialNumber asn1.RawValue
52+
}
53+
54+
type responseASN1 struct {
55+
Status asn1.Enumerated
56+
Response responseBytes "explicit,tag:0"
57+
}
58+
59+
type responseBytes struct {
60+
ResponseType asn1.ObjectIdentifier
61+
Response []byte
62+
}
63+
64+
type basicResponse struct {
65+
TBSResponseData responseData
66+
SignatureAlgorithm algorithmIdentifier
67+
Signature asn1.BitString
68+
Certificates []asn1.RawValue "explicit,tag:0,optional"
69+
}
70+
71+
type responseData struct {
72+
Raw asn1.RawContent
73+
Version int "optional,default:1,explicit,tag:0"
74+
RequestorName rdnSequence "optional,explicit,tag:1"
75+
KeyHash []byte "optional,explicit,tag:2"
76+
ProducedAt *time.Time
77+
Responses []singleResponse
78+
}
79+
80+
type singleResponse struct {
81+
CertID certID
82+
Good asn1.Flag "explicit,tag:0,optional"
83+
Revoked revokedInfo "explicit,tag:1,optional"
84+
Unknown asn1.Flag "explicit,tag:2,optional"
85+
ThisUpdate *time.Time
86+
NextUpdate *time.Time "explicit,tag:0,optional"
87+
}
88+
89+
type revokedInfo struct {
90+
RevocationTime *time.Time
91+
Reason int "explicit,tag:0,optional"
92+
}
93+
94+
// This is the exposed reflection of the internal OCSP structures.
95+
96+
const (
97+
// Good means that the certificate is valid.
98+
Good = iota
99+
// Revoked means that the certificate has been deliberately revoked.
100+
Revoked = iota
101+
// Unknown means that the OCSP responder doesn't know about the certificate.
102+
Unknown = iota
103+
// ServerFailed means that the OCSP responder failed to process the request.
104+
ServerFailed = iota
105+
)
106+
107+
// Response represents an OCSP response. See RFC 2560.
108+
type Response struct {
109+
// Status is one of {Good, Revoked, Unknown, ServerFailed}
110+
Status int
111+
SerialNumber []byte
112+
ProducedAt, ThisUpdate, NextUpdate, RevokedAt *time.Time
113+
RevocationReason int
114+
Certificate *x509.Certificate
115+
}
116+
117+
// ParseError results from an invalid OCSP response.
118+
type ParseError string
119+
120+
func (p ParseError) String() string {
121+
return string(p)
122+
}
123+
124+
// ParseResponse parses an OCSP response in DER form. It only supports
125+
// responses for a single certificate and only those using RSA signatures.
126+
// Non-RSA responses will result in an x509.UnsupportedAlgorithmError.
127+
// Signature errors or parse failures will result in a ParseError.
128+
func ParseResponse(bytes []byte) (*Response, os.Error) {
129+
var resp responseASN1
130+
rest, err := asn1.Unmarshal(&resp, bytes)
131+
if err != nil {
132+
return nil, err
133+
}
134+
if len(rest) > 0 {
135+
return nil, ParseError("trailing data in OCSP response")
136+
}
137+
138+
ret := new(Response)
139+
if resp.Status != ocspSuccess {
140+
ret.Status = ServerFailed
141+
return ret, nil
142+
}
143+
144+
if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) {
145+
return nil, ParseError("bad OCSP response type")
146+
}
147+
148+
var basicResp basicResponse
149+
rest, err = asn1.Unmarshal(&basicResp, resp.Response.Response)
150+
if err != nil {
151+
return nil, err
152+
}
153+
154+
if len(basicResp.Certificates) != 1 {
155+
return nil, ParseError("OCSP response contains bad number of certificates")
156+
}
157+
158+
if len(basicResp.TBSResponseData.Responses) != 1 {
159+
return nil, ParseError("OCSP response contains bad number of responses")
160+
}
161+
162+
ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
163+
if err != nil {
164+
return nil, err
165+
}
166+
167+
if ret.Certificate.PublicKeyAlgorithm != x509.RSA || !basicResp.SignatureAlgorithm.Algorithm.Equal(idSHA1WithRSA) {
168+
return nil, x509.UnsupportedAlgorithmError{}
169+
}
170+
171+
h := sha1.New()
172+
hashType := rsa.HashSHA1
173+
174+
pub := ret.Certificate.PublicKey.(*rsa.PublicKey)
175+
h.Write(basicResp.TBSResponseData.Raw)
176+
digest := h.Sum()
177+
signature := basicResp.Signature.RightAlign()
178+
179+
if rsa.VerifyPKCS1v15(pub, hashType, digest, signature) != nil {
180+
return nil, ParseError("bad OCSP signature")
181+
}
182+
183+
r := basicResp.TBSResponseData.Responses[0]
184+
185+
ret.SerialNumber = r.CertID.SerialNumber.Bytes
186+
187+
switch {
188+
case bool(r.Good):
189+
ret.Status = Good
190+
case bool(r.Unknown):
191+
ret.Status = Unknown
192+
default:
193+
ret.Status = Revoked
194+
ret.RevokedAt = r.Revoked.RevocationTime
195+
ret.RevocationReason = r.Revoked.Reason
196+
}
197+
198+
ret.ProducedAt = basicResp.TBSResponseData.ProducedAt
199+
ret.ThisUpdate = r.ThisUpdate
200+
ret.NextUpdate = r.NextUpdate
201+
202+
return ret, nil
203+
}

src/pkg/crypto/ocsp/ocsp_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package ocsp
2+
3+
import (
4+
"bytes"
5+
"encoding/hex"
6+
"reflect"
7+
"testing"
8+
"time"
9+
)
10+
11+
func TestOCSPDecode(t *testing.T) {
12+
responseBytes, _ := hex.DecodeString(ocspResponseHex)
13+
resp, err := ParseResponse(responseBytes)
14+
if err != nil {
15+
t.Error(err)
16+
}
17+
18+
expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}}
19+
20+
if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) {
21+
t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate)
22+
}
23+
24+
if !reflect.DeepEqual(resp.NextUpdate, resp.NextUpdate) {
25+
t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, expected.NextUpdate)
26+
}
27+
28+
if resp.Status != expected.Status {
29+
t.Errorf("resp.Status: got %d, want %d", resp.Status, expected.Status)
30+
}
31+
32+
if !bytes.Equal(resp.SerialNumber, expected.SerialNumber) {
33+
t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, expected.SerialNumber)
34+
}
35+
36+
if resp.RevocationReason != expected.RevocationReason {
37+
t.Errorf("resp.RevocationReason: got %d, want %d", resp.RevocationReason, expected.RevocationReason)
38+
}
39+
}
40+
41+
// This OCSP response was taken from Thawte's public OCSP responder.
42+
// To recreate:
43+
// $ openssl s_client -tls1 -showcerts -servername www.google.com -connect www.google.com:443
44+
// Copy and paste the first certificate into /tmp/cert.crt and the second into
45+
// /tmp/intermediate.crt
46+
// $ openssl ocsp -issuer /tmp/intermediate.crt -cert /tmp/cert.crt -url http://ocsp.thawte.com -resp_text -respout /tmp/ocsp.der
47+
// Then hex encode the result:
48+
// $ python -c 'print file("/tmp/ocsp.der", "r").read().encode("hex")'
49+
50+
const ocspResponseHex = "308206bc0a0100a08206b5308206b106092b0601050507300101048206a23082069e3081" +
51+
"c9a14e304c310b300906035504061302494c31163014060355040a130d5374617274436f" +
52+
"6d204c74642e312530230603550403131c5374617274436f6d20436c6173732031204f43" +
53+
"5350205369676e6572180f32303130303730373137333531375a30663064303c30090605" +
54+
"2b0e03021a050004146568874f40750f016a3475625e1f5c93e5a26d580414eb4234d098" +
55+
"b0ab9ff41b6b08f7cc642eef0e2c45020301d0fa8000180f323031303037303731353031" +
56+
"30355aa011180f32303130303730373138333531375a300d06092a864886f70d01010505" +
57+
"000382010100ab557ff070d1d7cebbb5f0ec91a15c3fed22eb2e1b8244f1b84545f013a4" +
58+
"fb46214c5e3fbfbebb8a56acc2b9db19f68fd3c3201046b3824d5ba689f99864328710cb" +
59+
"467195eb37d84f539e49f859316b32964dc3e47e36814ce94d6c56dd02733b1d0802f7ff" +
60+
"4eebdbbd2927dcf580f16cbc290f91e81b53cb365e7223f1d6e20a88ea064104875e0145" +
61+
"672b20fc14829d51ca122f5f5d77d3ad6c83889c55c7dc43680ba2fe3cef8b05dbcabdc0" +
62+
"d3e09aaf9725597f8c858c2fa38c0d6aed2e6318194420dd1a1137445d13e1c97ab47896" +
63+
"17a4e08925f46f867b72e3a4dc1f08cb870b2b0717f7207faa0ac512e628a029aba7457a" +
64+
"e63dcf3281e2162d9349a08204ba308204b6308204b23082039aa003020102020101300d" +
65+
"06092a864886f70d010105050030818c310b300906035504061302494c31163014060355" +
66+
"040a130d5374617274436f6d204c74642e312b3029060355040b13225365637572652044" +
67+
"69676974616c204365727469666963617465205369676e696e6731383036060355040313" +
68+
"2f5374617274436f6d20436c6173732031205072696d61727920496e7465726d65646961" +
69+
"746520536572766572204341301e170d3037313032353030323330365a170d3132313032" +
70+
"333030323330365a304c310b300906035504061302494c31163014060355040a130d5374" +
71+
"617274436f6d204c74642e312530230603550403131c5374617274436f6d20436c617373" +
72+
"2031204f435350205369676e657230820122300d06092a864886f70d0101010500038201" +
73+
"0f003082010a0282010100b9561b4c45318717178084e96e178df2255e18ed8d8ecc7c2b" +
74+
"7b51a6c1c2e6bf0aa3603066f132fe10ae97b50e99fa24b83fc53dd2777496387d14e1c3" +
75+
"a9b6a4933e2ac12413d085570a95b8147414a0bc007c7bcf222446ef7f1a156d7ea1c577" +
76+
"fc5f0facdfd42eb0f5974990cb2f5cefebceef4d1bdc7ae5c1075c5a99a93171f2b0845b" +
77+
"4ff0864e973fcfe32f9d7511ff87a3e943410c90a4493a306b6944359340a9ca96f02b66" +
78+
"ce67f028df2980a6aaee8d5d5d452b8b0eb93f923cc1e23fcccbdbe7ffcb114d08fa7a6a" +
79+
"3c404f825d1a0e715935cf623a8c7b59670014ed0622f6089a9447a7a19010f7fe58f841" +
80+
"29a2765ea367824d1c3bb2fda308530203010001a382015c30820158300c0603551d1301" +
81+
"01ff04023000300b0603551d0f0404030203a8301e0603551d250417301506082b060105" +
82+
"0507030906092b0601050507300105301d0603551d0e0416041445e0a36695414c5dd449" +
83+
"bc00e33cdcdbd2343e173081a80603551d230481a030819d8014eb4234d098b0ab9ff41b" +
84+
"6b08f7cc642eef0e2c45a18181a47f307d310b300906035504061302494c311630140603" +
85+
"55040a130d5374617274436f6d204c74642e312b3029060355040b132253656375726520" +
86+
"4469676974616c204365727469666963617465205369676e696e67312930270603550403" +
87+
"13205374617274436f6d2043657274696669636174696f6e20417574686f726974798201" +
88+
"0a30230603551d12041c301a8618687474703a2f2f7777772e737461727473736c2e636f" +
89+
"6d2f302c06096086480186f842010d041f161d5374617274436f6d205265766f63617469" +
90+
"6f6e20417574686f72697479300d06092a864886f70d01010505000382010100182d2215" +
91+
"8f0fc0291324fa8574c49bb8ff2835085adcbf7b7fc4191c397ab6951328253fffe1e5ec" +
92+
"2a7da0d50fca1a404e6968481366939e666c0a6209073eca57973e2fefa9ed1718e8176f" +
93+
"1d85527ff522c08db702e3b2b180f1cbff05d98128252cf0f450f7dd2772f4188047f19d" +
94+
"c85317366f94bc52d60f453a550af58e308aaab00ced33040b62bf37f5b1ab2a4f7f0f80" +
95+
"f763bf4d707bc8841d7ad9385ee2a4244469260b6f2bf085977af9074796048ecc2f9d48" +
96+
"a1d24ce16e41a9941568fec5b42771e118f16c106a54ccc339a4b02166445a167902e75e" +
97+
"6d8620b0825dcd18a069b90fd851d10fa8effd409deec02860d26d8d833f304b10669b42"

0 commit comments

Comments
 (0)