ソースを参照

acme: add Client.RevokeCert for certificate revocation

This change adds RevokeCert method to Client, implemented as described
in https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-6.6
and https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-6.5.

Change-Id: Ib0defbc299b46268c0f36dbe0f29f0081a3598d5
Reviewed-on: https://go-review.googlesource.com/27092
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Alex Vaghin 9 年 前
コミット
9fbab14f90
3 ファイル変更87 行追加0 行削除
  1. 34 0
      acme/internal/acme/acme.go
  2. 36 0
      acme/internal/acme/acme_test.go
  3. 17 0
      acme/internal/acme/types.go

+ 34 - 0
acme/internal/acme/acme.go

@@ -210,6 +210,40 @@ func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]by
 	}
 }
 
+// RevokeCert revokes a previously issued certificate cert, provided in DER format.
+//
+// The key argument, used to sign the request, must be authorized
+// to revoke the certificate. It's up to the CA to decide which keys are authorized.
+// For instance, the key pair of the certificate may be authorized.
+// If the key is nil, c.Key is used instead.
+func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
+	if _, err := c.Discover(ctx); err != nil {
+		return err
+	}
+
+	body := &struct {
+		Resource string `json:"resource"`
+		Cert     string `json:"certificate"`
+		Reason   int    `json:"reason"`
+	}{
+		Resource: "revoke-cert",
+		Cert:     base64.RawURLEncoding.EncodeToString(cert),
+		Reason:   int(reason),
+	}
+	if key == nil {
+		key = c.Key
+	}
+	res, err := postJWS(ctx, c.HTTPClient, key, c.dir.RevokeURL, body)
+	if err != nil {
+		return err
+	}
+	defer res.Body.Close()
+	if res.StatusCode != http.StatusOK {
+		return responseError(res)
+	}
+	return nil
+}
+
 // AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service
 // during account registration. See Register method of Client for more details.
 func AcceptTOS(tosURL string) bool { return true }

+ 36 - 0
acme/internal/acme/acme_test.go

@@ -745,6 +745,42 @@ func TestFetchCertSize(t *testing.T) {
 	}
 }
 
+func TestRevokeCert(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if r.Method == "HEAD" {
+			w.Header().Set("replay-nonce", "nonce")
+			return
+		}
+
+		var req struct {
+			Resource    string
+			Certificate string
+			Reason      int
+		}
+		decodeJWSRequest(t, &req, r)
+		if req.Resource != "revoke-cert" {
+			t.Errorf("req.Resource = %q; want revoke-cert", req.Resource)
+		}
+		if req.Reason != 1 {
+			t.Errorf("req.Reason = %d; want 1", req.Reason)
+		}
+		// echo -n cert | base64 | tr -d '=' | tr '/+' '_-'
+		cert := "Y2VydA"
+		if req.Certificate != cert {
+			t.Errorf("req.Certificate = %q; want %q", req.Certificate, cert)
+		}
+	}))
+	defer ts.Close()
+	client := &Client{
+		Key: testKey,
+		dir: &Directory{RevokeURL: ts.URL},
+	}
+	ctx := context.Background()
+	if err := client.RevokeCert(ctx, nil, []byte("cert"), CRLReasonKeyCompromise); err != nil {
+		t.Fatal(err)
+	}
+}
+
 func TestFetchNonce(t *testing.T) {
 	tests := []struct {
 		code  int

+ 17 - 0
acme/internal/acme/types.go

@@ -16,6 +16,23 @@ const (
 	StatusRevoked    = "revoked"
 )
 
+// CRLReasonCode identifies the reason for a certificate revocation.
+type CRLReasonCode int
+
+// CRL reason codes as defined in RFC 5280.
+const (
+	CRLReasonUnspecified          CRLReasonCode = 0
+	CRLReasonKeyCompromise        CRLReasonCode = 1
+	CRLReasonCACompromise         CRLReasonCode = 2
+	CRLReasonAffiliationChanged   CRLReasonCode = 3
+	CRLReasonSuperseded           CRLReasonCode = 4
+	CRLReasonCessationOfOperation CRLReasonCode = 5
+	CRLReasonCertificateHold      CRLReasonCode = 6
+	CRLReasonRemoveFromCRL        CRLReasonCode = 8
+	CRLReasonPrivilegeWithdrawn   CRLReasonCode = 9
+	CRLReasonAACompromise         CRLReasonCode = 10
+)
+
 // ErrUnsupportedKey is returned when an unsupported key type is encountered.
 var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")