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
|
||||
func (ccs *CryptoService) Create(role string) (*data.PublicKey, error) {
|
||||
// Generates a new RSA key
|
||||
key, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
||||
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate private key: %v", err)
|
||||
}
|
||||
|
||||
pemKey, err := trustmanager.KeyToPEM(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate the certificate for key: %v (%s)", role, err)
|
||||
}
|
||||
rsaPublicKey := key.PublicKey
|
||||
// Using x509 to Marshal the Public key into der encoding
|
||||
rsaPublicKey := rsaPrivKey.PublicKey
|
||||
|
||||
// Using x509 to Marshal the Public key into DER encoding
|
||||
pubBytes, err := x509.MarshalPKIXPublicKey(&rsaPublicKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("Failed to Marshal public key.")
|
||||
}
|
||||
|
||||
tufKey := data.NewPublicKey("RSA", pubBytes)
|
||||
|
||||
// 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
|
||||
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
|
||||
}
|
||||
|
|
|
|||
106
client/client.go
106
client/client.go
|
|
@ -39,28 +39,9 @@ const rsaKeySize int = 2048
|
|||
/// that doesn't exist
|
||||
var ErrRepositoryNotExist = errors.New("repository does not exist")
|
||||
|
||||
// Client is the interface that defines the Notary Client type
|
||||
type Client interface {
|
||||
ListPrivateKeys() []string
|
||||
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 UnlockedSigner struct {
|
||||
privKey *data.PrivateKey
|
||||
signer *signed.Signer
|
||||
}
|
||||
|
||||
type NotaryClient struct {
|
||||
|
|
@ -114,8 +95,7 @@ func NewClient(baseDir string) (*NotaryClient, error) {
|
|||
|
||||
nClient := &NotaryClient{baseDir: baseDir}
|
||||
|
||||
err := nClient.loadKeys(trustDir, rootKeysDir)
|
||||
if err != nil {
|
||||
if err := nClient.loadKeys(trustDir, rootKeysDir); err != nil {
|
||||
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
|
||||
// TUF repository.
|
||||
func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.PublicKey) error {
|
||||
func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
||||
remote, err := getRemoteStore(r.Gun)
|
||||
rawTSKey, err := remote.GetKey("timestamp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("RawKey: ", string(rawTSKey))
|
||||
|
||||
parsedKey := &data.TUFKey{}
|
||||
err = json.Unmarshal(rawTSKey, parsedKey)
|
||||
if err != nil {
|
||||
|
|
@ -140,6 +118,7 @@ func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.Pu
|
|||
}
|
||||
|
||||
timestampKey := data.NewPublicKey(parsedKey.Cipher(), parsedKey.Public())
|
||||
rootKey := uSigner.PublicKey()
|
||||
|
||||
targetsKey, err := r.signer.Create("targets")
|
||||
if err != nil {
|
||||
|
|
@ -174,21 +153,16 @@ func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.Pu
|
|||
return err
|
||||
}
|
||||
|
||||
// TODO(diogo): change to inline error catching
|
||||
err = kdb.AddRole(rootRole)
|
||||
if err != nil {
|
||||
if err := kdb.AddRole(rootRole); err != nil {
|
||||
return err
|
||||
}
|
||||
err = kdb.AddRole(targetsRole)
|
||||
if err != nil {
|
||||
if err := kdb.AddRole(targetsRole); err != nil {
|
||||
return err
|
||||
}
|
||||
err = kdb.AddRole(snapshotRole)
|
||||
if err != nil {
|
||||
if err := kdb.AddRole(snapshotRole); err != nil {
|
||||
return err
|
||||
}
|
||||
err = kdb.AddRole(timestampRole)
|
||||
if err != nil {
|
||||
if err := kdb.AddRole(timestampRole); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +182,7 @@ func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.Pu
|
|||
return err
|
||||
}
|
||||
|
||||
if err := r.saveMetadata(uRootKey.signer); err != nil {
|
||||
if err := r.saveMetadata(uSigner.signer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -551,38 +525,46 @@ func (c *NotaryClient) ListPrivateKeys() []string {
|
|||
|
||||
// GenRootKey generates a new root key protected by a given passphrase
|
||||
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
|
||||
key, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
||||
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
||||
if err != nil {
|
||||
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 {
|
||||
return "", fmt.Errorf("failed to generate the certificate for key: %v", err)
|
||||
return "", fmt.Errorf("failed to encode the private key: %v", err)
|
||||
}
|
||||
|
||||
//
|
||||
keyID := data.NewPrivateKey("RSA", pemKey, pemKey).ID()
|
||||
tufPrivKey, err := trustmanager.RSAToPrivateKey(rsaPrivKey)
|
||||
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
|
||||
func (c *NotaryClient) GetRootKey(rootKeyID, passphrase string) (UnlockedRootKey, error) {
|
||||
// GetRootSigner retreives a root key that includes the ID and a signer
|
||||
func (c *NotaryClient) GetRootSigner(rootKeyID, passphrase string) (*UnlockedSigner, error) {
|
||||
pemPrivKey, err := c.rootKeyStore.GetDecrypted(rootKeyID, passphrase)
|
||||
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))
|
||||
|
||||
return UnlockedRootKey{
|
||||
cipher: "RSA",
|
||||
pemPrivKey: pemPrivKey,
|
||||
signer: signer}, nil
|
||||
return &UnlockedSigner{
|
||||
privKey: tufPrivKey,
|
||||
signer: signer}, nil
|
||||
}
|
||||
|
||||
// GetRepository returns a new repository
|
||||
|
|
@ -604,15 +586,15 @@ func (c *NotaryClient) GetRepository(gun string, baseURL string, transport http.
|
|||
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
|
||||
rootCert, err := uRootKey.GenerateCertificate(gun)
|
||||
rootCert, err := uSigner.GenerateCertificate(gun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.certificateStore.AddCert(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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -633,7 +615,7 @@ func (c *NotaryClient) InitRepository(gun string, baseURL string, transport http
|
|||
caStore: c.caStore,
|
||||
certificateStore: c.certificateStore}
|
||||
|
||||
err = nRepo.Initialize(uRootKey, rootKey)
|
||||
err = nRepo.Initialize(uSigner)
|
||||
if err != nil {
|
||||
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
|
||||
func (uk *UnlockedRootKey) ID() string {
|
||||
return data.NewPrivateKey(uk.cipher, uk.pemPrivKey, uk.pemPrivKey).ID()
|
||||
func (uk *UnlockedSigner) ID() string {
|
||||
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
|
||||
func (uk *UnlockedRootKey) GenerateCertificate(gun string) (*x509.Certificate, error) {
|
||||
privKeyBytes, _ := pem.Decode(uk.pemPrivKey)
|
||||
privKey, err := x509.ParsePKCS1PrivateKey(privKeyBytes.Bytes)
|
||||
func (uk *UnlockedSigner) GenerateCertificate(gun string) (*x509.Certificate, error) {
|
||||
privKey, err := x509.ParsePKCS1PrivateKey(uk.privKey.Private())
|
||||
if err != nil {
|
||||
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")
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
// Inspect contents of the temporary directory
|
||||
|
|
@ -61,7 +61,7 @@ func TestInitRepo(t *testing.T) {
|
|||
// Look for keys in root_keys
|
||||
// There should be a file named after the key ID of the root key we
|
||||
// passed in.
|
||||
rootKeyFilename := rootKey.ID() + ".key"
|
||||
rootKeyFilename := rootSigner.ID() + ".key"
|
||||
_, err = os.Stat(filepath.Join(tempBaseDir, "private", "root_keys", rootKeyFilename))
|
||||
assert.NoError(t, err, "missing root key")
|
||||
|
||||
|
|
|
|||
|
|
@ -106,12 +106,12 @@ func tufInit(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
fatalf(err.Error())
|
||||
}
|
||||
rootKey, err := nClient.GetRootKey(rootKeyID, "passphrase")
|
||||
rootSigner, err := nClient.GetRootSigner(rootKeyID, "passphrase")
|
||||
if err != nil {
|
||||
fatalf(err.Error())
|
||||
}
|
||||
|
||||
_, err = nClient.InitRepository(args[0], "", t, rootKey)
|
||||
_, err = nClient.InitRepository(args[0], "", t, rootSigner)
|
||||
if err != nil {
|
||||
fatalf(err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,14 +120,6 @@ func loadCertFromPEM(pemBytes []byte) (*x509.Certificate, error) {
|
|||
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
|
||||
func FingerprintCert(cert *x509.Certificate) string {
|
||||
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
|
||||
// only supports RSA (PKCS#1).
|
||||
func ParsePEMEncryptedPrivateKey(pemBytes []byte, passphrase string) (crypto.PrivateKey, error) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue