Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for importing RSA-PSS keys into Transit #19519

Merged
merged 2 commits into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion builtin/logical/transit/path_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions changelog/19519.txt
Original file line number Diff line number Diff line change
@@ -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.
```
10 changes: 9 additions & 1 deletion sdk/helper/keysutil/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
33 changes: 33 additions & 0 deletions sdk/helper/keysutil/util.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keysutil

import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}