mirror of https://github.com/docker/docs.git
Starting the key refactor; rename UnlockedRootKey
Signed-off-by: Diogo Monica <diogo@docker.com>
This commit is contained in:
parent
2f986f1a1b
commit
f9f11e5781
|
|
@ -39,27 +39,33 @@ func NewRootCryptoService(rootKeyStore *trustmanager.KeyFileStore, passphrase st
|
||||||
// Create is used to generate keys for targets, snapshots and timestamps
|
// Create is used to generate keys for targets, snapshots and timestamps
|
||||||
func (ccs *CryptoService) Create(role string) (*data.PublicKey, error) {
|
func (ccs *CryptoService) Create(role string) (*data.PublicKey, error) {
|
||||||
// Generates a new RSA key
|
// Generates a new RSA key
|
||||||
key, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not generate private key: %v", err)
|
return nil, fmt.Errorf("could not generate private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pemKey, err := trustmanager.KeyToPEM(key)
|
rsaPublicKey := rsaPrivKey.PublicKey
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to generate the certificate for key: %v (%s)", role, err)
|
// Using x509 to Marshal the Public key into DER encoding
|
||||||
}
|
|
||||||
rsaPublicKey := key.PublicKey
|
|
||||||
// Using x509 to Marshal the Public key into der encoding
|
|
||||||
pubBytes, err := x509.MarshalPKIXPublicKey(&rsaPublicKey)
|
pubBytes, err := x509.MarshalPKIXPublicKey(&rsaPublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("Failed to Marshal public key.")
|
return nil, errors.New("Failed to Marshal public key.")
|
||||||
}
|
}
|
||||||
|
|
||||||
tufKey := data.NewPublicKey("RSA", pubBytes)
|
tufKey := data.NewPublicKey("RSA", pubBytes)
|
||||||
|
|
||||||
// Passing in the the GUN + keyID as the name for the private key and adding it
|
// Passing in the the GUN + keyID as the name for the private key and adding it
|
||||||
// to our KeyFileStore. Final storage will be under $BASE_PATH/GUN/keyID.key
|
// to our KeyFileStore. Final storage will be under $BASE_PATH/GUN/keyID.key
|
||||||
privKeyFilename := filepath.Join(ccs.gun, tufKey.ID())
|
privKeyFilename := filepath.Join(ccs.gun, tufKey.ID())
|
||||||
ccs.keyStore.Add(privKeyFilename, pemKey)
|
|
||||||
|
// Get a PEM encoded representation of the private key
|
||||||
|
pemRSAPrivKey, err := trustmanager.KeyToPEM(rsaPrivKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to generate the certificate for key: %v (%s)", role, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the PEM-encoded private key into our keystore
|
||||||
|
ccs.keyStore.Add(privKeyFilename, pemRSAPrivKey)
|
||||||
|
|
||||||
return tufKey, nil
|
return tufKey, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
106
client/client.go
106
client/client.go
|
|
@ -39,28 +39,9 @@ const rsaKeySize int = 2048
|
||||||
/// that doesn't exist
|
/// that doesn't exist
|
||||||
var ErrRepositoryNotExist = errors.New("repository does not exist")
|
var ErrRepositoryNotExist = errors.New("repository does not exist")
|
||||||
|
|
||||||
// Client is the interface that defines the Notary Client type
|
type UnlockedSigner struct {
|
||||||
type Client interface {
|
privKey *data.PrivateKey
|
||||||
ListPrivateKeys() []string
|
signer *signed.Signer
|
||||||
GenRootKey(passphrase string) (string, error)
|
|
||||||
GetRootKey(rootKeyID string, passphrase string) (UnlockedRootKey, error)
|
|
||||||
GetRepository(gun string, baseURL string, transport http.RoundTripper) (Repository, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repository is the interface that represents a Notary Repository
|
|
||||||
type Repository interface {
|
|
||||||
Initialize(UnlockedRootKey) error
|
|
||||||
AddTarget(target Target) error
|
|
||||||
ListTargets() ([]Target, error)
|
|
||||||
GetTargetByName(name string) (Target, error)
|
|
||||||
Publish() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnlockedRootKey struct {
|
|
||||||
cipher string
|
|
||||||
pemPrivKey []byte
|
|
||||||
pemPubKey []byte
|
|
||||||
signer *signed.Signer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotaryClient struct {
|
type NotaryClient struct {
|
||||||
|
|
@ -114,8 +95,7 @@ func NewClient(baseDir string) (*NotaryClient, error) {
|
||||||
|
|
||||||
nClient := &NotaryClient{baseDir: baseDir}
|
nClient := &NotaryClient{baseDir: baseDir}
|
||||||
|
|
||||||
err := nClient.loadKeys(trustDir, rootKeysDir)
|
if err := nClient.loadKeys(trustDir, rootKeysDir); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,15 +104,13 @@ func NewClient(baseDir string) (*NotaryClient, error) {
|
||||||
|
|
||||||
// Initialize creates a new repository by using rootKey as the root Key for the
|
// Initialize creates a new repository by using rootKey as the root Key for the
|
||||||
// TUF repository.
|
// TUF repository.
|
||||||
func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.PublicKey) error {
|
func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
||||||
remote, err := getRemoteStore(r.Gun)
|
remote, err := getRemoteStore(r.Gun)
|
||||||
rawTSKey, err := remote.GetKey("timestamp")
|
rawTSKey, err := remote.GetKey("timestamp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("RawKey: ", string(rawTSKey))
|
|
||||||
|
|
||||||
parsedKey := &data.TUFKey{}
|
parsedKey := &data.TUFKey{}
|
||||||
err = json.Unmarshal(rawTSKey, parsedKey)
|
err = json.Unmarshal(rawTSKey, parsedKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -140,6 +118,7 @@ func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.Pu
|
||||||
}
|
}
|
||||||
|
|
||||||
timestampKey := data.NewPublicKey(parsedKey.Cipher(), parsedKey.Public())
|
timestampKey := data.NewPublicKey(parsedKey.Cipher(), parsedKey.Public())
|
||||||
|
rootKey := uSigner.PublicKey()
|
||||||
|
|
||||||
targetsKey, err := r.signer.Create("targets")
|
targetsKey, err := r.signer.Create("targets")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -174,21 +153,16 @@ func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.Pu
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(diogo): change to inline error catching
|
if err := kdb.AddRole(rootRole); err != nil {
|
||||||
err = kdb.AddRole(rootRole)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = kdb.AddRole(targetsRole)
|
if err := kdb.AddRole(targetsRole); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = kdb.AddRole(snapshotRole)
|
if err := kdb.AddRole(snapshotRole); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = kdb.AddRole(timestampRole)
|
if err := kdb.AddRole(timestampRole); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,7 +182,7 @@ func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.Pu
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.saveMetadata(uRootKey.signer); err != nil {
|
if err := r.saveMetadata(uSigner.signer); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -551,38 +525,46 @@ func (c *NotaryClient) ListPrivateKeys() []string {
|
||||||
|
|
||||||
// GenRootKey generates a new root key protected by a given passphrase
|
// GenRootKey generates a new root key protected by a given passphrase
|
||||||
func (c *NotaryClient) GenRootKey(passphrase string) (string, error) {
|
func (c *NotaryClient) GenRootKey(passphrase string) (string, error) {
|
||||||
|
// TODO(diogo): Refactor TUF Key creation. We should never see crypto.privatekeys
|
||||||
// Generates a new RSA key
|
// Generates a new RSA key
|
||||||
key, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("could not generate private key: %v", err)
|
return "", fmt.Errorf("could not generate private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pemKey, err := trustmanager.KeyToPEM(key)
|
// Encode the private key in PEM format since that is the final storage format
|
||||||
|
pemPrivKey, err := trustmanager.KeyToPEM(rsaPrivKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to generate the certificate for key: %v", err)
|
return "", fmt.Errorf("failed to encode the private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
tufPrivKey, err := trustmanager.RSAToPrivateKey(rsaPrivKey)
|
||||||
keyID := data.NewPrivateKey("RSA", pemKey, pemKey).ID()
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to convert private key: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
c.rootKeyStore.AddEncrypted(keyID, pemKey, passphrase)
|
c.rootKeyStore.AddEncrypted(tufPrivKey.ID(), pemPrivKey, passphrase)
|
||||||
|
|
||||||
return keyID, nil
|
return tufPrivKey.ID(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRootKey retreives a root key that includes the ID and a signer
|
// GetRootSigner retreives a root key that includes the ID and a signer
|
||||||
func (c *NotaryClient) GetRootKey(rootKeyID, passphrase string) (UnlockedRootKey, error) {
|
func (c *NotaryClient) GetRootSigner(rootKeyID, passphrase string) (*UnlockedSigner, error) {
|
||||||
pemPrivKey, err := c.rootKeyStore.GetDecrypted(rootKeyID, passphrase)
|
pemPrivKey, err := c.rootKeyStore.GetDecrypted(rootKeyID, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UnlockedRootKey{}, fmt.Errorf("could not get encrypted root key: %v", err)
|
return nil, fmt.Errorf("could not get decrypted root key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tufPrivKey, err := trustmanager.TufParsePEMPrivateKey(pemPrivKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not get parse root key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signer := signed.NewSigner(NewRootCryptoService(c.rootKeyStore, passphrase))
|
signer := signed.NewSigner(NewRootCryptoService(c.rootKeyStore, passphrase))
|
||||||
|
|
||||||
return UnlockedRootKey{
|
return &UnlockedSigner{
|
||||||
cipher: "RSA",
|
privKey: tufPrivKey,
|
||||||
pemPrivKey: pemPrivKey,
|
signer: signer}, nil
|
||||||
signer: signer}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRepository returns a new repository
|
// GetRepository returns a new repository
|
||||||
|
|
@ -604,15 +586,15 @@ func (c *NotaryClient) GetRepository(gun string, baseURL string, transport http.
|
||||||
certificateStore: c.certificateStore}, nil
|
certificateStore: c.certificateStore}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NotaryClient) InitRepository(gun string, baseURL string, transport http.RoundTripper, uRootKey UnlockedRootKey) (*NotaryRepository, error) {
|
func (c *NotaryClient) InitRepository(gun string, baseURL string, transport http.RoundTripper, uSigner *UnlockedSigner) (*NotaryRepository, error) {
|
||||||
// Creates and saves a trusted certificate for this store, with this root key
|
// Creates and saves a trusted certificate for this store, with this root key
|
||||||
rootCert, err := uRootKey.GenerateCertificate(gun)
|
rootCert, err := uSigner.GenerateCertificate(gun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.certificateStore.AddCert(rootCert)
|
c.certificateStore.AddCert(rootCert)
|
||||||
rootKey := data.NewPublicKey("RSA", trustmanager.CertToPEM(rootCert))
|
rootKey := data.NewPublicKey("RSA", trustmanager.CertToPEM(rootCert))
|
||||||
err = c.rootKeyStore.Link(uRootKey.ID(), rootKey.ID())
|
err = c.rootKeyStore.Link(uSigner.ID(), rootKey.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -633,7 +615,7 @@ func (c *NotaryClient) InitRepository(gun string, baseURL string, transport http
|
||||||
caStore: c.caStore,
|
caStore: c.caStore,
|
||||||
certificateStore: c.certificateStore}
|
certificateStore: c.certificateStore}
|
||||||
|
|
||||||
err = nRepo.Initialize(uRootKey, rootKey)
|
err = nRepo.Initialize(uSigner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -679,14 +661,18 @@ func (c *NotaryClient) loadKeys(trustDir, rootKeysDir string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID gets a consistent ID based on the PrivateKey bytes and cipher type
|
// ID gets a consistent ID based on the PrivateKey bytes and cipher type
|
||||||
func (uk *UnlockedRootKey) ID() string {
|
func (uk *UnlockedSigner) ID() string {
|
||||||
return data.NewPrivateKey(uk.cipher, uk.pemPrivKey, uk.pemPrivKey).ID()
|
return uk.PublicKey().ID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKey Returns the public key associated with the Root Key
|
||||||
|
func (uk *UnlockedSigner) PublicKey() *data.PublicKey {
|
||||||
|
return data.PublicKeyFromPrivate(*uk.privKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateCertificate
|
// GenerateCertificate
|
||||||
func (uk *UnlockedRootKey) GenerateCertificate(gun string) (*x509.Certificate, error) {
|
func (uk *UnlockedSigner) GenerateCertificate(gun string) (*x509.Certificate, error) {
|
||||||
privKeyBytes, _ := pem.Decode(uk.pemPrivKey)
|
privKey, err := x509.ParsePKCS1PrivateKey(uk.privKey.Private())
|
||||||
privKey, err := x509.ParsePKCS1PrivateKey(privKeyBytes.Bytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse root key: %v (%s)", gun, err.Error())
|
return nil, fmt.Errorf("failed to parse root key: %v (%s)", gun, err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ func TestInitRepo(t *testing.T) {
|
||||||
rootKeyID, err := client.GenRootKey("passphrase")
|
rootKeyID, err := client.GenRootKey("passphrase")
|
||||||
assert.NoError(t, err, "error generating root key: %s", err)
|
assert.NoError(t, err, "error generating root key: %s", err)
|
||||||
|
|
||||||
rootKey, err := client.GetRootKey(rootKeyID, "passphrase")
|
rootSigner, err := client.GetRootSigner(rootKeyID, "passphrase")
|
||||||
assert.NoError(t, err, "error retreiving root key: %s", err)
|
assert.NoError(t, err, "error retreiving root key: %s", err)
|
||||||
|
|
||||||
gun := "docker.com/notary"
|
gun := "docker.com/notary"
|
||||||
repo, err := client.InitRepository(gun, "", nil, rootKey)
|
repo, err := client.InitRepository(gun, "", nil, rootSigner)
|
||||||
assert.NoError(t, err, "error creating repository: %s", err)
|
assert.NoError(t, err, "error creating repository: %s", err)
|
||||||
|
|
||||||
// Inspect contents of the temporary directory
|
// Inspect contents of the temporary directory
|
||||||
|
|
@ -61,7 +61,7 @@ func TestInitRepo(t *testing.T) {
|
||||||
// Look for keys in root_keys
|
// Look for keys in root_keys
|
||||||
// There should be a file named after the key ID of the root key we
|
// There should be a file named after the key ID of the root key we
|
||||||
// passed in.
|
// passed in.
|
||||||
rootKeyFilename := rootKey.ID() + ".key"
|
rootKeyFilename := rootSigner.ID() + ".key"
|
||||||
_, err = os.Stat(filepath.Join(tempBaseDir, "private", "root_keys", rootKeyFilename))
|
_, err = os.Stat(filepath.Join(tempBaseDir, "private", "root_keys", rootKeyFilename))
|
||||||
assert.NoError(t, err, "missing root key")
|
assert.NoError(t, err, "missing root key")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,12 +106,12 @@ func tufInit(cmd *cobra.Command, args []string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf(err.Error())
|
fatalf(err.Error())
|
||||||
}
|
}
|
||||||
rootKey, err := nClient.GetRootKey(rootKeyID, "passphrase")
|
rootSigner, err := nClient.GetRootSigner(rootKeyID, "passphrase")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf(err.Error())
|
fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = nClient.InitRepository(args[0], "", t, rootKey)
|
_, err = nClient.InitRepository(args[0], "", t, rootSigner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf(err.Error())
|
fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,14 +120,6 @@ func loadCertFromPEM(pemBytes []byte) (*x509.Certificate, error) {
|
||||||
return nil, errors.New("no certificates found in PEM data")
|
return nil, errors.New("no certificates found in PEM data")
|
||||||
}
|
}
|
||||||
|
|
||||||
func FingerprintPEMCert(pemCert []byte) (string, error) {
|
|
||||||
cert, err := loadCertFromPEM(pemCert)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return FingerprintCert(cert), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FingerprintCert returns a TUF compliant fingerprint for a X509 Certificate
|
// FingerprintCert returns a TUF compliant fingerprint for a X509 Certificate
|
||||||
func FingerprintCert(cert *x509.Certificate) string {
|
func FingerprintCert(cert *x509.Certificate) string {
|
||||||
return string(fingerprintCert(cert))
|
return string(fingerprintCert(cert))
|
||||||
|
|
@ -202,6 +194,44 @@ func ParsePEMPrivateKey(pemBytes []byte) (crypto.PrivateKey, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TufParsePEMPrivateKey returns a data.PrivateKey from a PEM encoded private key. It
|
||||||
|
// only supports RSA (PKCS#1).
|
||||||
|
func TufParsePEMPrivateKey(pemBytes []byte) (*data.PrivateKey, error) {
|
||||||
|
block, _ := pem.Decode(pemBytes)
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("no valid key found")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch block.Type {
|
||||||
|
case "RSA PRIVATE KEY":
|
||||||
|
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse PEM: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tufRSAPrivateKey, err := RSAToPrivateKey(rsaPrivKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not convert crypto.PrivateKey to PrivateKey: %v", err)
|
||||||
|
}
|
||||||
|
return tufRSAPrivateKey, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported key type %q", block.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey) (*data.PrivateKey, error) {
|
||||||
|
// Get a DER-encoded representation of the PublicKey
|
||||||
|
rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a DER-encoded representation of the PrivateKey
|
||||||
|
rsaPrivBytes := x509.MarshalPKCS1PrivateKey(rsaPrivKey)
|
||||||
|
|
||||||
|
return data.NewPrivateKey("RSA", rsaPubBytes, rsaPrivBytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ParsePEMEncryptedPrivateKey returns a private key from a PEM encrypted private key. It
|
// ParsePEMEncryptedPrivateKey returns a private key from a PEM encrypted private key. It
|
||||||
// only supports RSA (PKCS#1).
|
// only supports RSA (PKCS#1).
|
||||||
func ParsePEMEncryptedPrivateKey(pemBytes []byte, passphrase string) (crypto.PrivateKey, error) {
|
func ParsePEMEncryptedPrivateKey(pemBytes []byte, passphrase string) (crypto.PrivateKey, error) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue