Add support for ed25519 (#11780)

* update azure instructions

Update instructions in regards to azure AD Authentication and OIDC

* Initial pass of ed25519

* Fix typos on marshal function

* test wip

* typo

* fix tests

* missef changelog

* fix mismatch between signature and algo

* added test coverage for ed25519

* remove pkcs1 since does not exist for ed25519

* add ed25519 support to getsigner

* pull request feedback

Signed-off-by: Anner J. Bonilla <abonilla@hoyosintegrity.com>

* typo on key

Signed-off-by: Anner J. Bonilla <abonilla@hoyosintegrity.com>

* cast mistake

Signed-off-by: Anner J. Bonilla <abonilla@hoyosintegrity.com>

Co-authored-by: Jim Kalafut <jkalafut@hashicorp.com>
This commit is contained in:
Anner J. Bonilla
2021-10-05 11:28:49 -04:00
committed by GitHub
parent 0a022d1c79
commit 30fd91c018
11 changed files with 317 additions and 24 deletions

View File

@@ -5,6 +5,7 @@ import (
"context" "context"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
@@ -211,6 +212,8 @@ func TestBackend_Roles(t *testing.T) {
{"RSACSR", &rsaCAKey, &rsaCACert, true}, {"RSACSR", &rsaCAKey, &rsaCACert, true},
{"EC", &ecCAKey, &ecCACert, false}, {"EC", &ecCAKey, &ecCACert, false},
{"ECCSR", &ecCAKey, &ecCACert, true}, {"ECCSR", &ecCAKey, &ecCACert, true},
{"ED", &edCAKey, &edCACert, false},
{"EDCSR", &edCAKey, &edCACert, true},
} }
for _, tc := range cases { for _, tc := range cases {
@@ -309,6 +312,13 @@ func checkCertsAndPrivateKey(keyType string, key crypto.Signer, usage x509.KeyUs
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing EC key: %s", err) return nil, fmt.Errorf("error parsing EC key: %s", err)
} }
case "ed25519":
parsedCertBundle.PrivateKeyType = certutil.Ed25519PrivateKey
parsedCertBundle.PrivateKey = key
parsedCertBundle.PrivateKeyBytes, err = x509.MarshalPKCS8PrivateKey(key.(ed25519.PrivateKey))
if err != nil {
return nil, fmt.Errorf("error parsing Ed25519 key: %s", err)
}
} }
} }
@@ -324,6 +334,8 @@ func checkCertsAndPrivateKey(keyType string, key crypto.Signer, usage x509.KeyUs
} }
switch { switch {
case parsedCertBundle.PrivateKeyType == certutil.Ed25519PrivateKey && keyType != "ed25519":
fallthrough
case parsedCertBundle.PrivateKeyType == certutil.RSAPrivateKey && keyType != "rsa": case parsedCertBundle.PrivateKeyType == certutil.RSAPrivateKey && keyType != "rsa":
fallthrough fallthrough
case parsedCertBundle.PrivateKeyType == certutil.ECPrivateKey && keyType != "ec": case parsedCertBundle.PrivateKeyType == certutil.ECPrivateKey && keyType != "ec":
@@ -707,7 +719,7 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
generatedRSAKeys := map[int]crypto.Signer{} generatedRSAKeys := map[int]crypto.Signer{}
generatedECKeys := map[int]crypto.Signer{} generatedECKeys := map[int]crypto.Signer{}
generatedEdKeys := map[int]crypto.Signer{}
/* /*
// For the number of tests being run, a seed of 1 has been tested // For the number of tests being run, a seed of 1 has been tested
// to hit all of the various values below. However, for normal // to hit all of the various values below. However, for normal
@@ -1017,6 +1029,13 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
generatedECKeys[keyBits] = privKey generatedECKeys[keyBits] = privKey
} }
case "ed25519":
privKey, ok = generatedEdKeys[keyBits]
if !ok {
_, privKey, _ = ed25519.GenerateKey(rand.Reader)
generatedEdKeys[keyBits] = privKey
}
default: default:
panic("invalid key type: " + keyType) panic("invalid key type: " + keyType)
} }
@@ -3095,6 +3114,36 @@ func setCerts() {
Bytes: caBytes, Bytes: caBytes,
} }
rsaCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock))) rsaCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock)))
_, edk, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
marshaledKey, err = x509.MarshalPKCS8PrivateKey(edk)
if err != nil {
panic(err)
}
keyPEMBlock = &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshaledKey,
}
edCAKey = strings.TrimSpace(string(pem.EncodeToMemory(keyPEMBlock)))
if err != nil {
panic(err)
}
subjKeyID, err = certutil.GetSubjKeyID(edk)
if err != nil {
panic(err)
}
caBytes, err = x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, edk.Public(), edk)
if err != nil {
panic(err)
}
caCertPEMBlock = &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
}
edCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock)))
} }
func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) {
@@ -3281,4 +3330,6 @@ var (
rsaCACert string rsaCACert string
ecCAKey string ecCAKey string
ecCACert string ecCACert string
edCAKey string
edCACert string
) )

View File

@@ -3,6 +3,7 @@ package pki
import ( import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
@@ -49,7 +50,7 @@ func TestBackend_CA_Steps(t *testing.T) {
client := cluster.Cores[0].Client client := cluster.Cores[0].Client
// Set RSA/EC CA certificates // Set RSA/EC CA certificates
var rsaCAKey, rsaCACert, ecCAKey, ecCACert string var rsaCAKey, rsaCACert, ecCAKey, ecCACert, edCAKey, edCACert string
{ {
cak, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) cak, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil { if err != nil {
@@ -119,10 +120,40 @@ func TestBackend_CA_Steps(t *testing.T) {
Bytes: caBytes, Bytes: caBytes,
} }
rsaCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock))) rsaCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock)))
_, edk, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
marshaledKey, err = x509.MarshalPKCS8PrivateKey(edk)
if err != nil {
panic(err)
}
keyPEMBlock = &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshaledKey,
}
edCAKey = strings.TrimSpace(string(pem.EncodeToMemory(keyPEMBlock)))
if err != nil {
panic(err)
}
_, err = certutil.GetSubjKeyID(edk)
if err != nil {
panic(err)
}
caBytes, err = x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, edk.Public(), edk)
if err != nil {
panic(err)
}
caCertPEMBlock = &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
}
edCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock)))
} }
// Setup backends // Setup backends
var rsaRoot, rsaInt, ecRoot, ecInt *backend var rsaRoot, rsaInt, ecRoot, ecInt, edRoot, edInt *backend
{ {
if err := client.Sys().Mount("rsaroot", &api.MountInput{ if err := client.Sys().Mount("rsaroot", &api.MountInput{
Type: "pki", Type: "pki",
@@ -167,6 +198,28 @@ func TestBackend_CA_Steps(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
ecInt = b ecInt = b
if err := client.Sys().Mount("ed25519root", &api.MountInput{
Type: "pki",
Config: api.MountConfigInput{
DefaultLeaseTTL: "16h",
MaxLeaseTTL: "60h",
},
}); err != nil {
t.Fatal(err)
}
edRoot = b
if err := client.Sys().Mount("ed25519int", &api.MountInput{
Type: "pki",
Config: api.MountConfigInput{
DefaultLeaseTTL: "16h",
MaxLeaseTTL: "60h",
},
}); err != nil {
t.Fatal(err)
}
edInt = b
} }
t.Run("teststeps", func(t *testing.T) { t.Run("teststeps", func(t *testing.T) {
@@ -188,6 +241,15 @@ func TestBackend_CA_Steps(t *testing.T) {
subClient.SetToken(client.Token()) subClient.SetToken(client.Token())
runSteps(t, ecRoot, ecInt, subClient, "ecroot/", "ecint/", ecCACert, ecCAKey) runSteps(t, ecRoot, ecInt, subClient, "ecroot/", "ecint/", ecCACert, ecCAKey)
}) })
t.Run("ed25519", func(t *testing.T) {
t.Parallel()
subClient, err := client.Clone()
if err != nil {
t.Fatal(err)
}
subClient.SetToken(client.Token())
runSteps(t, edRoot, edInt, subClient, "ed25519root/", "ed25519int/", edCACert, edCAKey)
})
}) })
} }

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
@@ -597,6 +598,18 @@ func signCert(b *backend,
pubKey.Params().BitSize)} pubKey.Params().BitSize)}
} }
case "ed25519":
// Verify that the key matches the role type
if csr.PublicKeyAlgorithm != x509.PublicKeyAlgorithm(x509.Ed25519) {
return nil, errutil.UserError{Err: fmt.Sprintf(
"role requires keys of type %s",
data.role.KeyType)}
}
_, ok := csr.PublicKey.(ed25519.PublicKey)
if !ok {
return nil, errutil.UserError{Err: "could not parse CSR's public key"}
}
case "any": case "any":
// We only care about running RSA < 2048 bit checks, so if not RSA // We only care about running RSA < 2048 bit checks, so if not RSA
// break out // break out

View File

@@ -270,8 +270,8 @@ Set to 384 for SHA384 and 512 for SHA512.
Type: framework.TypeString, Type: framework.TypeString,
Default: "rsa", Default: "rsa",
Description: `The type of key to use; defaults to RSA. "rsa" Description: `The type of key to use; defaults to RSA. "rsa"
and "ec" are the only valid values.`, "ec" and "ed25519" are the only valid values.`,
AllowedValues: []interface{}{"rsa", "ec"}, AllowedValues: []interface{}{"rsa", "ec", "ed25519"},
DisplayAttrs: &framework.DisplayAttributes{ DisplayAttrs: &framework.DisplayAttributes{
Value: "rsa", Value: "rsa",
}, },

View File

@@ -193,8 +193,8 @@ protection use. Defaults to false.`,
Type: framework.TypeString, Type: framework.TypeString,
Default: "rsa", Default: "rsa",
Description: `The type of key to use; defaults to RSA. "rsa" Description: `The type of key to use; defaults to RSA. "rsa"
and "ec" are the only valid values.`, "ec" and "ed25519" are the only valid values.`,
AllowedValues: []interface{}{"rsa", "ec"}, AllowedValues: []interface{}{"rsa", "ec", "ed25519"},
}, },
"key_bits": { "key_bits": {

3
changelog/11780.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:feature
pki: Support ed25519 as a key for the pki backend
```

View File

@@ -3,6 +3,7 @@ package certutil
import ( import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
@@ -36,6 +37,8 @@ func TestCertBundleConversion(t *testing.T) {
refreshECCertBundleWithChain(), refreshECCertBundleWithChain(),
refreshEC8CertBundle(), refreshEC8CertBundle(),
refreshEC8CertBundleWithChain(), refreshEC8CertBundleWithChain(),
refreshEd255198CertBundle(),
refreshEd255198CertBundleWithChain(),
} }
for i, cbut := range cbuts { for i, cbut := range cbuts {
@@ -75,6 +78,8 @@ func BenchmarkCertBundleParsing(b *testing.B) {
refreshECCertBundleWithChain(), refreshECCertBundleWithChain(),
refreshEC8CertBundle(), refreshEC8CertBundle(),
refreshEC8CertBundleWithChain(), refreshEC8CertBundleWithChain(),
refreshEd255198CertBundle(),
refreshEd255198CertBundleWithChain(),
} }
for i, cbut := range cbuts { for i, cbut := range cbuts {
@@ -103,6 +108,8 @@ func TestCertBundleParsing(t *testing.T) {
refreshECCertBundleWithChain(), refreshECCertBundleWithChain(),
refreshEC8CertBundle(), refreshEC8CertBundle(),
refreshEC8CertBundleWithChain(), refreshEC8CertBundleWithChain(),
refreshEd255198CertBundle(),
refreshEd255198CertBundleWithChain(),
} }
for i, cbut := range cbuts { for i, cbut := range cbuts {
@@ -179,6 +186,10 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund
if pcbut.PrivateKeyType != ECPrivateKey { if pcbut.PrivateKeyType != ECPrivateKey {
return fmt.Errorf("parsed bundle has wrong pkcs8 private key type: %v, should be 'ec' (%v)", pcbut.PrivateKeyType, ECPrivateKey) return fmt.Errorf("parsed bundle has wrong pkcs8 private key type: %v, should be 'ec' (%v)", pcbut.PrivateKeyType, ECPrivateKey)
} }
case privEd255198KeyPem:
if pcbut.PrivateKeyType != Ed25519PrivateKey {
return fmt.Errorf("parsed bundle has wrong pkcs8 private key type: %v, should be 'ed25519' (%v)", pcbut.PrivateKeyType, ECPrivateKey)
}
default: default:
return fmt.Errorf("parsed bundle has unknown private key type") return fmt.Errorf("parsed bundle has unknown private key type")
} }
@@ -221,6 +232,10 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund
if cb.PrivateKey != privECKeyPem && cb.PrivateKey != privEC8KeyPem { if cb.PrivateKey != privECKeyPem && cb.PrivateKey != privEC8KeyPem {
return fmt.Errorf("bundle private key does not match") return fmt.Errorf("bundle private key does not match")
} }
case Ed25519PrivateKey:
if cb.PrivateKey != privEd255198KeyPem {
return fmt.Errorf("bundle private key does not match")
}
default: default:
return fmt.Errorf("certBundle has unknown private key type") return fmt.Errorf("certBundle has unknown private key type")
} }
@@ -245,6 +260,7 @@ func TestCSRBundleConversion(t *testing.T) {
csrbuts := []*CSRBundle{ csrbuts := []*CSRBundle{
refreshRSACSRBundle(), refreshRSACSRBundle(),
refreshECCSRBundle(), refreshECCSRBundle(),
refreshEd25519CSRBundle(),
} }
for _, csrbut := range csrbuts { for _, csrbut := range csrbuts {
@@ -294,6 +310,10 @@ func compareCSRBundleToParsedCSRBundle(csrbut *CSRBundle, pcsrbut *ParsedCSRBund
if pcsrbut.PrivateKeyType != ECPrivateKey { if pcsrbut.PrivateKeyType != ECPrivateKey {
return fmt.Errorf("parsed bundle has wrong private key type") return fmt.Errorf("parsed bundle has wrong private key type")
} }
case privEd255198KeyPem:
if pcsrbut.PrivateKeyType != Ed25519PrivateKey {
return fmt.Errorf("parsed bundle has wrong private key type")
}
default: default:
return fmt.Errorf("parsed bundle has unknown private key type") return fmt.Errorf("parsed bundle has unknown private key type")
} }
@@ -325,6 +345,13 @@ func compareCSRBundleToParsedCSRBundle(csrbut *CSRBundle, pcsrbut *ParsedCSRBund
if csrb.PrivateKey != privECKeyPem { if csrb.PrivateKey != privECKeyPem {
return fmt.Errorf("bundle ec private key does not match") return fmt.Errorf("bundle ec private key does not match")
} }
case "ed25519":
if pcsrbut.PrivateKeyType != Ed25519PrivateKey {
return fmt.Errorf("bundle has wrong private key type")
}
if csrb.PrivateKey != privEd255198KeyPem {
return fmt.Errorf("bundle ed25519 private key does not match")
}
default: default:
return fmt.Errorf("bundle has unknown private key type") return fmt.Errorf("bundle has unknown private key type")
} }
@@ -476,6 +503,30 @@ func refreshECCertBundleWithChain() *CertBundle {
return ret return ret
} }
func refreshEd255198CertBundle() *CertBundle {
initTest.Do(setCerts)
return &CertBundle{
Certificate: certEd25519Pem,
PrivateKey: privEd255198KeyPem,
CAChain: []string{issuingCaChainPem[0]},
}
}
func refreshEd255198CertBundleWithChain() *CertBundle {
initTest.Do(setCerts)
ret := refreshEd255198CertBundle()
ret.CAChain = issuingCaChainPem
return ret
}
func refreshEd25519CSRBundle() *CSRBundle {
initTest.Do(setCerts)
return &CSRBundle{
CSR: csrEd25519Pem,
PrivateKey: privEd255198KeyPem,
}
}
func refreshRSACSRBundle() *CSRBundle { func refreshRSACSRBundle() *CSRBundle {
initTest.Do(setCerts) initTest.Do(setCerts)
return &CSRBundle{ return &CSRBundle{
@@ -714,18 +765,81 @@ func setCerts() {
privRSA8KeyPem = strings.TrimSpace(string(pem.EncodeToMemory(keyPEMBlock))) privRSA8KeyPem = strings.TrimSpace(string(pem.EncodeToMemory(keyPEMBlock)))
} }
// Ed25519 generation
{
pubkey, privkey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
subjKeyID, err := GetSubjKeyID(privkey)
if err != nil {
panic(err)
}
certTemplate := &x509.Certificate{
Subject: pkix.Name{
CommonName: "localhost",
},
SubjectKeyId: subjKeyID,
DNSNames: []string{"localhost"},
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement,
SerialNumber: big.NewInt(mathrand.Int63()),
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(262980 * time.Hour),
}
csrTemplate := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: "localhost",
},
DNSNames: []string{"localhost"},
}
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, privkey)
if err != nil {
panic(err)
}
csrPEMBlock := &pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csrBytes,
}
csrEd25519Pem = strings.TrimSpace(string(pem.EncodeToMemory(csrPEMBlock)))
certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, intCert, pubkey, intKey)
if err != nil {
panic(err)
}
certPEMBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certEd25519Pem = strings.TrimSpace(string(pem.EncodeToMemory(certPEMBlock)))
marshaledKey, err := x509.MarshalPKCS8PrivateKey(privkey)
if err != nil {
panic(err)
}
keyPEMBlock := &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshaledKey,
}
privEd255198KeyPem = strings.TrimSpace(string(pem.EncodeToMemory(keyPEMBlock)))
}
issuingCaChainPem = []string{intCertPEM, caCertPEM} issuingCaChainPem = []string{intCertPEM, caCertPEM}
} }
var ( var (
initTest sync.Once initTest sync.Once
privRSA8KeyPem string privRSA8KeyPem string
privRSAKeyPem string privRSAKeyPem string
csrRSAPem string csrRSAPem string
certRSAPem string certRSAPem string
privECKeyPem string privEd255198KeyPem string
csrECPem string csrEd25519Pem string
privEC8KeyPem string certEd25519Pem string
certECPem string privECKeyPem string
issuingCaChainPem []string csrECPem string
privEC8KeyPem string
certECPem string
issuingCaChainPem []string
) )

View File

@@ -164,6 +164,10 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) {
parsedBundle.PrivateKey = signer parsedBundle.PrivateKey = signer
parsedBundle.PrivateKeyType = ECPrivateKey parsedBundle.PrivateKeyType = ECPrivateKey
parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKeyBytes = pemBlock.Bytes
case ed25519.PrivateKey:
parsedBundle.PrivateKey = signer
parsedBundle.PrivateKeyType = Ed25519PrivateKey
parsedBundle.PrivateKeyBytes = pemBlock.Bytes
} }
} else if certificates, err := x509.ParseCertificates(pemBlock.Bytes); err == nil { } else if certificates, err := x509.ParseCertificates(pemBlock.Bytes); err == nil {
certPath = append(certPath, &CertBlock{ certPath = append(certPath, &CertBlock{
@@ -246,6 +250,16 @@ func generatePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyC
if err != nil { if err != nil {
return errutil.InternalError{Err: fmt.Sprintf("error marshalling EC private key: %v", err)} return errutil.InternalError{Err: fmt.Sprintf("error marshalling EC private key: %v", err)}
} }
case "ed25519":
privateKeyType = Ed25519PrivateKey
_, privateKey, err = ed25519.GenerateKey(randReader)
if err != nil {
return errutil.InternalError{Err: fmt.Sprintf("error generating ed25519 private key: %v", err)}
}
privateKeyBytes, err = x509.MarshalPKCS8PrivateKey(privateKey.(ed25519.PrivateKey))
if err != nil {
return errutil.InternalError{Err: fmt.Sprintf("error marshalling Ed25519 private key: %v", err)}
}
default: default:
return errutil.UserError{Err: fmt.Sprintf("unknown key type: %s", keyType)} return errutil.UserError{Err: fmt.Sprintf("unknown key type: %s", keyType)}
} }
@@ -309,7 +323,16 @@ func ComparePublicKeys(key1Iface, key2Iface crypto.PublicKey) (bool, error) {
return false, nil return false, nil
} }
return true, nil return true, nil
case ed25519.PublicKey:
key1 := key1Iface.(ed25519.PublicKey)
key2, ok := key2Iface.(ed25519.PublicKey)
if !ok {
return false, fmt.Errorf("key types do not match: %T and %T", key1Iface, key2Iface)
}
if !key1.Equal(key2) {
return false, nil
}
return true, nil
default: default:
return false, fmt.Errorf("cannot compare key with type %T", key1Iface) return false, fmt.Errorf("cannot compare key with type %T", key1Iface)
} }
@@ -521,7 +544,7 @@ func ValidateKeyTypeLength(keyType string, keyBits int) error {
default: default:
return fmt.Errorf("unsupported bit length for EC key: %d", keyBits) return fmt.Errorf("unsupported bit length for EC key: %d", keyBits)
} }
case "any": case "any", "ed25519":
default: default:
return fmt.Errorf("unknown key type %s", keyType) return fmt.Errorf("unknown key type %s", keyType)
} }
@@ -617,6 +640,8 @@ func createCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertB
case 512: case 512:
certTemplate.SignatureAlgorithm = x509.SHA512WithRSA certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
} }
case Ed25519PrivateKey:
certTemplate.SignatureAlgorithm = x509.PureEd25519
case ECPrivateKey: case ECPrivateKey:
switch data.Params.SignatureBits { switch data.Params.SignatureBits {
case 256: case 256:
@@ -651,6 +676,8 @@ func createCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertB
case 512: case 512:
certTemplate.SignatureAlgorithm = x509.SHA512WithRSA certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
} }
case "ed25519":
certTemplate.SignatureAlgorithm = x509.PureEd25519
case "ec": case "ec":
switch data.Params.SignatureBits { switch data.Params.SignatureBits {
case 256: case 256:
@@ -754,6 +781,8 @@ func createCSR(data *CreationBundle, addBasicConstraints bool, randReader io.Rea
csrTemplate.SignatureAlgorithm = x509.SHA256WithRSA csrTemplate.SignatureAlgorithm = x509.SHA256WithRSA
case "ec": case "ec":
csrTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256 csrTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
case "ed25519":
csrTemplate.SignatureAlgorithm = x509.PureEd25519
} }
csr, err := x509.CreateCertificateRequest(randReader, csrTemplate, result.PrivateKey) csr, err := x509.CreateCertificateRequest(randReader, csrTemplate, result.PrivateKey)

View File

@@ -12,6 +12,7 @@ import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
@@ -56,6 +57,7 @@ const (
UnknownPrivateKey PrivateKeyType = "" UnknownPrivateKey PrivateKeyType = ""
RSAPrivateKey PrivateKeyType = "rsa" RSAPrivateKey PrivateKeyType = "rsa"
ECPrivateKey PrivateKeyType = "ec" ECPrivateKey PrivateKeyType = "ec"
Ed25519PrivateKey PrivateKeyType = "ed25519"
) )
// TLSUsage controls whether the intended usage of a *tls.Config // TLSUsage controls whether the intended usage of a *tls.Config
@@ -185,6 +187,8 @@ func (c *CertBundle) ToParsedCertBundle() (*ParsedCertBundle, error) {
c.PrivateKeyType = ECPrivateKey c.PrivateKeyType = ECPrivateKey
case RSAPrivateKey: case RSAPrivateKey:
c.PrivateKeyType = RSAPrivateKey c.PrivateKeyType = RSAPrivateKey
case Ed25519PrivateKey:
c.PrivateKeyType = Ed25519PrivateKey
} }
default: default:
return nil, errutil.UserError{Err: fmt.Sprintf("Unsupported key block type: %s", pemBlock.Type)} return nil, errutil.UserError{Err: fmt.Sprintf("Unsupported key block type: %s", pemBlock.Type)}
@@ -290,6 +294,8 @@ func (p *ParsedCertBundle) ToCertBundle() (*CertBundle, error) {
block.Type = string(ECBlock) block.Type = string(ECBlock)
case RSAPrivateKey: case RSAPrivateKey:
block.Type = string(PKCS1Block) block.Type = string(PKCS1Block)
case Ed25519PrivateKey:
block.Type = string(PKCS8Block)
} }
} }
@@ -380,7 +386,7 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) {
case PKCS8Block: case PKCS8Block:
if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil {
switch k := k.(type) { switch k := k.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey: case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
return k.(crypto.Signer), nil return k.(crypto.Signer), nil
default: default:
return nil, errutil.UserError{Err: "Found unknown private key type in pkcs#8 wrapping"} return nil, errutil.UserError{Err: "Found unknown private key type in pkcs#8 wrapping"}
@@ -411,6 +417,8 @@ func getPKCS8Type(bs []byte) (PrivateKeyType, error) {
return ECPrivateKey, nil return ECPrivateKey, nil
case *rsa.PrivateKey: case *rsa.PrivateKey:
return RSAPrivateKey, nil return RSAPrivateKey, nil
case ed25519.PrivateKey:
return Ed25519PrivateKey, nil
default: default:
return UnknownPrivateKey, errutil.UserError{Err: "Found unknown private key type in pkcs#8 wrapping"} return UnknownPrivateKey, errutil.UserError{Err: "Found unknown private key type in pkcs#8 wrapping"}
} }
@@ -443,6 +451,9 @@ func (c *CSRBundle) ToParsedCSRBundle() (*ParsedCSRBundle, error) {
} else if _, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes); err == nil { } else if _, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes); err == nil {
result.PrivateKeyType = RSAPrivateKey result.PrivateKeyType = RSAPrivateKey
c.PrivateKeyType = "rsa" c.PrivateKeyType = "rsa"
} else if _, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil {
result.PrivateKeyType = Ed25519PrivateKey
c.PrivateKeyType = "ed25519"
} else { } else {
return nil, errutil.UserError{Err: fmt.Sprintf("Unknown private key type in bundle: %s", c.PrivateKeyType)} return nil, errutil.UserError{Err: fmt.Sprintf("Unknown private key type in bundle: %s", c.PrivateKeyType)}
} }
@@ -491,6 +502,9 @@ func (p *ParsedCSRBundle) ToCSRBundle() (*CSRBundle, error) {
case ECPrivateKey: case ECPrivateKey:
result.PrivateKeyType = "ec" result.PrivateKeyType = "ec"
block.Type = "EC PRIVATE KEY" block.Type = "EC PRIVATE KEY"
case Ed25519PrivateKey:
result.PrivateKeyType = "ed25519"
block.Type = "PRIVATE KEY"
default: default:
return nil, errutil.InternalError{Err: "Could not determine private key type when creating block"} return nil, errutil.InternalError{Err: "Could not determine private key type when creating block"}
} }
@@ -525,8 +539,15 @@ func (p *ParsedCSRBundle) getSigner() (crypto.Signer, error) {
return nil, errutil.UserError{Err: fmt.Sprintf("Unable to parse CA's private RSA key: %s", err)} return nil, errutil.UserError{Err: fmt.Sprintf("Unable to parse CA's private RSA key: %s", err)}
} }
case Ed25519PrivateKey:
signerd, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes)
signer = signerd.(ed25519.PrivateKey)
if err != nil {
return nil, errutil.UserError{Err: fmt.Sprintf("Unable to parse CA's private Ed25519 key: %s", err)}
}
default: default:
return nil, errutil.UserError{Err: "Unable to determine type of private key; only RSA and EC are supported"} return nil, errutil.UserError{Err: "Unable to determine type of private key; only RSA, Ed25519 and EC are supported"}
} }
return signer, nil return signer, nil
} }

View File

@@ -128,7 +128,7 @@ export default Certificate.extend({
}), }),
keyType: attr('string', { keyType: attr('string', {
possibleValues: ['rsa', 'ec'], possibleValues: ['rsa', 'ec','ed25519'],
defaultValue: 'rsa', defaultValue: 'rsa',
}), }),
keyBits: attr('number', { keyBits: attr('number', {

View File

@@ -463,7 +463,7 @@ can be set in a CSR are supported.
PEM-encoded DER, depending on the value of `format`. The other option is PEM-encoded DER, depending on the value of `format`. The other option is
`pkcs8` which will return the key marshalled as PEM-encoded PKCS8. `pkcs8` which will return the key marshalled as PEM-encoded PKCS8.
- `key_type` `(string: "rsa")`  Specifies the desired key type; must be `rsa` - `key_type` `(string: "rsa")`  Specifies the desired key type; must be `rsa`, `ed25519`
or `ec`. or `ec`.
- `key_bits` `(int: 2048)`  Specifies the number of bits to use. This must be - `key_bits` `(int: 2048)`  Specifies the number of bits to use. This must be
@@ -1084,7 +1084,7 @@ overwrite the existing cert/key with new values.
PEM-encoded DER, depending on the value of `format`. The other option is PEM-encoded DER, depending on the value of `format`. The other option is
`pkcs8` which will return the key marshalled as PEM-encoded PKCS8. `pkcs8` which will return the key marshalled as PEM-encoded PKCS8.
- `key_type` `(string: "rsa")`  Specifies the desired key type; must be `rsa` - `key_type` `(string: "rsa")`  Specifies the desired key type; must be `rsa`, `ed25519`
or `ec`. or `ec`.
- `key_bits` `(int: 2048)`  Specifies the number of bits to use. This must be - `key_bits` `(int: 2048)`  Specifies the number of bits to use. This must be