mirror of https://github.com/docker/docs.git
Added key generate
This commit is contained in:
parent
a7164b638b
commit
08124c18f6
|
@ -1,16 +1,24 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/vetinari/trustmanager"
|
"github.com/docker/vetinari/trustmanager"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var subjectKeyID string
|
var subjectKeyID string
|
||||||
|
@ -26,6 +34,7 @@ func init() {
|
||||||
cmdKeys.AddCommand(cmdKeysTrust)
|
cmdKeys.AddCommand(cmdKeysTrust)
|
||||||
cmdKeys.AddCommand(cmdKeysList)
|
cmdKeys.AddCommand(cmdKeysList)
|
||||||
cmdKeys.AddCommand(cmdKeysRemove)
|
cmdKeys.AddCommand(cmdKeysRemove)
|
||||||
|
cmdKeys.AddCommand(cmdKeysGenerate)
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdKeysList = &cobra.Command{
|
var cmdKeysList = &cobra.Command{
|
||||||
|
@ -49,6 +58,13 @@ var cmdKeysTrust = &cobra.Command{
|
||||||
Run: keysTrust,
|
Run: keysTrust,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cmdKeysGenerate = &cobra.Command{
|
||||||
|
Use: "generate [ QDN ]",
|
||||||
|
Short: "Generates a new key for a specific QDN.",
|
||||||
|
Long: "generates a new key for a specific QDN. Qualified Docker Name.",
|
||||||
|
Run: keysGenerate,
|
||||||
|
}
|
||||||
|
|
||||||
func keysRemove(cmd *cobra.Command, args []string) {
|
func keysRemove(cmd *cobra.Command, args []string) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
|
@ -114,6 +130,91 @@ func keysList(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func keysGenerate(cmd *cobra.Command, args []string) {
|
||||||
|
if len(args) < 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
fatalf("must specify a QDN")
|
||||||
|
}
|
||||||
|
|
||||||
|
// (diogo): Validate QDNs
|
||||||
|
qualifiedDN := args[0]
|
||||||
|
|
||||||
|
key, err := generateKey(qualifiedDN)
|
||||||
|
if err != nil {
|
||||||
|
fatalf("could not generate key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
template := newCertificate(qualifiedDN, qualifiedDN)
|
||||||
|
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.(crypto.Signer).Public(), key)
|
||||||
|
if err != nil {
|
||||||
|
fatalf("failed to generate certificate: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certName := filepath.Join(viper.GetString("privDir"), qualifiedDN+".crt")
|
||||||
|
certOut, err := os.Create(certName)
|
||||||
|
if err != nil {
|
||||||
|
fatalf("failed to save certificate: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer certOut.Close()
|
||||||
|
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||||
|
if err != nil {
|
||||||
|
fatalf("failed to save certificate: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCertificate(qualifiedDN, organization string) *x509.Certificate {
|
||||||
|
notBefore := time.Now()
|
||||||
|
notAfter := notBefore.Add(time.Hour * 24 * 365 * 2)
|
||||||
|
|
||||||
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
if err != nil {
|
||||||
|
fatalf("failed to generate serial number: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{organization},
|
||||||
|
CommonName: qualifiedDN,
|
||||||
|
},
|
||||||
|
NotBefore: notBefore,
|
||||||
|
NotAfter: notAfter,
|
||||||
|
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateKey(qualifiedDN string) (crypto.PrivateKey, error) {
|
||||||
|
curve := elliptic.P384()
|
||||||
|
key, err := ecdsa.GenerateKey(curve, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not generate private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyBytes, err := x509.MarshalECPrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not marshal private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyName := filepath.Join(viper.GetString("privDir"), qualifiedDN+".key")
|
||||||
|
keyOut, err := os.OpenFile(keyName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not write privatekey: %v", err)
|
||||||
|
}
|
||||||
|
defer keyOut.Close()
|
||||||
|
|
||||||
|
err = pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to encode key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
func printCert(cert *x509.Certificate) {
|
func printCert(cert *x509.Certificate) {
|
||||||
timeDifference := cert.NotAfter.Sub(time.Now())
|
timeDifference := cert.NotAfter.Sub(time.Now())
|
||||||
subjectKeyID := trustmanager.FingerprintCert(cert)
|
subjectKeyID := trustmanager.FingerprintCert(cert)
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
const configFileName string = "config"
|
const configFileName string = "config"
|
||||||
const configPath string = ".docker/trust/"
|
const configPath string = ".docker/trust/"
|
||||||
const caDir string = ".docker/trust/certificate_authorities/"
|
const caDir string = ".docker/trust/certificate_authorities/"
|
||||||
|
const privDir string = ".docker/trust/private/"
|
||||||
|
|
||||||
var caStore trustmanager.X509Store
|
var caStore trustmanager.X509Store
|
||||||
|
|
||||||
|
@ -49,12 +50,15 @@ func init() {
|
||||||
|
|
||||||
// Set up the defaults for our config
|
// Set up the defaults for our config
|
||||||
viper.SetDefault("caDir", path.Join(homeDir, path.Dir(caDir)))
|
viper.SetDefault("caDir", path.Join(homeDir, path.Dir(caDir)))
|
||||||
|
viper.SetDefault("privDir", path.Join(homeDir, path.Dir(privDir)))
|
||||||
|
|
||||||
// Get the final value for the CA directory
|
// Get the final value for the CA directory
|
||||||
finalcaDir := viper.GetString("caDir")
|
finalcaDir := viper.GetString("caDir")
|
||||||
|
finalPrivDir := viper.GetString("privDir")
|
||||||
|
|
||||||
// Ensure the existence of the CAs directory
|
// Ensure the existence of the CAs directory
|
||||||
createDirectory(finalcaDir)
|
createDirectory(finalcaDir)
|
||||||
|
createDirectory(finalPrivDir)
|
||||||
|
|
||||||
// Load all CAs that aren't expired and don't use SHA1
|
// Load all CAs that aren't expired and don't use SHA1
|
||||||
// We could easily add "return cert.IsCA && cert.BasicConstraintsValid" in order
|
// We could easily add "return cert.IsCA && cert.BasicConstraintsValid" in order
|
||||||
|
|
Loading…
Reference in New Issue