"veyron/security": Load/Save private keys in PEM format
This CL adds methods to load and save ECDSA private keys in
PEM format (with ASN.1, DER data)
Change-Id: I12de7439655273b2000cfaf6cd082ca74256eae6
diff --git a/security/util.go b/security/util.go
index 0a8f9bb..8af97f2 100644
--- a/security/util.go
+++ b/security/util.go
@@ -2,15 +2,22 @@
import (
"bytes"
+ "crypto/ecdsa"
+ "crypto/x509"
"encoding/base64"
"encoding/json"
+ "encoding/pem"
+ "errors"
"fmt"
"io"
+ "io/ioutil"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vom"
)
+const ecPrivateKeyPEMType = "EC PRIVATE KEY"
+
var nullACL security.ACL
// OpenACL creates an ACL that grants access to all principals.
@@ -20,6 +27,47 @@
return acl
}
+// LoadPEMKey loads a key from 'r', assuming that it was
+// saved using SavePEMKey.
+func LoadPEMKey(r io.Reader) (interface{}, error) {
+ pemKeyBytes, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+
+ pemKey, _ := pem.Decode(pemKeyBytes)
+ if pemKey == nil {
+ return nil, errors.New("no PEM key block read")
+ }
+
+ switch pemKey.Type {
+ case ecPrivateKeyPEMType:
+ return x509.ParseECPrivateKey(pemKey.Bytes)
+ }
+ 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.
+//
+// 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,
+ }
+
+ switch k := key.(type) {
+ case *ecdsa.PrivateKey:
+ var err error
+ if pemKey.Bytes, err = x509.MarshalECPrivateKey(k); err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("key of type %T cannot be saved", k)
+ }
+ return pem.Encode(w, pemKey)
+}
+
// LoadIdentity reads a PrivateID from r, assuming that it was written using
// SaveIdentity.
//
diff --git a/security/util_test.go b/security/util_test.go
index 445a54a..1da3c31 100644
--- a/security/util_test.go
+++ b/security/util_test.go
@@ -2,6 +2,9 @@
import (
"bytes"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
"fmt"
"reflect"
"testing"
@@ -10,6 +13,26 @@
"veyron.io/veyron/veyron2/vom"
)
+func TestLoadSavePEMKey(t *testing.T) {
+ key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatalf("Failed ecdsa.GenerateKey: %v", err)
+ }
+
+ var buf bytes.Buffer
+ if err := SavePEMKey(&buf, key); err != nil {
+ t.Fatalf("Failed to save ECDSA private key: %v", err)
+ }
+
+ loadedKey, err := LoadPEMKey(&buf)
+ 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 TestLoadSaveIdentity(t *testing.T) {
id := security.FakePrivateID("test")