From b3035b16b57477e94ade923e8a5aa25c7538843e Mon Sep 17 00:00:00 2001
From: xenolf <xenolf@users.noreply.github.com>
Date: Sun, 27 Sep 2015 14:51:44 +0200
Subject: [PATCH] Support for cert revocation

---
 acme/client.go   | 21 +++++++++++++++++++++
 acme/messages.go |  5 +++++
 cli_handlers.go  | 34 ++++++++++++++++++++++++++++++++++
 3 files changed, 60 insertions(+)

diff --git a/acme/client.go b/acme/client.go
index d2ae18db..b5f34f8c 100644
--- a/acme/client.go
+++ b/acme/client.go
@@ -158,6 +158,27 @@ func (c *Client) ObtainCertificates(domains []string) ([]CertificateResource, er
 	return c.requestCertificates(challenges)
 }
 
+func (c *Client) RevokeCertificate(certificate []byte) error {
+	encodedCert := base64.URLEncoding.EncodeToString(certificate)
+
+	jsonBytes, err := json.Marshal(revokeCertMessage{Resource: "revoke-cert", Certificate: encodedCert})
+	if err != nil {
+		return err
+	}
+
+	resp, err := c.jws.post(c.directory.RevokeCertURL, jsonBytes)
+	if err != nil {
+		return err
+	}
+
+	if resp.StatusCode != 200 {
+		body, _ := ioutil.ReadAll(resp.Body)
+		return fmt.Errorf("The server returned an error while trying to revoke the certificate.\n%s", body)
+	}
+
+	return nil
+}
+
 // Looks through the challenge combinations to find a solvable match.
 // Then solves the challenges in series and returns.
 func (c *Client) solveChallenges(challenges []*authorizationResource) error {
diff --git a/acme/messages.go b/acme/messages.go
index fdfadc4b..c5c345c7 100644
--- a/acme/messages.go
+++ b/acme/messages.go
@@ -75,6 +75,11 @@ type csrMessage struct {
 	Authorizations []string `json:"authorizations"`
 }
 
+type revokeCertMessage struct {
+	Resource    string `json:"resource"`
+	Certificate string `json:"certificate"`
+}
+
 // CertificateResource represents a CA issued certificate.
 // PrivateKey and Certificate are both already PEM encoded
 // and can be directly written to disk.
diff --git a/cli_handlers.go b/cli_handlers.go
index 438c14f0..cb8d5004 100644
--- a/cli_handlers.go
+++ b/cli_handlers.go
@@ -113,3 +113,37 @@ func run(c *cli.Context) {
 
 	}
 }
+
+func revoke(c *cli.Context) {
+	err := checkFolder(c.GlobalString("path"))
+	if err != nil {
+		logger().Fatalf("Cound not check/create path: %v", err)
+	}
+
+	conf := NewConfiguration(c)
+	if !c.GlobalIsSet("email") {
+		logger().Fatal("You have to pass an account (email address) to the program using --email or -m")
+	}
+
+	acc := NewAccount(c.GlobalString("email"), conf)
+	client := acme.NewClient(c.GlobalString("server"), acc, conf.RsaBits(), conf.OptPort())
+
+	err = checkFolder(conf.CertPath())
+	if err != nil {
+		logger().Fatalf("Cound not check/create path: %v", err)
+	}
+
+	for _, domain := range c.GlobalStringSlice("domains") {
+		logger().Printf("Trying to revoke certificate for domain %s", domain)
+
+		certPath := path.Join(conf.CertPath(), domain+".crt")
+		certBytes, err := ioutil.ReadFile(certPath)
+
+		err = client.RevokeCertificate(certBytes)
+		if err != nil {
+			logger().Printf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
+		} else {
+			logger().Print("Certificate was revoked.")
+		}
+	}
+}