mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 19:47:54 +00:00
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:
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
3
changelog/11780.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:feature
|
||||||
|
pki: Support ed25519 as a key for the pki backend
|
||||||
|
```
|
||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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', {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user