mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-23 22:27:38 -05:00
Support ECC certificates
This commit is contained in:
parent
741880a38b
commit
9099375b11
6 changed files with 102 additions and 51 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,26 +1,52 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// rsaPrivateKeyBytes returns the bytes of DER-encoded key.
|
||||
func rsaPrivateKeyBytes(key *rsa.PrivateKey) []byte {
|
||||
return x509.MarshalPKCS1PrivateKey(key)
|
||||
// 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))
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue