Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions api/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/crypto/pkcs12"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/log"
Expand Down Expand Up @@ -203,6 +204,12 @@ type genSignRequest struct {
Request *csr.CertificateRequest `json:"request"`
Profile string `json:"profile"`
Label string `json:"label"`
Format format `json:"format"`
}

type format struct {
Type string `json:"type"`
Password string `json:"password"`
}

// Handle responds to requests for the CA to generate a new private
Expand Down Expand Up @@ -265,10 +272,20 @@ func (cg *CertGeneratorHandler) Handle(w http.ResponseWriter, r *http.Request) e
return errors.NewBadRequest(err)
}

var file string
if req.Format.Type == "pkcs12" {
var password []byte
if req.Format.Password != "" {
password = []byte(req.Format.Password)
}
file = pkcs12.ParseAndEncode(key, certBytes, password)
}

result := map[string]interface{}{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This output should also contain the base64-encoded PKCS#12 file if it was requested.

"private_key": string(key),
"certificate_request": string(csr),
"certificate": string(certBytes),
"format": file,
"sums": map[string]Sum{
"certificate_request": reqSum,
"certificate": certSum,
Expand Down
26 changes: 26 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,32 @@ func PrintCert(key, csrBytes, cert []byte) {
fmt.Printf("%s\n", jsonOut)
}

// PrintCertAndFile outputs a cert, key, csr, and file to stdout
func PrintCertAndFile(key, csrBytes, cert []byte, file string) {
out := map[string]string{}
if cert != nil {
out["cert"] = string(cert)
}

if key != nil {
out["key"] = string(key)
}

if csrBytes != nil {
out["csr"] = string(csrBytes)
}

if file != "" {
out["file"] = file
}

jsonOut, err := json.Marshal(out)
if err != nil {
return
}
fmt.Printf("%s\n", jsonOut)
}

// PrintOCSPResponse outputs an OCSP response to stdout
// ocspResponse is base64 encoded
func PrintOCSPResponse(resp []byte) {
Expand Down
2 changes: 2 additions & 0 deletions cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type Config struct {
IntermediatesFile string
CABundleFile string
IntBundleFile string
Format string
Address string
Port int
Password string
Expand Down Expand Up @@ -73,6 +74,7 @@ func registerFlags(c *Config, f *flag.FlagSet) {
f.StringVar(&c.IntermediatesFile, "intermediates", "", "intermediate certs")
f.StringVar(&c.CABundleFile, "ca-bundle", "", "path to root certificate store")
f.StringVar(&c.IntBundleFile, "int-bundle", "", "path to intermediate certificate store")
f.StringVar(&c.Format, "format", "", "format for the exported file")
f.StringVar(&c.Address, "address", "127.0.0.1", "Address to bind")
f.IntVar(&c.Port, "port", 8888, "Port to bind")
f.StringVar(&c.ConfigFile, "config", "", "path to configuration file")
Expand Down
31 changes: 27 additions & 4 deletions cli/gencert/gencert.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import (
"github.com/cloudflare/cfssl/cli"
"github.com/cloudflare/cfssl/cli/genkey"
"github.com/cloudflare/cfssl/cli/sign"
"github.com/cloudflare/cfssl/crypto/pkcs12"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/initca"
"github.com/cloudflare/cfssl/log"
"github.com/cloudflare/cfssl/signer"
)

var gencertUsageText = `cfssl gencert -- generate a new key and signed certificate
var gencertUsageText = `cfssl gencert -- generate a new key and signed certificate (or PKCS#12 bundle)

Usage of gencert:
Generate a new key and cert from CSR:
Expand All @@ -29,13 +30,17 @@ Usage of gencert:
Re-generate a CA cert with the CA key and certificate:
cfssl gencert -renewca -ca cert -ca-key key

Generate a PKCS#12 file with a new key and cert from CSR:
cfssl gencert -initca -format pkcs12 [-password password] CSRJSON
cfssl gencert -ca cert -ca-key key -format pkcs12 [-password password] CSRJSON

Arguments:
CSRJSON: JSON file containing the request, use '-' for reading JSON from stdin

Flags:
`

var gencertFlags = []string{"initca", "remote", "ca", "ca-key", "config", "hostname", "profile", "label"}
var gencertFlags = []string{"initca", "remote", "ca", "ca-key", "config", "hostname", "profile", "label", "format", "password"}

func gencertMain(args []string, c cli.Config) error {
if c.RenewCA {
Expand Down Expand Up @@ -84,7 +89,16 @@ func gencertMain(args []string, c cli.Config) error {
}

}
cli.PrintCert(key, csrPEM, cert)
if c.Format == "pkcs12" {
var password []byte
if c.Password != "0" {
password = []byte(c.Password)
}
file := pkcs12.ParseAndEncode(key, cert, password)
cli.PrintCertAndFile(key, csrPEM, cert, file)
} else {
cli.PrintCert(key, csrPEM, cert)
}

default:
if req.CA != nil {
Expand Down Expand Up @@ -143,7 +157,16 @@ func gencertMain(args []string, c cli.Config) error {
log.Warning(generator.CSRNoHostMessage)
}

cli.PrintCert(key, csrBytes, cert)
if c.Format == "pkcs12" {
var password []byte
if c.Password != "0" {
password = []byte(c.Password)
}
file := pkcs12.ParseAndEncode(key, cert, password)
cli.PrintCertAndFile(key, csrBytes, cert, file)
} else {
cli.PrintCert(key, csrBytes, cert)
}
}
return nil
}
Expand Down
55 changes: 55 additions & 0 deletions crypto/pkcs12/pkcs12.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Package pkcs12 implements the parsing and encoding of key and certificate files into a PKCS#12 file
package pkcs12
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use a package-level comment.


import (
"crypto/x509"
"encoding/base64"
"encoding/pem"

"github.com/AGWA-forks/go-pkcs12"
)

// ParseAndEncode takes key, certificate, and optional password
// as []byte and parses them to get a suitable format to encode them
func ParseAndEncode(key, cert, password []byte) string {
var file string
certBlock, _ := pem.Decode(cert)
certBytes := certBlock.Bytes
certificate, err := x509.ParseCertificate(certBytes)
if err != nil {
return file
}

block, _ := pem.Decode(key)
privKey := block.Bytes
if block.Type == "RSA PRIVATE KEY" {
pkcsKey, err := x509.ParsePKCS1PrivateKey(privKey)
if err != nil {
return file
}
file = Encode(pkcsKey, certificate, password)
} else if block.Type == "EC PRIVATE KEY" {
ecKey, err := x509.ParseECPrivateKey(privKey)
if err != nil {
return file
}
file = Encode(ecKey, certificate, password)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the error cases here?


return file
}

// Encode is called by ParseAndEncode with a key, certificate, and optional password
// to call AGWA-forks's pkcs12 encode function and returns the pkcs12 file as a base64 encoded string
func Encode(privateKey interface{}, certificate *x509.Certificate, password []byte) string {
var none []*x509.Certificate
var data string
pfxData, err := pkcs12.Encode(privateKey, certificate, none, password)
if err != nil {
return data
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure writing to "outfile.p12" is what you want here. The caller should get a byte slice containing the encoded data.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kisom originally thought exporting the PKCS12 key might not be a good idea since it's the binary data. Or is there a different way of doing it that I'm missing?


data = base64.StdEncoding.EncodeToString(pfxData)

return data
}
2 changes: 2 additions & 0 deletions doc/api/endpoint_newcert.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Optional parameters:
the CSR, useful when interacting with cfssl server that stands
in front of a remote multi-root CA signer
* profile: a string specifying the signing profile for the signer
* format: a json object specifying the output file type and
optional password. (example: "format": {"type": "pkcs12", "password": "password"})

Result:

Expand Down