Skip to content

Commit

Permalink
Add revoke command
Browse files Browse the repository at this point in the history
  • Loading branch information
namreg committed Aug 26, 2018
1 parent 9ce254d commit c5ac5fc
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ certstrap allows you to build your own certificate system:
1. Initialize certificate authorities
2. Create identities and certificate signature requests for hosts
3. Sign and generate certificates
4. Revoke certificates

## Certificate architecture

Expand Down Expand Up @@ -80,6 +81,13 @@ $ ./bin/certstrap sign Alice --CA CertAuth
Created out/Alice.crt from out/Alice.csr signed by out/CertAuth.key
```

### Revoke certificate:

```
$ ./bin/certstrap revoke --CN Alice --CA CertAuth
'CertAuth' authority has revoked certificate for 'Alice'.
```

#### PKCS Format:
If you'd like to convert your certificate and key to PKCS12 format, simply run:
```
Expand Down
1 change: 1 addition & 0 deletions certstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func main() {
cmd.NewInitCommand(),
cmd.NewCertRequestCommand(),
cmd.NewSignCommand(),
mcmd.NewRevokeCommand(),
}
app.Before = func(c *cli.Context) error {
cmd.InitDepot(c.String("depot-path"))
Expand Down
127 changes: 127 additions & 0 deletions cmd/revoke.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package cmd

import (
"crypto/rand"
"crypto/x509"
x509pkix "crypto/x509/pkix"
"errors"
"fmt"
"os"
"strings"
"time"

"github.com/square/certstrap/Godeps/_workspace/src/github.com/codegangsta/cli"
"github.com/square/certstrap/depot"
"github.com/square/certstrap/pkix"
)

type revokeCommand struct {
ca, cn string
}

// NewRevokeCommand revokes the given CN's certificate by adding it to the CA's CRL.
func NewRevokeCommand() cli.Command {
return cli.Command{
Name: "revoke",
Usage: "Revoke certificate",
Description: "Add certificate to the CA's CRL.",
Flags: []cli.Flag{
cli.StringFlag{"CN", "", "Certificate's CN to revoke", ""},
cli.StringFlag{"CA", "", "CA's name to revoke cert", ""},
},
Action: new(revokeCommand).run,
}
}

func (c *revokeCommand) checkErr(err error) {
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
}

func (c *revokeCommand) parseArgs(ctx *cli.Context) error {
if ctx.String("CA") == "" {
return errors.New("CA name must be provided")
}
c.ca = strings.Replace(ctx.String("CA"), " ", "_", -1)

if ctx.String("CN") == "" {
return errors.New("CN name must be provided")
}
c.cn = strings.Replace(ctx.String("CN"), " ", "_", -1)

return nil
}

func (c *revokeCommand) run(ctx *cli.Context) {
c.checkErr(c.parseArgs(ctx))

caCert, err := c.CAx509Certificate()
c.checkErr(err)

cnCert, err := c.CNx509Certificate()
c.checkErr(err)

revoked, err := c.revokedCertificates()
c.checkErr(err)

revoked = append(revoked, x509pkix.RevokedCertificate{
SerialNumber: cnCert.SerialNumber,
RevocationTime: time.Now(),
})

err = c.saveRevokedCertificates(caCert, revoked)
c.checkErr(err)

fmt.Fprintln(os.Stdout, "%q authority has revoked the certificate for %q.", c.cn, c.ca)
}

func (c *revokeCommand) CAx509Certificate() (*x509.Certificate, error) {
cert, err := depot.GetCertificate(d, c.ca)
if err != nil {
return nil, err
}
return cert.GetRawCertificate()
}

func (c *revokeCommand) CNx509Certificate() (*x509.Certificate, error) {
cert, err := depot.GetCertificate(d, c.cn)
if err != nil {
return nil, err
}
return cert.GetRawCertificate()
}

func (c *revokeCommand) revokedCertificates() ([]x509pkix.RevokedCertificate, error) {
list, err := depot.GetCertificateRevocationList(d, c.ca)
if err != nil {
return nil, err
}

certList, err := x509.ParseDERCRL(list.DERBytes())
if err != nil {
return nil, err
}

return certList.TBSCertList.RevokedCertificates, nil
}

func (c *revokeCommand) saveRevokedCertificates(cert *x509.Certificate, list []x509pkix.RevokedCertificate) error {
priv, err := depot.GetPrivateKey(d, c.ca)
if err != nil {
return fmt.Errorf("could not get %q private key: %v", c.ca, err)
}

crlBytes, err := cert.CreateCRL(rand.Reader, priv.Private, list, time.Now(), time.Now())
if err != nil {
return fmt.Errorf("could not create CRL: %v", err)
}
if err := d.Delete(depot.CrlTag(c.ca)); err != nil {
return fmt.Errorf("could not delete CRL: %v", err)
}
if err = depot.PutCertificateRevocationList(d, c.ca, pkix.NewCertificateRevocationListFromDER(crlBytes)); err != nil {
return fmt.Errorf("could not put revokation list: %v", err)
}
return nil
}
9 changes: 9 additions & 0 deletions depot/pkix.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,12 @@ func PutCertificateRevocationList(d Depot, name string, crl *pkix.CertificateRev
}
return d.Put(CrlTag(name), b)
}

//GetCertificateRevocationList gets a CRL file for a given name and ca in the depot.
func GetCertificateRevocationList(d Depot, name string) (*pkix.CertificateRevocationList, error) {
b, err := d.Get(CrlTag(name))
if err != nil {
return nil, err
}
return pkix.NewCertificateRevocationListFromPEM(b)
}
5 changes: 5 additions & 0 deletions pkix/crl.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ type CertificateRevocationList struct {
derBytes []byte
}

//DERBytes returns DER-formatted bytes of the CRL.
func (c *CertificateRevocationList) DERBytes() []byte {
return c.derBytes
}

// NewCertificateRevocationListFromDER inits CertificateRevocationList from DER-format bytes
func NewCertificateRevocationListFromDER(derBytes []byte) *CertificateRevocationList {
return &CertificateRevocationList{derBytes: derBytes}
Expand Down

0 comments on commit c5ac5fc

Please sign in to comment.