Add support for importing RSA-PSS keys into Transit (#19519)
* Add support for importing RSA-PSS keys in Transit Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add changelog entry Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> --------- Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This commit is contained in:
parent
480c1dc85b
commit
ab3d6d61e0
|
@ -46,7 +46,10 @@ var (
|
|||
keys = map[string]interface{}{}
|
||||
)
|
||||
|
||||
const nssFormattedEd25519Key = "MGcCAQAwFAYHKoZIzj0CAQYJKwYBBAHaRw8BBEwwSgIBAQQgfJm5R+LK4FMwGzOpemTBXksimEVOVCE8QeC+XBBfNU+hIwMhADaif7IhYx46IHcRTy1z8LeyhABep+UB8Da6olMZGx0i"
|
||||
const (
|
||||
nssFormattedEd25519Key = "MGcCAQAwFAYHKoZIzj0CAQYJKwYBBAHaRw8BBEwwSgIBAQQgfJm5R+LK4FMwGzOpemTBXksimEVOVCE8QeC+XBBfNU+hIwMhADaif7IhYx46IHcRTy1z8LeyhABep+UB8Da6olMZGx0i"
|
||||
rsaPSSFormattedKey = "MIIEvAIBADALBgkqhkiG9w0BAQoEggSoMIIEpAIBAAKCAQEAiFXSBaicB534+2qMZTVzQHMjuhb4NM9hi5H4EAFiYHEBuvm2BAk58NdBK3wiMq/p7Ewu5NQI0gJ7GlcV1MBU94U6MEmWNd0ztmlz37esEDuaCDhmLEBHKRzs8Om0bY9vczcNwcnRIYusP2KMxon3Gv2C86M2Jahig70AIq0E9C7esfrlYxFnoxUfO09XyYfiHlZY59+/dhyULp/RDIvaQ0/DqSSnYmXw8vRQ1gp6DqIzxx3j8ikUrpE7MK6348keFQj1eb83Z5w8qgIdceHHH4wbIAW7qWCPJ/vIJp8Pe1NEanlef61pDut2YcljvN79ccjX/QyqwqYv6xX2uzSlpQIDAQABAoIBACtpBCAoIVJtkv9e3EhHniR55PjWYn7SP5GEz3MtNalWokHqS/H6DBhrOcWCV5NDHx1N3qqe9xYDkzX+X6Wn/gX4RmBkte79uX8OEca8wY1DpRaT+riBWQc2vh0xlPFDuC177KX1QGFJi3V9SCzZdjSCXyV7pPyVopSm4/mmlMq5ANfN8bcHAtcArP7vPzEdckJqurjwHyzsUZJa9sk3OL3rBkKy5bmoPebE1ZQ7C+9eA4u9MKSy95WpTiqMe3rRhvr6zj4bzEvzS9M4r2EdwgAn4FyDwtGdOqtfbtSLTikb73f4MSINnWbt3YPBfRC4PGjWXIN2sMG5XYC3KH+RKbsCgYEAu0HOFInH8OtWiUY0aqRKZuo7lrBczNa5gnce3ZYnNkfrPlu1Xp0SjUkEWukznBLO0N9lvG9j3ksUDTQlPoKarJb9uf/1H0tYHhHm6mP8mH87yfVn2bLb3VPeIQYb+MXnDrwNVCAtxhuHlpnXJPldeuVKeRigHUNIEs76UMiiLqMCgYEAumJxm5NrKk0LXUQmeZolLh0lM/shg8zW7Vi3Ksz5Pe4Pcmg+hTbHjZuJwK6HesljEA0JDNkS0+5hkqiS5UDnj94XfDbi08/kKbPYA12GPVSRNTJxL8q70rFnEUZuMBeL0SKMPhEfR2z5TDDZUBoO6HBUUwgJAij1EsXrBAb0BxcCgYBKS3eKKohLi/PPjy0oynpCjtiJlvuawe7kVoLGg9aW8L3jBdvV6Bf+OmQh9bhmSggIUzo4IzHKdptECdZlEMhxhY6xh14nxmr1s0Cc6oLDtmdwX4+OjioxjB7rl1Ltxwc/j1jycbn3ieCn3e3AW7e9FNARb7XHJnSoEbq65n+CZQKBgQChLPozYAL/HIrkR0fCRmM6gmemkNeFo0CFFP+oWoJ6ZIAlHjJafmmIcmVoI0TzEG3C9pLJ8nmOnYjxCyekakEUryi9+LSkGBWlXmlBV8H7DUNYrlskyfssEs8fKDmnCuWUn3yJO8NBv+HBWkjCNRaJOIIjH0KzBHoRludJnz2tVwKBgQCsQF5lvcXefNfQojbhF+9NfyhvAc7EsMTXQhP9HEj0wVqTuuqyGyu8meXEkcQPRl6yD/yZKuMREDNNck4KV2fdGekBsh8zBgpxdHQ2DcbfxZfNgv3yoX3f0grb/ApQNJb3DVW9FVRigue8XPzFOFX/demJmkUnTg3zGFnXLXjgxg=="
|
||||
)
|
||||
|
||||
func generateKeys(t *testing.T) {
|
||||
t.Helper()
|
||||
|
@ -114,6 +117,39 @@ func TestTransit_ImportNSSEd25519Key(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTransit_ImportRSAPSS(t *testing.T) {
|
||||
generateKeys(t)
|
||||
b, s := createBackendWithStorage(t)
|
||||
|
||||
wrappingKey, err := b.getWrappingKey(context.Background(), s)
|
||||
if err != nil || wrappingKey == nil {
|
||||
t.Fatalf("failed to retrieve public wrapping key: %s", err)
|
||||
}
|
||||
privWrappingKey := wrappingKey.Keys[strconv.Itoa(wrappingKey.LatestVersion)].RSAKey
|
||||
pubWrappingKey := &privWrappingKey.PublicKey
|
||||
|
||||
rawPKCS8, err := base64.StdEncoding.DecodeString(rsaPSSFormattedKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse rsa-pss base64: %v", err)
|
||||
}
|
||||
|
||||
blob := wrapTargetPKCS8ForImport(t, pubWrappingKey, rawPKCS8, "SHA256")
|
||||
req := &logical.Request{
|
||||
Storage: s,
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "keys/rsa-pss/import",
|
||||
Data: map[string]interface{}{
|
||||
"ciphertext": blob,
|
||||
"type": "rsa-2048",
|
||||
},
|
||||
}
|
||||
|
||||
_, err = b.HandleRequest(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to import RSA-PSS private key: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransit_Import(t *testing.T) {
|
||||
generateKeys(t)
|
||||
b, s := createBackendWithStorage(t)
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
secrets/transit: Allow importing RSA-PSS OID (1.2.840.113549.1.1.10) private keys via BYOK.
|
||||
```
|
|
@ -1425,10 +1425,18 @@ func (p *Policy) Import(ctx context.Context, storage logical.Storage, key []byte
|
|||
var edErr error
|
||||
parsedPrivateKey, edErr = ParsePKCS8Ed25519PrivateKey(key)
|
||||
if edErr != nil {
|
||||
return fmt.Errorf("error parsing asymmetric key:\n - assuming contents are an ed25519 private key: %s\n - original error: %v", edErr, err)
|
||||
return fmt.Errorf("error parsing asymmetric key:\n - assuming contents are an ed25519 private key: %v\n - original error: %w", edErr, err)
|
||||
}
|
||||
|
||||
// Parsing as Ed25519-in-PKCS8-ECPrivateKey succeeded!
|
||||
} else if strings.Contains(err.Error(), oidSignatureRSAPSS.String()) {
|
||||
var rsaErr error
|
||||
parsedPrivateKey, rsaErr = ParsePKCS8RSAPSSPrivateKey(key)
|
||||
if rsaErr != nil {
|
||||
return fmt.Errorf("error parsing asymmetric key:\n - assuming contents are an RSA/PSS private key: %v\n - original error: %w", rsaErr, err)
|
||||
}
|
||||
|
||||
// Parsing as RSA-PSS in PKCS8 succeeded!
|
||||
} else {
|
||||
return fmt.Errorf("error parsing asymmetric key: %s", err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package keysutil
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
|
@ -53,6 +54,9 @@ var (
|
|||
// Other implementations may use the OID 1.3.101.110 from
|
||||
// https://datatracker.ietf.org/doc/html/rfc8410.
|
||||
oidRFC8410Ed25519 = asn1.ObjectIdentifier{1, 3, 101, 110}
|
||||
|
||||
// See crypto/x509/x509.go in the Go toolchain source distribution.
|
||||
oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
|
||||
)
|
||||
|
||||
func isEd25519OID(oid asn1.ObjectIdentifier) bool {
|
||||
|
@ -113,3 +117,32 @@ func ParsePKCS8Ed25519PrivateKey(der []byte) (key interface{}, err error) {
|
|||
|
||||
return ed25519.NewKeyFromSeed(ed25519Key.PrivateKey), nil
|
||||
}
|
||||
|
||||
// ParsePKCS8PrivateKey parses an unencrypted private key in PKCS #8, ASN.1 DER form.
|
||||
//
|
||||
// This helper only supports RSA/PSS keys (with OID 1.2.840.113549.1.1.10).
|
||||
//
|
||||
// It returns a *rsa.PrivateKey, a *ecdsa.PrivateKey, or a ed25519.PrivateKey.
|
||||
// More types might be supported in the future.
|
||||
//
|
||||
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
|
||||
func ParsePKCS8RSAPSSPrivateKey(der []byte) (key interface{}, err error) {
|
||||
var privKey pkcs8
|
||||
if _, err := asn1.Unmarshal(der, &privKey); err == nil {
|
||||
switch {
|
||||
case privKey.Algo.Algorithm.Equal(oidSignatureRSAPSS):
|
||||
// Fall through; there's no parameters here unlike ECDSA
|
||||
// containers, so we can go to parsing the inner rsaPrivateKey
|
||||
// object.
|
||||
default:
|
||||
return nil, errors.New("keysutil: failed to parse key as RSA PSS private key")
|
||||
}
|
||||
}
|
||||
|
||||
key, err = x509.ParsePKCS1PrivateKey(privKey.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("keysutil: failed to parse inner RSA PSS private key: %w", err)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue