Skip to content

Commit c03ff58

Browse files
committed
feat: add a way to represent redacted x509 private keys
If the x509 private key is set to be `******`, do not base64-encode it in the YAML representation, so it stays human-readable, clearly communicating that it is redacted. Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
1 parent c3225ee commit c03ff58

File tree

2 files changed

+78
-13
lines changed

2 files changed

+78
-13
lines changed

x509/x509.go

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ import (
2727
"time"
2828
)
2929

30+
// Redacted is a special string that is used to indicate that a private key should be YAML-marshaled without the base64 encoding.
31+
//
32+
// If the value of a private key is exactly this string (in bytes), it will be marshaled as-is into YAML, without the base64 encoding.
33+
const Redacted = "******"
34+
3035
// CertificateAuthority represents a CA.
3136
type CertificateAuthority struct {
3237
Crt *x509.Certificate
@@ -789,13 +794,18 @@ func (p *PEMEncodedCertificateAndKey) UnmarshalYAML(unmarshal func(interface{})
789794
return err
790795
}
791796

792-
decodedKey, err := base64.StdEncoding.DecodeString(aux.Key)
793-
if err != nil {
794-
return err
795-
}
796-
797797
p.Crt = decodedCrt
798-
p.Key = decodedKey
798+
799+
if aux.Key == Redacted {
800+
p.Key = []byte(Redacted)
801+
} else {
802+
decodedKey, err := base64.StdEncoding.DecodeString(aux.Key)
803+
if err != nil {
804+
return err
805+
}
806+
807+
p.Key = decodedKey
808+
}
799809

800810
return nil
801811
}
@@ -811,7 +821,12 @@ func (p *PEMEncodedCertificateAndKey) MarshalYAML() (interface{}, error) {
811821
}
812822

813823
aux.Crt = base64.StdEncoding.EncodeToString(p.Crt)
814-
aux.Key = base64.StdEncoding.EncodeToString(p.Key)
824+
825+
if string(p.Key) == Redacted {
826+
aux.Key = Redacted
827+
} else {
828+
aux.Key = base64.StdEncoding.EncodeToString(p.Key)
829+
}
815830

816831
return aux, nil
817832
}
@@ -939,12 +954,16 @@ func (p *PEMEncodedKey) UnmarshalYAML(unmarshal func(interface{}) error) error {
939954
return err
940955
}
941956

942-
decodedKey, err := base64.StdEncoding.DecodeString(aux.Key)
943-
if err != nil {
944-
return err
945-
}
957+
if aux.Key == Redacted {
958+
p.Key = []byte(Redacted)
959+
} else {
960+
decodedKey, err := base64.StdEncoding.DecodeString(aux.Key)
961+
if err != nil {
962+
return err
963+
}
946964

947-
p.Key = decodedKey
965+
p.Key = decodedKey
966+
}
948967

949968
return nil
950969
}
@@ -958,7 +977,11 @@ func (p *PEMEncodedKey) MarshalYAML() (interface{}, error) {
958977
Key string `yaml:"key"`
959978
}
960979

961-
aux.Key = base64.StdEncoding.EncodeToString(p.Key)
980+
if string(p.Key) == Redacted {
981+
aux.Key = Redacted
982+
} else {
983+
aux.Key = base64.StdEncoding.EncodeToString(p.Key)
984+
}
962985

963986
return aux, nil
964987
}

x509/x509_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package x509_test
77
import (
88
stdx509 "crypto/x509"
99
"crypto/x509/pkix"
10+
"encoding/base64"
1011
"testing"
1112
"time"
1213

@@ -327,3 +328,44 @@ func TestNewCertificateFromCSR(t *testing.T) {
327328
assert.Equal(t, []string{"os:admin"}, crt1.X509Certificate.Subject.Organization)
328329
assert.Equal(t, []string(nil), crt2.X509Certificate.Subject.Organization)
329330
}
331+
332+
func TestPEMEncodedCertificateAndKeyYAMLMarshaling(t *testing.T) {
333+
t.Parallel()
334+
335+
// when key is not set to redacted, it should be base64'd on marshaling
336+
337+
pair := &x509.PEMEncodedCertificateAndKey{
338+
Crt: []byte("crt"),
339+
Key: []byte("key"),
340+
}
341+
342+
bytes, err := yaml.Marshal(pair)
343+
require.NoError(t, err)
344+
345+
var m map[string]any
346+
347+
require.NoError(t, yaml.Unmarshal(bytes, &m))
348+
349+
assert.Equal(t, base64.StdEncoding.EncodeToString(pair.Key), m["key"])
350+
assert.Equal(t, base64.StdEncoding.EncodeToString(pair.Crt), m["crt"])
351+
352+
// when the key is set to Redacted, it should be marshaled as-is, without base64 encoding
353+
354+
pair.Key = []byte(x509.Redacted)
355+
356+
bytes, err = yaml.Marshal(pair)
357+
require.NoError(t, err)
358+
359+
require.NoError(t, yaml.Unmarshal(bytes, &m))
360+
361+
assert.Equal(t, x509.Redacted, m["key"])
362+
assert.Equal(t, base64.StdEncoding.EncodeToString(pair.Crt), m["crt"])
363+
364+
// unmarshal should work with redacted key
365+
366+
var unmarshalPair x509.PEMEncodedCertificateAndKey
367+
368+
require.NoError(t, yaml.Unmarshal(bytes, &unmarshalPair))
369+
370+
assert.Equal(t, []byte(x509.Redacted), unmarshalPair.Key)
371+
}

0 commit comments

Comments
 (0)