veyron/veyron/security/agent: Encryption of PEM block in Load/SavePEMKey.
* This will be used by the agent server to save and load private keys.
Change-Id: I31e7ee239160536b49be0d58cc32291d4933a48d
diff --git a/security/util.go b/security/util.go
index 8af97f2..0ae070f 100644
--- a/security/util.go
+++ b/security/util.go
@@ -3,6 +3,7 @@
import (
"bytes"
"crypto/ecdsa"
+ "crypto/rand"
"crypto/x509"
"encoding/base64"
"encoding/json"
@@ -27,9 +28,11 @@
return acl
}
-// LoadPEMKey loads a key from 'r', assuming that it was
-// saved using SavePEMKey.
-func LoadPEMKey(r io.Reader) (interface{}, error) {
+// LoadPEMKey loads a key from 'r', assuming that it was saved using SavePEMKey
+// and the specified passphrase 'passphrase'.
+// If passphrase is nil, the key will still be encrypted with a random salt, but
+// this will offer no protection as the salt is stored with the PEMKey.
+func LoadPEMKey(r io.Reader, passphrase []byte) (interface{}, error) {
pemKeyBytes, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
@@ -40,31 +43,39 @@
return nil, errors.New("no PEM key block read")
}
+ data, err := x509.DecryptPEMBlock(pemKey, passphrase)
+ if err != nil {
+ return nil, err
+ }
+
switch pemKey.Type {
case ecPrivateKeyPEMType:
- return x509.ParseECPrivateKey(pemKey.Bytes)
+ return x509.ParseECPrivateKey(data)
}
return nil, fmt.Errorf("PEM key block has an unrecognized type: %v", pemKey.Type)
}
-// SavePEMKey marshals 'key' and saves the bytes to 'w' in PEM format.
+// SavePEMKey marshals 'key', encrypts it using 'passphrase', and saves the bytes to 'w' in PEM format.
//
// For example, if key is an ECDSA private key, it will be marshaled
-// in ASN.1, DER format and then written in a PEM block.
-func SavePEMKey(w io.Writer, key interface{}) error {
- pemKey := &pem.Block{
- Type: ecPrivateKeyPEMType,
- }
-
+// in ASN.1, DER format, encrypted, and then written in a PEM block.
+func SavePEMKey(w io.Writer, key interface{}, passphrase []byte) error {
+ var data []byte
switch k := key.(type) {
case *ecdsa.PrivateKey:
var err error
- if pemKey.Bytes, err = x509.MarshalECPrivateKey(k); err != nil {
+ if data, err = x509.MarshalECPrivateKey(k); err != nil {
return err
}
default:
return fmt.Errorf("key of type %T cannot be saved", k)
}
+
+ pemKey, err := x509.EncryptPEMBlock(rand.Reader, ecPrivateKeyPEMType, data, passphrase, x509.PEMCipherAES256)
+ if err != nil {
+ return fmt.Errorf("failed to encrypt pem block: %v", err)
+ }
+
return pem.Encode(w, pemKey)
}
diff --git a/security/util_test.go b/security/util_test.go
index 0e32e1d..bd55641 100644
--- a/security/util_test.go
+++ b/security/util_test.go
@@ -20,11 +20,41 @@
}
var buf bytes.Buffer
- if err := SavePEMKey(&buf, key); err != nil {
+ if err := SavePEMKey(&buf, key, nil); err != nil {
t.Fatalf("Failed to save ECDSA private key: %v", err)
}
- loadedKey, err := LoadPEMKey(&buf)
+ loadedKey, err := LoadPEMKey(&buf, nil)
+ if err != nil {
+ t.Fatalf("Failed to load ECDSA private key: %v", err)
+ }
+ if !reflect.DeepEqual(loadedKey, key) {
+ t.Fatalf("Got key %v, but want %v", loadedKey, key)
+ }
+}
+
+func TestLoadSavePEMKeyWithPassword(t *testing.T) {
+ pass := []byte("openSesame")
+ incorrect_pass := []byte("wrongPassword")
+ key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatalf("Failed ecdsa.GenerateKey: %v", err)
+ }
+ var buf bytes.Buffer
+ // Test incorrect password.
+ if err := SavePEMKey(&buf, key, pass); err != nil {
+ t.Fatalf("Failed to save ECDSA private key: %v", err)
+ }
+ loadedKey, err := LoadPEMKey(&buf, incorrect_pass)
+ if err == nil {
+ t.Errorf("loaded ECDSA private key with incorrect password")
+ }
+
+ // Test correct password.
+ if err := SavePEMKey(&buf, key, pass); err != nil {
+ t.Fatalf("Failed to save ECDSA private key: %v", err)
+ }
+ loadedKey, err = LoadPEMKey(&buf, pass)
if err != nil {
t.Fatalf("Failed to load ECDSA private key: %v", err)
}