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>
This commit is contained in:
Alex Vaghin
2016-08-17 14:31:42 +00:00
parent a8a4eedf8c
commit 9fbab14f90
3 changed files with 87 additions and 0 deletions
+34
View File
@@ -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
View File
@@ -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
View File
@@ -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")