Skip to content
This repository has been archived by the owner on Aug 31, 2021. It is now read-only.

Commit

Permalink
Merge pull request #5 from theckman/supereasypki
Browse files Browse the repository at this point in the history
create intermediate CA certificates + bug fixes
  • Loading branch information
jeremy-clerc authored Oct 26, 2016
2 parents 5c2d8b7 + 9269e30 commit 93992aa
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 30 deletions.
30 changes: 24 additions & 6 deletions cmd/easypki/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,18 @@ func createBundle(c *cli.Context) {
NotAfter: time.Now().AddDate(0, 0, c.Int("expire")),
}

if c.Bool("ca") {
intCA := c.Bool("intermediate")

if intCA || c.Bool("ca") {
template.IsCA = true
filename = "ca"

if !intCA {
filename = "ca"
}
} else if c.Bool("client") {
template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
template.EmailAddresses = c.StringSlice("email")
} else {
// We default to server
template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageServerAuth)

IPs := make([]net.IP, 0, len(c.StringSlice("ip")))
for _, ipStr := range c.StringSlice("ip") {
if i := net.ParseIP(ipStr); i != nil {
Expand All @@ -93,7 +95,14 @@ func createBundle(c *cli.Context) {
template.IPAddresses = IPs
template.DNSNames = c.StringSlice("dns")
}
err := easypki.GenerateCertifcate(c.GlobalString("root"), filename, template)
err := easypki.GenerateCertificate(&easypki.GenerationRequest{
PKIRoot: c.GlobalString("root"),
Name: filename,
Template: template,
MaxPathLen: c.Int("max-path-len"),
IsIntermediateCA: intCA,
IsClientCertificate: c.Bool("client"),
})
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -170,6 +179,15 @@ func parseArgs() {
Name: "ca",
Usage: "certificate authority",
},
cli.BoolFlag{
Name: "intermediate",
Usage: "intermediate certificate authority; implies --ca",
},
cli.IntFlag{
Name: "max-path-len",
Usage: "intermediate maximum path length",
Value: -1, // default to less-than 0 when not defined
},
cli.BoolFlag{
Name: "client",
Usage: "generate a client certificate (default is server)",
Expand Down
100 changes: 76 additions & 24 deletions pkg/easypki/easyca.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,36 @@ func GeneratePrivateKey(path string) (*rsa.PrivateKey, error) {
return key, nil
}

func GenerateCertifcate(pkiroot, name string, template *x509.Certificate) error {
// GenerationRequest is a struct for providing configuration to
// GenerateCertificate when actioning a certification generation request.
type GenerationRequest struct {
PKIRoot string
Name string
Template *x509.Certificate
MaxPathLen int
IsIntermediateCA bool
IsClientCertificate bool
}

// GenerateCertificate is a function for helping to generate new x509
// certificates and keys from the GenerationRequest. This function renders the
// content out to the filesystem.
func GenerateCertificate(genReq *GenerationRequest) error {
// TODO(jclerc): check that pki has been init

var crtPath string
privateKeyPath := filepath.Join(pkiroot, "private", name+".key")
if name == "ca" {
crtPath = filepath.Join(pkiroot, name+".crt")
privateKeyPath := filepath.Join(genReq.PKIRoot, "private", genReq.Name+".key")
if genReq.Name == "ca" {
crtPath = filepath.Join(genReq.PKIRoot, genReq.Name+".crt")
} else {
crtPath = filepath.Join(pkiroot, "issued", name+".crt")
crtPath = filepath.Join(genReq.PKIRoot, "issued", genReq.Name+".crt")
}

var caCrt *x509.Certificate
var caKey *rsa.PrivateKey

if _, err := os.Stat(privateKeyPath); err == nil {
return fmt.Errorf("a key pair for %v already exists", name)
return fmt.Errorf("a key pair for %v already exists", genReq.Name)
}

privateKey, err := GeneratePrivateKey(privateKeyPath)
Expand All @@ -94,39 +108,77 @@ func GenerateCertifcate(pkiroot, name string, template *x509.Certificate) error
return fmt.Errorf("marshal public key: %v", err)
}
subjectKeyId := sha1.Sum(publicKeyBytes)
template.SubjectKeyId = subjectKeyId[:]
genReq.Template.SubjectKeyId = subjectKeyId[:]

genReq.Template.NotBefore = time.Now()
genReq.Template.SignatureAlgorithm = x509.SHA256WithRSA

template.NotBefore = time.Now()
template.SignatureAlgorithm = x509.SHA256WithRSA
if template.IsCA {
// Non-intermediate Certificate Authority
if genReq.Template.IsCA && !genReq.IsIntermediateCA {
// Random Serial
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return fmt.Errorf("failed to generate ca serial number: %s", err)
}
template.SerialNumber = serialNumber
template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign
template.BasicConstraintsValid = true
template.Issuer = template.Subject
template.AuthorityKeyId = template.SubjectKeyId
genReq.Template.SerialNumber = serialNumber

caCrt = template
// Root certificate can self-sign
genReq.Template.Issuer = genReq.Template.Subject
genReq.Template.AuthorityKeyId = genReq.Template.SubjectKeyId

// Use the generated certificate template and private key (self-signing)
caCrt = genReq.Template
caKey = privateKey
} else {
template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
serialNumber, err := NextNumber(pkiroot, "serial")
}

// Intermediate-only Certificate Authority
if genReq.Template.IsCA && genReq.IsIntermediateCA {
genReq.Template.ExtKeyUsage = []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
x509.ExtKeyUsageServerAuth,
}
}

// Either type of Certificate Authority (intermediate, root, etc.)
if genReq.Template.IsCA || genReq.IsIntermediateCA {
genReq.Template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign
genReq.Template.BasicConstraintsValid = true

// Enforce Maximum Path Length
if genReq.MaxPathLen >= 0 {
genReq.Template.MaxPathLen = genReq.MaxPathLen
genReq.Template.MaxPathLenZero = true // doesn't force to zero
}
}

// Any leaf: intermediate CAs, client/server certificates, signed by a root
if !genReq.Template.IsCA || genReq.IsIntermediateCA {
serialNumber, err := NextNumber(genReq.PKIRoot, "serial")
if err != nil {
return fmt.Errorf("get next serial: %v", err)
}
template.SerialNumber = serialNumber
genReq.Template.SerialNumber = serialNumber

caCrt, caKey, err = GetCA(pkiroot)
caCrt, caKey, err = GetCA(genReq.PKIRoot)
if err != nil {
return fmt.Errorf("get ca: %v", err)
}
}

crt, err := x509.CreateCertificate(rand.Reader, template, caCrt, privateKey.Public(), caKey)
// Should cover only client/server (All non-CA, i.e. doesn't include intermediates)
if !genReq.Template.IsCA {
if !genReq.IsClientCertificate {
genReq.Template.ExtKeyUsage = append(genReq.Template.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
}
// Clients can only use ClientAuth
genReq.Template.ExtKeyUsage = append(genReq.Template.ExtKeyUsage, x509.ExtKeyUsageClientAuth)

// set the usage for non-CA certificates
genReq.Template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
}

crt, err := x509.CreateCertificate(rand.Reader, genReq.Template, caCrt, privateKey.Public(), caKey)
if err != nil {
return fmt.Errorf("create certificate: %v", err)
}
Expand All @@ -146,8 +198,8 @@ func GenerateCertifcate(pkiroot, name string, template *x509.Certificate) error
}

// I do not think we have to write the ca.crt in the index
if !template.IsCA {
WriteIndex(pkiroot, name, template)
if !genReq.Template.IsCA {
WriteIndex(genReq.PKIRoot, genReq.Name, genReq.Template)
if err != nil {
return fmt.Errorf("write index: %v", err)
}
Expand Down

0 comments on commit 93992aa

Please sign in to comment.