Skip to content

Commit c4515f1

Browse files
authored
fix: handle EC parameters before TLS private keys (#9338)
* Handle EC parameters before TLS private keys Signed-off-by: Jules Dutel <jules.dutel@numberly.com>
1 parent fb89033 commit c4515f1

3 files changed

Lines changed: 56 additions & 1 deletion

File tree

internal/gatewayapi/tls.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func parseCertsFromTLSSecretsData(secrets []*corev1.Secret) ([]*corev1.Secret, [
105105

106106
keyData := secret.Data[corev1.TLSPrivateKeyKey]
107107

108-
keyBlock, _ := pem.Decode(keyData)
108+
keyBlock := firstSupportedPrivateKeyBlock(keyData)
109109
if keyBlock == nil {
110110
errs = append(errs, fmt.Errorf("%s/%s must contain valid %s and %s, unable to decode pem data in %s",
111111
secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, corev1.TLSPrivateKeyKey))
@@ -219,6 +219,29 @@ func parseCertsFromTLSSecretsData(secrets []*corev1.Secret) ([]*corev1.Secret, [
219219
return validSecrets, certs, nil
220220
}
221221

222+
// firstSupportedPrivateKeyBlock returns the first supported private key PEM block,
223+
// skipping preceding non-key blocks such as OpenSSL's "EC PARAMETERS" block.
224+
// It returns the first block when no supported key is found so the caller can
225+
// report the unexpected block type.
226+
func firstSupportedPrivateKeyBlock(data []byte) *pem.Block {
227+
var firstBlock *pem.Block
228+
for len(data) > 0 {
229+
block, rest := pem.Decode(data)
230+
if block == nil {
231+
return firstBlock
232+
}
233+
if firstBlock == nil {
234+
firstBlock = block
235+
}
236+
switch block.Type {
237+
case "PRIVATE KEY", "RSA PRIVATE KEY", "EC PRIVATE KEY":
238+
return block
239+
}
240+
data = rest
241+
}
242+
return firstBlock
243+
}
244+
222245
// validateCertBlock parses the certificate(s) in a single PEM block and returns
223246
// an error if the block is not a valid certificate or if any certificate in it
224247
// is expired or not yet valid (outside its NotBefore/NotAfter window).

internal/gatewayapi/tls_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,37 @@ func TestValidateTLSSecretsData(t *testing.T) {
352352
}
353353
}
354354

355+
func TestValidateTLSSecretsDataWithECParameters(t *testing.T) {
356+
const ecParametersPEM = "-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END EC PARAMETERS-----\n"
357+
358+
t.Run("ec-parameters-before-private-key", func(t *testing.T) {
359+
secrets := createTestSecrets(t, []string{"ecdsa-p256-cert.pem"}, []string{"ecdsa-p256.key"})
360+
keyData := append([]byte(nil), secrets[0].Data[corev1.TLSPrivateKeyKey]...)
361+
secrets[0].Data[corev1.TLSPrivateKeyKey] = append([]byte(ecParametersPEM), keyData...)
362+
363+
validSecrets, certs, err := parseCertsFromTLSSecretsData(secrets)
364+
require.NoError(t, err)
365+
require.Len(t, validSecrets, 1)
366+
require.Len(t, certs, 1)
367+
368+
keyBlock, _ := pem.Decode(keyData)
369+
require.NotNil(t, keyBlock)
370+
require.Equal(t, pem.EncodeToMemory(keyBlock), validSecrets[0].Data[corev1.TLSPrivateKeyKey])
371+
})
372+
373+
t.Run("ec-parameters-without-private-key", func(t *testing.T) {
374+
secrets := createTestSecrets(t, []string{"ecdsa-p256-cert.pem"}, []string{"ecdsa-p256.key"})
375+
secrets[0].Data[corev1.TLSPrivateKeyKey] = []byte(ecParametersPEM)
376+
377+
validSecrets, certs, err := parseCertsFromTLSSecretsData(secrets)
378+
require.Error(t, err)
379+
require.Equal(t, "test/secret must contain valid tls.crt and tls.key, EC PARAMETERS key format found in tls.key, supported formats are PKCS1, PKCS8 or EC", err.Error())
380+
require.Equal(t, gwapiv1.ListenerReasonInvalidCertificateRef, err.Reason())
381+
require.Empty(t, validSecrets)
382+
require.Empty(t, certs)
383+
})
384+
}
385+
355386
func TestFilterValidCertificates(t *testing.T) {
356387
type testCase struct {
357388
Name string
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed Gateway TLS Secret validation rejecting valid ECDSA private keys when the `tls.key` includes an `EC PARAMETERS` PEM block before the private key.

0 commit comments

Comments
 (0)