diff --git a/example/client-auth/README.md b/example/client-auth/README.md index b359ace..44d0d50 100644 --- a/example/client-auth/README.md +++ b/example/client-auth/README.md @@ -7,14 +7,14 @@ having a trusted certificate. Build the PKI from the yaml definition: ``` -go run build-pki.go -config_path pki.yaml -db_path pki.boltdb +go run client-auth.go -config_path pki.yaml -db_path pki.boltdb ``` Fetch the certificates needed for nginx: ``` -go run get.go -db_path pki.boltdb -ca_name "Admins Intermediate CA" -bundle_name "localhost" -go run get.go -db_path pki.boltdb -bundle_name "Admins Intermediate CA" +go run client-auth.go -db_path pki.boltdb -ca_name "Admins Intermediate CA" -bundle_name "localhost" +go run client-auth.go -db_path pki.boltdb -bundle_name "Admins Intermediate CA" ``` Create the nginx config structure: @@ -32,7 +32,7 @@ golang.org/x/crypto/pkcs12 only provides decoding, so we use openssl. Fetch the client certificate and create a pkcs12 formatted file: ``` -go run get.go -db_path pki.boltdb -ca_name "Admins Intermediate CA" -bundle_name bob@acme.com -full_chain=false +go run client-auth.go -db_path pki.boltdb -ca_name "Admins Intermediate CA" -bundle_name bob@acme.com -full_chain=false cat bob@acme.com.{key,crt} | openssl pkcs12 -export -out bob@acme.com+pkcs12.crt ``` @@ -41,7 +41,7 @@ Import bob@acme.com+pkcs12.crt in your favorite browser. Fetch the root CA to import in the browser: ``` -go run get.go -db_path pki.boltdb -bundle_name "CA" +go run client-auth.go -db_path pki.boltdb -bundle_name "CA" ``` Import CA+chain.crt in your favorite browser. diff --git a/example/client-auth/build-pki.go b/example/client-auth/build-pki.go deleted file mode 100644 index 63dfefc..0000000 --- a/example/client-auth/build-pki.go +++ /dev/null @@ -1,82 +0,0 @@ -package main - -import ( - "crypto/x509/pkix" - "flag" - "io/ioutil" - "log" - - "crypto/x509" - "time" - - "github.com/boltdb/bolt" - "github.com/go-yaml/yaml" - "github.com/google/easypki/pkg/certificate" - "github.com/google/easypki/pkg/easypki" - "github.com/google/easypki/pkg/store" -) - -type configCerts struct { - Name string `yaml:"name"` - CommonName string `yaml:"commonName"` - DNSNames []string `yaml:"dnsNames"` - EmailAddresses []string `yaml:"emailAddresses"` - IsCA bool `yaml:"isCA"` - IsClient bool `yaml:"isClient"` - Signer string `yaml:"signer"` - Expire time.Duration `yaml:"expire"` -} - -type config struct { - Subject pkix.Name `yaml:"subject"` - Certs []configCerts `yaml:"certs"` -} - -func main() { - var ( - configPath = flag.String("config_path", "chain.yaml", "Configuration path to generate PKI.") - dbPath = flag.String("db_path", "", "Bolt database path.") - ) - flag.Parse() - b, err := ioutil.ReadFile(*configPath) - if err != nil { - log.Fatalf("Failed reading configuration file %v: %v", *configPath, err) - } - conf := &config{} - if err := yaml.Unmarshal(b, conf); err != nil { - log.Fatalf("Failed umarshaling yaml config (%v) %v: %v", *configPath, string(b), err) - } - db, err := bolt.Open(*dbPath, 0600, nil) - if err != nil { - log.Fatalf("Failed opening bolt database %v: %v", *dbPath, err) - } - defer db.Close() - pki := &easypki.EasyPKI{Store: &store.Bolt{DB: db}} - for _, cert := range conf.Certs { - req := &easypki.Request{ - Name: cert.Name, - Template: &x509.Certificate{ - Subject: conf.Subject, - NotAfter: time.Now().Add(cert.Expire), - IsCA: cert.IsCA, - DNSNames: cert.DNSNames, - EmailAddresses: cert.EmailAddresses, - }, - IsClientCertificate: cert.IsClient, - } - if cert.IsCA { - req.Template.MaxPathLen = -1 - } - req.Template.Subject.CommonName = cert.CommonName - var signer *certificate.Bundle - if cert.Signer != "" { - signer, err = pki.GetCA(cert.Signer) - if err != nil { - log.Fatalf("Cannot sign %v because cannot get CA %v: %v", cert.Name, cert.Signer, err) - } - } - if err := pki.Sign(signer, req); err != nil { - log.Fatalf("Cannot create bundle for %v: %v", cert.Name, err) - } - } -} diff --git a/example/client-auth/client-auth.go b/example/client-auth/client-auth.go new file mode 100644 index 0000000..c877fbb --- /dev/null +++ b/example/client-auth/client-auth.go @@ -0,0 +1,157 @@ +package main + +import ( + "encoding/pem" + "flag" + "io/ioutil" + "log" + "time" + + "os" + + "crypto/x509" + "crypto/x509/pkix" + + "github.com/boltdb/bolt" + "github.com/go-yaml/yaml" + "github.com/google/easypki/pkg/certificate" + "github.com/google/easypki/pkg/easypki" + "github.com/google/easypki/pkg/store" +) + +func main() { + var ( + caName = flag.String("ca_name", "", "Name of the CA which signed the bundle.") + bundleName = flag.String("bundle_name", "", "Name of the bundle to retrieve.") + fullChain = flag.Bool("full_chain", true, "Include chain of trust in certificate output.") + dbPath = flag.String("db_path", "", "Bolt database path.") + configPath = flag.String("config_path", "", "Configuration path to generate PKI.") + ) + flag.Parse() + if *dbPath == "" { + log.Fatal("Arg db_path must be set.") + } + if *bundleName == "" && *configPath == "" { + log.Fatal("One of bundle_name or config_path must be set.") + } + db, err := bolt.Open(*dbPath, 0600, nil) + if err != nil { + log.Fatalf("Failed opening bolt database %v: %v", *dbPath, err) + } + defer db.Close() + pki := &easypki.EasyPKI{Store: &store.Bolt{DB: db}} + if *bundleName != "" { + get(pki, *caName, *bundleName, *fullChain) + return + } + build(pki, *configPath) +} + +// build create a full PKI based on a yaml configuration. +func build(pki *easypki.EasyPKI, configPath string) { + type configCerts struct { + Name string `yaml:"name"` + CommonName string `yaml:"commonName"` + DNSNames []string `yaml:"dnsNames"` + EmailAddresses []string `yaml:"emailAddresses"` + IsCA bool `yaml:"isCA"` + IsClient bool `yaml:"isClient"` + Signer string `yaml:"signer"` + Expire time.Duration `yaml:"expire"` + } + + type config struct { + Subject pkix.Name `yaml:"subject"` + Certs []configCerts `yaml:"certs"` + } + + b, err := ioutil.ReadFile(configPath) + if err != nil { + log.Fatalf("Failed reading configuration file %v: %v", configPath, err) + } + conf := &config{} + if err := yaml.Unmarshal(b, conf); err != nil { + log.Fatalf("Failed umarshaling yaml config (%v) %v: %v", configPath, string(b), err) + } + for _, cert := range conf.Certs { + req := &easypki.Request{ + Name: cert.Name, + Template: &x509.Certificate{ + Subject: conf.Subject, + NotAfter: time.Now().Add(cert.Expire), + IsCA: cert.IsCA, + DNSNames: cert.DNSNames, + EmailAddresses: cert.EmailAddresses, + }, + IsClientCertificate: cert.IsClient, + } + if cert.IsCA { + req.Template.MaxPathLen = -1 + } + req.Template.Subject.CommonName = cert.CommonName + var signer *certificate.Bundle + if cert.Signer != "" { + signer, err = pki.GetCA(cert.Signer) + if err != nil { + log.Fatalf("Cannot sign %v because cannot get CA %v: %v", cert.Name, cert.Signer, err) + } + } + if err := pki.Sign(signer, req); err != nil { + log.Fatalf("Cannot create bundle for %v: %v", cert.Name, err) + } + } +} + +// get retrieves a bundle from the bolt database. If fullChain is true, the +// certificate will be the chain of trust from the primary tup to root CA. +func get(pki *easypki.EasyPKI, caName, bundleName string, fullChain bool) { + var bundle *certificate.Bundle + if caName == "" { + caName = bundleName + } + bundle, err := pki.GetBundle(caName, bundleName) + if err != nil { + log.Fatalf("Failed getting bundle %v within CA %v: %v", bundleName, caName, err) + } + leaf := bundle + chain := []*certificate.Bundle{bundle} + if fullChain { + for { + if leaf.Cert.Issuer.CommonName == leaf.Cert.Subject.CommonName { + break + } + ca, err := pki.GetCA(leaf.Cert.Issuer.CommonName) + if err != nil { + log.Fatalf("Failed getting signing CA %v: %v", leaf.Cert.Issuer.CommonName, err) + } + chain = append(chain, ca) + leaf = ca + } + } + key, err := os.Create(bundleName + ".key") + if err != nil { + log.Fatalf("Failed creating key output file: %v", err) + } + if err := pem.Encode(key, &pem.Block{ + Bytes: x509.MarshalPKCS1PrivateKey(bundle.Key), + Type: "RSA PRIVATE KEY", + }); err != nil { + log.Fatalf("Failed ecoding private key: %v", err) + } + crtName := bundleName + ".crt" + if fullChain { + crtName = bundleName + "+chain.crt" + } + cert, err := os.Create(crtName) + if err != nil { + log.Fatalf("Failed creating chain output file: %v", err) + } + for _, c := range chain { + if err := pem.Encode(cert, &pem.Block{ + Bytes: c.Cert.Raw, + Type: "CERTIFICATE", + }); err != nil { + log.Fatalf("Failed ecoding %v certificate: %v", c.Name, err) + } + } +} diff --git a/example/client-auth/get.go b/example/client-auth/get.go deleted file mode 100644 index a252216..0000000 --- a/example/client-auth/get.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "encoding/pem" - "flag" - "log" - - "os" - - "crypto/x509" - - "github.com/boltdb/bolt" - "github.com/google/easypki/pkg/certificate" - "github.com/google/easypki/pkg/easypki" - "github.com/google/easypki/pkg/store" -) - -func main() { - var ( - caName = flag.String("ca_name", "", "Name of the CA which signed the bundle.") - bundleName = flag.String("bundle_name", "", "Name of the bundle to retrieve.") - fullChain = flag.Bool("full_chain", true, "Include chain of trust in certificate output.") - dbPath = flag.String("db_path", "", "Bolt database path.") - ) - flag.Parse() - if *bundleName == "" { - log.Fatal("bundle_name cannot be empty") - } - db, err := bolt.Open(*dbPath, 0600, nil) - if err != nil { - log.Fatalf("Failed opening bolt database %v: %v", *dbPath, err) - } - defer db.Close() - pki := &easypki.EasyPKI{Store: &store.Bolt{DB: db}} - - var bundle *certificate.Bundle - if *caName == "" { - *caName = *bundleName - } - bundle, err = pki.GetBundle(*caName, *bundleName) - if err != nil { - log.Fatalf("Failed getting bundle %v within CA %v: %v", *bundleName, *caName, err) - } - leaf := bundle - chain := []*certificate.Bundle{bundle} - if *fullChain { - for { - if leaf.Cert.Issuer.CommonName == leaf.Cert.Subject.CommonName { - break - } - ca, err := pki.GetCA(leaf.Cert.Issuer.CommonName) - if err != nil { - log.Fatalf("Failed getting signing CA %v: %v", leaf.Cert.Issuer.CommonName, err) - } - chain = append(chain, ca) - leaf = ca - } - } - key, err := os.Create(*bundleName + ".key") - if err != nil { - log.Fatalf("Failed creating key output file: %v", err) - } - if err := pem.Encode(key, &pem.Block{ - Bytes: x509.MarshalPKCS1PrivateKey(bundle.Key), - Type: "RSA PRIVATE KEY", - }); err != nil { - log.Fatalf("Failed ecoding private key: %v", err) - } - crtName := *bundleName + ".crt" - if *fullChain { - crtName = *bundleName + "+chain.crt" - } - cert, err := os.Create(crtName) - if err != nil { - log.Fatalf("Failed creating chain output file: %v", err) - } - for _, c := range chain { - if err := pem.Encode(cert, &pem.Block{ - Bytes: c.Cert.Raw, - Type: "CERTIFICATE", - }); err != nil { - log.Fatalf("Failed ecoding %v certificate: %v", c.Name, err) - } - } -}