瀏覽代碼

ocsp: add support for OCSP response extensions

Some current uses of OCSP require extensions. In particular, Certificate
Transparency (RFC 6962) can use an OCSP extension to carry a Signed
Certificate Timestamp. This patch adds support for OCSP extensions (in
particular, singleExtensions), by adding Extensions and ExtraExtensions
fields with the same semantics as in x509.Certificate.

As a side-effect, trying to parse a response with a critical extension
will now return an error, just like parsing a certificate.

This change does not enable extensions in OCSP requests, just responses.

Change-Id: I5918f26ea1bb9d1ece96e85a6bb7691c7c017467
Reviewed-on: https://go-review.googlesource.com/18202
Reviewed-by: Adam Langley <agl@golang.org>
Richard Barnes 10 年之前
父節點
當前提交
552e9d568f
共有 2 個文件被更改,包括 150 次插入10 次删除
  1. 34 10
      ocsp/ocsp.go
  2. 116 0
      ocsp/ocsp_test.go

+ 34 - 10
ocsp/ocsp.go

@@ -85,12 +85,13 @@ type responseData struct {
 }
 
 type singleResponse struct {
-	CertID     certID
-	Good       asn1.Flag   `asn1:"tag:0,optional"`
-	Revoked    revokedInfo `asn1:"tag:1,optional"`
-	Unknown    asn1.Flag   `asn1:"tag:2,optional"`
-	ThisUpdate time.Time   `asn1:"generalized"`
-	NextUpdate time.Time   `asn1:"generalized,explicit,tag:0,optional"`
+	CertID           certID
+	Good             asn1.Flag        `asn1:"tag:0,optional"`
+	Revoked          revokedInfo      `asn1:"tag:1,optional"`
+	Unknown          asn1.Flag        `asn1:"tag:2,optional"`
+	ThisUpdate       time.Time        `asn1:"generalized"`
+	NextUpdate       time.Time        `asn1:"generalized,explicit,tag:0,optional"`
+	SingleExtensions []pkix.Extension `asn1:"explicit,tag:1,optional"`
 }
 
 type revokedInfo struct {
@@ -257,7 +258,7 @@ const (
 	AACompromise         = iota
 )
 
-// Request represents an OCSP request. See RFC 2560.
+// Request represents an OCSP request. See RFC 6960.
 type Request struct {
 	HashAlgorithm  crypto.Hash
 	IssuerNameHash []byte
@@ -265,7 +266,8 @@ type Request struct {
 	SerialNumber   *big.Int
 }
 
-// Response represents an OCSP response. See RFC 2560.
+// Response represents an OCSP response containing a single SingleResponse. See
+// RFC 6960.
 type Response struct {
 	// Status is one of {Good, Revoked, Unknown, ServerFailed}
 	Status                                        int
@@ -278,6 +280,20 @@ type Response struct {
 	TBSResponseData    []byte
 	Signature          []byte
 	SignatureAlgorithm x509.SignatureAlgorithm
+
+	// Extensions contains raw X.509 extensions from the singleExtensions field
+	// of the OCSP response. When parsing certificates, this can be used to
+	// extract non-critical extensions that are not parsed by this package. When
+	// marshaling OCSP responses, the Extensions field is ignored, see
+	// ExtraExtensions.
+	Extensions []pkix.Extension
+
+	// ExtraExtensions contains extensions to be copied, raw, into any marshaled
+	// OCSP response (in the singleExtensions field). Values override any
+	// extensions that would otherwise be produced based on the other fields. The
+	// ExtraExtensions field is not populated when parsing certificates, see
+	// Extensions.
+	ExtraExtensions []pkix.Extension
 }
 
 // These are pre-serialized error responses for the various non-success codes
@@ -405,6 +421,13 @@ func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
 
 	r := basicResp.TBSResponseData.Responses[0]
 
+	for _, ext := range r.SingleExtensions {
+		if ext.Critical {
+			return nil, ParseError("unsupported critical extension")
+		}
+	}
+	ret.Extensions = r.SingleExtensions
+
 	ret.SerialNumber = r.CertID.SerialNumber
 
 	switch {
@@ -534,8 +557,9 @@ func CreateResponse(issuer, responderCert *x509.Certificate, template Response,
 			IssuerKeyHash: issuerKeyHash,
 			SerialNumber:  template.SerialNumber,
 		},
-		ThisUpdate: template.ThisUpdate.UTC(),
-		NextUpdate: template.NextUpdate.UTC(),
+		ThisUpdate:       template.ThisUpdate.UTC(),
+		NextUpdate:       template.NextUpdate.UTC(),
+		SingleExtensions: template.ExtraExtensions,
 	}
 
 	switch template.Status {

+ 116 - 0
ocsp/ocsp_test.go

@@ -62,6 +62,30 @@ func TestOCSPDecodeWithoutCert(t *testing.T) {
 	}
 }
 
+func TestOCSPDecodeWithExtensions(t *testing.T) {
+	responseBytes, _ := hex.DecodeString(ocspResponseWithCriticalExtensionHex)
+	_, err := ParseResponse(responseBytes, nil)
+	if err == nil {
+		t.Error(err)
+	}
+
+	responseBytes, _ = hex.DecodeString(ocspResponseWithExtensionHex)
+	response, err := ParseResponse(responseBytes, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(response.Extensions) != 1 {
+		t.Errorf("len(response.Extensions): got %v, want %v", len(response.Extensions), 1)
+	}
+
+	extensionBytes := response.Extensions[0].Value
+	expectedBytes, _ := hex.DecodeString(ocspExtensionValueHex)
+	if !bytes.Equal(extensionBytes, expectedBytes) {
+		t.Errorf("response.Extensions[0]: got %x, want %x", extensionBytes, expectedBytes)
+	}
+}
+
 func TestOCSPSignature(t *testing.T) {
 	issuerCert, _ := hex.DecodeString(startComHex)
 	issuer, err := x509.ParseCertificate(issuerCert)
@@ -162,6 +186,15 @@ func TestOCSPResponse(t *testing.T) {
 		t.Fatal(err)
 	}
 
+	extensionBytes, _ := hex.DecodeString(ocspExtensionValueHex)
+	extensions := []pkix.Extension{
+		pkix.Extension{
+			Id:       ocspExtensionOID,
+			Critical: false,
+			Value:    extensionBytes,
+		},
+	}
+
 	producedAt := time.Now().Truncate(time.Minute)
 	thisUpdate := time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC)
 	nextUpdate := time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC)
@@ -173,6 +206,7 @@ func TestOCSPResponse(t *testing.T) {
 		RevokedAt:        thisUpdate,
 		RevocationReason: KeyCompromise,
 		Certificate:      responder,
+		ExtraExtensions:  extensions,
 	}
 
 	responseBytes, err := CreateResponse(issuer, responder, template, responderPrivateKey)
@@ -197,6 +231,10 @@ func TestOCSPResponse(t *testing.T) {
 		t.Errorf("resp.RevokedAt: got %d, want %d", resp.RevokedAt, template.RevokedAt)
 	}
 
+	if !reflect.DeepEqual(resp.Extensions, template.ExtraExtensions) {
+		t.Errorf("resp.Extensions: got %v, want %v", resp.Extensions, template.ExtraExtensions)
+	}
+
 	if !resp.ProducedAt.Equal(producedAt) {
 		t.Errorf("resp.ProducedAt: got %d, want %d", resp.ProducedAt, producedAt)
 	}
@@ -333,6 +371,84 @@ const ocspResponseWithoutCertHex = "308201d40a0100a08201cd308201c906092b06010505
 	"20a1a65c7f0b6427a224b3c98edd96b9b61f706099951188b0289555ad30a216fb774651" +
 	"5a35fca2e054dfa8"
 
+// PKIX nonce extension
+var ocspExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 2}
+var ocspExtensionValueHex = "0403000000"
+
+const ocspResponseWithCriticalExtensionHex = "308204fe0a0100a08204f7308204f306092b0601050507300101048204e4308204e03081" +
+	"dba003020100a11b3019311730150603550403130e4f43535020526573706f6e64657218" +
+	"0f32303136303130343137303130305a3081a53081a23049300906052b0e03021a050004" +
+	"14c0fe0278fc99188891b3f212e9c7e1b21ab7bfc004140dfc1df0a9e0f01ce7f2b21317" +
+	"7e6f8d157cd4f60210017f77deb3bcbb235d44ccc7dba62e72a116180f32303130303730" +
+	"373135303130355aa0030a0101180f32303130303730373135303130355aa011180f3230" +
+	"3130303730373138333531375aa1193017301506092b06010505073001020101ff040504" +
+	"03000000300d06092a864886f70d01010b0500038201010031c730ca60a7a0d92d8e4010" +
+	"911b469de95b4d27e89de6537552436237967694f76f701cf6b45c932bd308bca4a8d092" +
+	"5c604ba94796903091d9e6c000178e72c1f0a24a277dd262835af5d17d3f9d7869606c9f" +
+	"e7c8e708a41645699895beee38bfa63bb46296683761c5d1d65439b8ab868dc3017c9eeb" +
+	"b70b82dbf3a31c55b457d48bb9e82b335ed49f445042eaf606b06a3e0639824924c89c63" +
+	"eccddfe85e6694314138b2536f5e15e07085d0f6e26d4b2f8244bab0d70de07283ac6384" +
+	"a0501fc3dea7cf0adfd4c7f34871080900e252ddc403e3f0265f2a704af905d3727504ed" +
+	"28f3214a219d898a022463c78439799ca81c8cbafdbcec34ea937cd6a08202ea308202e6" +
+	"308202e2308201caa003020102020101300d06092a864886f70d01010b05003019311730" +
+	"150603550403130e4f43535020526573706f6e646572301e170d31353031333031353530" +
+	"33335a170d3136303133303135353033335a3019311730150603550403130e4f43535020" +
+	"526573706f6e64657230820122300d06092a864886f70d01010105000382010f00308201" +
+	"0a0282010100e8155f2d3e6f2e8d14c62a788bd462f9f844e7a6977c83ef1099f0f6616e" +
+	"c5265b56f356e62c5400f0b06a2e7945a82752c636df32a895152d6074df1701dc6ccfbc" +
+	"bec75a70bd2b55ae2be7e6cad3b5fd4cd5b7790ab401a436d3f5f346074ffde8a99d5b72" +
+	"3350f0a112076614b12ef79c78991b119453445acf2416ab0046b540db14c9fc0f27b898" +
+	"9ad0f63aa4b8aefc91aa8a72160c36307c60fec78a93d3fddf4259902aa77e7332971c7d" +
+	"285b6a04f648993c6922a3e9da9adf5f81508c3228791843e5d49f24db2f1290bafd97e6" +
+	"55b1049a199f652cd603c4fafa330c390b0da78fbbc67e8fa021cbd74eb96222b12ace31" +
+	"a77dcf920334dc94581b0203010001a3353033300e0603551d0f0101ff04040302078030" +
+	"130603551d25040c300a06082b06010505070309300c0603551d130101ff04023000300d" +
+	"06092a864886f70d01010b05000382010100718012761b5063e18f0dc44644d8e6ab8612" +
+	"31c15fd5357805425d82aec1de85bf6d3e30fce205e3e3b8b795bbe52e40a439286d2288" +
+	"9064f4aeeb150359b9425f1da51b3a5c939018555d13ac42c565a0603786a919328f3267" +
+	"09dce52c22ad958ecb7873b9771d1148b1c4be2efe80ba868919fc9f68b6090c2f33c156" +
+	"d67156e42766a50b5d51e79637b7e58af74c2a951b1e642fa7741fec982cc937de37eff5" +
+	"9e2005d5939bfc031589ca143e6e8ab83f40ee08cc20a6b4a95a318352c28d18528dcaf9" +
+	"66705de17afa19d6e8ae91ddf33179d16ebb6ac2c69cae8373d408ebf8c55308be6c04d9" +
+	"3a25439a94299a65a709756c7a3e568be049d5c38839"
+
+const ocspResponseWithExtensionHex = "308204fb0a0100a08204f4308204f006092b0601050507300101048204e1308204dd3081" +
+	"d8a003020100a11b3019311730150603550403130e4f43535020526573706f6e64657218" +
+	"0f32303136303130343136353930305a3081a230819f3049300906052b0e03021a050004" +
+	"14c0fe0278fc99188891b3f212e9c7e1b21ab7bfc004140dfc1df0a9e0f01ce7f2b21317" +
+	"7e6f8d157cd4f60210017f77deb3bcbb235d44ccc7dba62e72a116180f32303130303730" +
+	"373135303130355aa0030a0101180f32303130303730373135303130355aa011180f3230" +
+	"3130303730373138333531375aa1163014301206092b0601050507300102040504030000" +
+	"00300d06092a864886f70d01010b05000382010100c09a33e0b2324c852421bb83f85ac9" +
+	"9113f5426012bd2d2279a8166e9241d18a33c870894250622ffc7ed0c4601b16d624f90b" +
+	"779265442cdb6868cf40ab304ab4b66e7315ed02cf663b1601d1d4751772b31bc299db23" +
+	"9aebac78ed6797c06ed815a7a8d18d63cfbb609cafb47ec2e89e37db255216eb09307848" +
+	"d01be0a3e943653c78212b96ff524b74c9ec456b17cdfb950cc97645c577b2e09ff41dde" +
+	"b03afb3adaa381cc0f7c1d95663ef22a0f72f2c45613ae8e2b2d1efc96e8463c7d1d8a1d" +
+	"7e3b35df8fe73a301fc3f804b942b2b3afa337ff105fc1462b7b1c1d75eb4566c8665e59" +
+	"f80393b0adbf8004ff6c3327ed34f007cb4a3348a7d55e06e3a08202ea308202e6308202" +
+	"e2308201caa003020102020101300d06092a864886f70d01010b05003019311730150603" +
+	"550403130e4f43535020526573706f6e646572301e170d3135303133303135353033335a" +
+	"170d3136303133303135353033335a3019311730150603550403130e4f43535020526573" +
+	"706f6e64657230820122300d06092a864886f70d01010105000382010f003082010a0282" +
+	"010100e8155f2d3e6f2e8d14c62a788bd462f9f844e7a6977c83ef1099f0f6616ec5265b" +
+	"56f356e62c5400f0b06a2e7945a82752c636df32a895152d6074df1701dc6ccfbcbec75a" +
+	"70bd2b55ae2be7e6cad3b5fd4cd5b7790ab401a436d3f5f346074ffde8a99d5b723350f0" +
+	"a112076614b12ef79c78991b119453445acf2416ab0046b540db14c9fc0f27b8989ad0f6" +
+	"3aa4b8aefc91aa8a72160c36307c60fec78a93d3fddf4259902aa77e7332971c7d285b6a" +
+	"04f648993c6922a3e9da9adf5f81508c3228791843e5d49f24db2f1290bafd97e655b104" +
+	"9a199f652cd603c4fafa330c390b0da78fbbc67e8fa021cbd74eb96222b12ace31a77dcf" +
+	"920334dc94581b0203010001a3353033300e0603551d0f0101ff04040302078030130603" +
+	"551d25040c300a06082b06010505070309300c0603551d130101ff04023000300d06092a" +
+	"864886f70d01010b05000382010100718012761b5063e18f0dc44644d8e6ab861231c15f" +
+	"d5357805425d82aec1de85bf6d3e30fce205e3e3b8b795bbe52e40a439286d22889064f4" +
+	"aeeb150359b9425f1da51b3a5c939018555d13ac42c565a0603786a919328f326709dce5" +
+	"2c22ad958ecb7873b9771d1148b1c4be2efe80ba868919fc9f68b6090c2f33c156d67156" +
+	"e42766a50b5d51e79637b7e58af74c2a951b1e642fa7741fec982cc937de37eff59e2005" +
+	"d5939bfc031589ca143e6e8ab83f40ee08cc20a6b4a95a318352c28d18528dcaf966705d" +
+	"e17afa19d6e8ae91ddf33179d16ebb6ac2c69cae8373d408ebf8c55308be6c04d93a2543" +
+	"9a94299a65a709756c7a3e568be049d5c38839"
+
 const ocspRequestHex = "3051304f304d304b3049300906052b0e03021a05000414c0fe0278fc99188891b3f212e9" +
 	"c7e1b21ab7bfc004140dfc1df0a9e0f01ce7f2b213177e6f8d157cd4f60210017f77deb3" +
 	"bcbb235d44ccc7dba62e72"