Commit 9e386fc9 authored by Matt Holt's avatar Matt Holt

Merge pull request #652 from elcore/patch-2

https: Support ECC keys
parents 36b440c0 9099375b
......@@ -34,16 +34,7 @@ var NewACMEClient = func(email string, allowPrompts bool) (*ACMEClient, error) {
}
// The client facilitates our communication with the CA server.
var kt acme.KeyType
if rsaKeySizeToUse == Rsa2048 {
kt = acme.RSA2048
} else if rsaKeySizeToUse == Rsa4096 {
kt = acme.RSA4096
} else {
// TODO(hkjn): Support more types? Current changes are quick fix for #640.
return nil, fmt.Errorf("https: unsupported keysize")
}
client, err := acme.NewClient(CAUrl, &leUser, kt)
client, err := acme.NewClient(CAUrl, &leUser, KeyType)
if err != nil {
return nil, err
}
......
package https
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"io/ioutil"
"os"
)
// loadRSAPrivateKey loads a PEM-encoded RSA private key from file.
func loadRSAPrivateKey(file string) (*rsa.PrivateKey, error) {
// loadPrivateKey loads a PEM-encoded ECC/RSA private key from file.
func loadPrivateKey(file string) (crypto.PrivateKey, error) {
keyBytes, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
keyBlock, _ := pem.Decode(keyBytes)
return x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
switch keyBlock.Type {
case "RSA PRIVATE KEY":
return x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
case "EC PRIVATE KEY":
return x509.ParseECPrivateKey(keyBlock.Bytes)
}
return nil, errors.New("unknown private key type")
}
// saveRSAPrivateKey saves a PEM-encoded RSA private key to file.
func saveRSAPrivateKey(key *rsa.PrivateKey, file string) error {
pemKey := pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
// savePrivateKey saves a PEM-encoded ECC/RSA private key to file.
func savePrivateKey(key crypto.PrivateKey, file string) error {
var pemType string
var keyBytes []byte
switch key := key.(type) {
case *ecdsa.PrivateKey:
var err error
pemType = "EC"
keyBytes, err = x509.MarshalECPrivateKey(key)
if err != nil {
return err
}
case *rsa.PrivateKey:
pemType = "RSA"
keyBytes = x509.MarshalPKCS1PrivateKey(key)
}
pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes}
keyOut, err := os.Create(file)
if err != nil {
return err
......
......@@ -2,6 +2,9 @@ package https
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
......@@ -10,23 +13,17 @@ import (
"testing"
)
func init() {
rsaKeySizeToUse = 2048 // TODO(hkjn): Bring back support for small
// keys to speed up tests? Current changes
// are quick fix for #640.
}
func TestSaveAndLoadRSAPrivateKey(t *testing.T) {
keyFile := "test.key"
defer os.Remove(keyFile)
privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySizeToUse)
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
// test save
err = saveRSAPrivateKey(privateKey, keyFile)
err = savePrivateKey(privateKey, keyFile)
if err != nil {
t.Fatal("error saving private key:", err)
}
......@@ -45,23 +42,70 @@ func TestSaveAndLoadRSAPrivateKey(t *testing.T) {
}
// test load
loadedKey, err := loadRSAPrivateKey(keyFile)
loadedKey, err := loadPrivateKey(keyFile)
if err != nil {
t.Error("error loading private key:", err)
}
// verify loaded key is correct
if !rsaPrivateKeysSame(privateKey, loadedKey) {
if !PrivateKeysSame(privateKey, loadedKey) {
t.Error("Expected key bytes to be the same, but they weren't")
}
}
// rsaPrivateKeysSame compares the bytes of a and b and returns true if they are the same.
func rsaPrivateKeysSame(a, b *rsa.PrivateKey) bool {
return bytes.Equal(rsaPrivateKeyBytes(a), rsaPrivateKeyBytes(b))
func TestSaveAndLoadECCPrivateKey(t *testing.T) {
keyFile := "test.key"
defer os.Remove(keyFile)
privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
t.Fatal(err)
}
// test save
err = savePrivateKey(privateKey, keyFile)
if err != nil {
t.Fatal("error saving private key:", err)
}
// it doesn't make sense to test file permission on windows
if runtime.GOOS != "windows" {
// get info of the key file
info, err := os.Stat(keyFile)
if err != nil {
t.Fatal("error stating private key:", err)
}
// verify permission of key file is correct
if info.Mode().Perm() != 0600 {
t.Error("Expected key file to have permission 0600, but it wasn't")
}
}
// test load
loadedKey, err := loadPrivateKey(keyFile)
if err != nil {
t.Error("error loading private key:", err)
}
// verify loaded key is correct
if !PrivateKeysSame(privateKey, loadedKey) {
t.Error("Expected key bytes to be the same, but they weren't")
}
}
// PrivateKeysSame compares the bytes of a and b and returns true if they are the same.
func PrivateKeysSame(a, b crypto.PrivateKey) bool {
return bytes.Equal(PrivateKeyBytes(a), PrivateKeyBytes(b))
}
// rsaPrivateKeyBytes returns the bytes of DER-encoded key.
func rsaPrivateKeyBytes(key *rsa.PrivateKey) []byte {
return x509.MarshalPKCS1PrivateKey(key)
// PrivateKeyBytes returns the bytes of DER-encoded key.
func PrivateKeyBytes(key crypto.PrivateKey) []byte {
var keyBytes []byte
switch key := key.(type) {
case *rsa.PrivateKey:
keyBytes = x509.MarshalPKCS1PrivateKey(key)
case *ecdsa.PrivateKey:
keyBytes, _ = x509.MarshalECPrivateKey(key)
}
return keyBytes
}
......@@ -401,21 +401,10 @@ var (
// default port for the challenge must be forwarded to this one.
const AlternatePort = "5033"
// KeySize represents the length of a key in bits.
type KeySize int
// Key sizes are used to determine the strength of a key.
const (
Ecc224 KeySize = 224
Ecc256 = 256
Rsa2048 = 2048
Rsa4096 = 4096
)
// rsaKeySizeToUse is the size to use for new RSA keys.
// KeyType is the type to use for new keys.
// This shouldn't need to change except for in tests;
// the size can be drastically reduced for speed.
var rsaKeySizeToUse = Rsa2048
var KeyType = acme.EC384
// stopChan is used to signal the maintenance goroutine
// to terminate.
......
......@@ -3,8 +3,9 @@ package https
import (
"bufio"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/json"
"errors"
"fmt"
......@@ -21,7 +22,7 @@ import (
type User struct {
Email string
Registration *acme.RegistrationResource
key *rsa.PrivateKey
key crypto.PrivateKey
}
// GetEmail gets u's email.
......@@ -64,7 +65,7 @@ func getUser(email string) (User, error) {
}
// load their private key
user.key, err = loadRSAPrivateKey(storage.UserKeyFile(email))
user.key, err = loadPrivateKey(storage.UserKeyFile(email))
if err != nil {
return user, err
}
......@@ -83,7 +84,7 @@ func saveUser(user User) error {
}
// save private key file
err = saveRSAPrivateKey(user.key, storage.UserKeyFile(user.Email))
err = savePrivateKey(user.key, storage.UserKeyFile(user.Email))
if err != nil {
return err
}
......@@ -104,7 +105,7 @@ func saveUser(user User) error {
// instead. It does NOT prompt the user.
func newUser(email string) (User, error) {
user := User{Email: email}
privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySizeToUse)
privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
return user, errors.New("error generating private key: " + err.Error())
}
......
......@@ -114,7 +114,7 @@ func TestGetUserAlreadyExists(t *testing.T) {
}
// Assert keys are the same
if !rsaPrivateKeysSame(user.key, user2.key) {
if !PrivateKeysSame(user.key, user2.key) {
t.Error("Expected private key to be the same after loading, but it wasn't")
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment