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 pre-existing secrets for CTLog. #429

Merged
merged 3 commits into from
Oct 18, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
34 changes: 33 additions & 1 deletion cmd/ctlog/createctconfig/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"errors"
"flag"
"fmt"
"log"
Expand All @@ -37,6 +38,7 @@ import (
"google.golang.org/protobuf/types/known/anypb"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"knative.dev/pkg/logging"
"knative.dev/pkg/signals"
Expand All @@ -55,6 +57,7 @@ const (
var (
cmname = flag.String("configmap", "ctlog-config", "Name of the configmap where the treeID lives")
configInSecret = flag.Bool("config-in-secret", false, "If set to true, create the ctlog configuration proto into a secret specified in ctlog-secrets under key 'config'")
privateKeySecret = flag.String("private-secret", "", "If there's an existing private key that should be used, read it from this secret, decrypt with the key-password and use it instead of creating a new one.")
secretName = flag.String("secret", "ctlog-secrets", "Name of the secret to create for the keyfiles")
pubKeySecretName = flag.String("pubkeysecret", "ctlog-public-key", "Name of the secret to create containing only the public key")
ctlogPrefix = flag.String("log-prefix", "sigstorescaffolding", "Prefix to append to the url. This is basically the name of the log.")
Expand Down Expand Up @@ -150,7 +153,16 @@ func main() {
if existingSecret.Data[privateKey] == nil ||
existingSecret.Data[publicKey] == nil ||
(existingSecret.Data[configKey] == nil && existingCMConfig == nil) {
ctlogConfig, err := createConfigWithKeys(ctx, *keyType)
var ctlogConfig *ctlog.CTLogConfig
var err error
if *privateKeySecret != "" {
// We have an existing private key, use it instead of creating
// a new one.
ctlogConfig, err = createConfigFromExistingSecret(ctx, nsSecret, *privateKeySecret)
} else {
// Create a fresh private key.
ctlogConfig, err = createConfigWithKeys(ctx, *keyType)
}
if err != nil {
logging.FromContext(ctx).Fatalf("Failed to generate keys: %v", err)
}
Expand Down Expand Up @@ -248,3 +260,23 @@ func createConfigWithKeys(ctx context.Context, keytype string) (*ctlog.CTLogConf
PubKey: signer.Public(),
}, nil
}

// create
func createConfigFromExistingSecret(ctx context.Context, nsSecret v1.SecretInterface, secretName string) (*ctlog.CTLogConfig, error) {
existingSecret, err := nsSecret.Get(ctx, secretName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("getting an existing private key secret: %w", err)
}
private := existingSecret.Data[privateKey]
if private == nil || len(private) == 0 {
return nil, errors.New("secret missing private key entry")
}
priv, pub, err := ctlog.DecryptExistingPrivateKey(private, *keyPassword)
if err != nil {
return nil, fmt.Errorf("decrypting existing private key secret: %w", err)
}
return &ctlog.CTLogConfig{
PrivKey: priv,
PubKey: pub,
}, nil
}
47 changes: 33 additions & 14 deletions pkg/ctlog/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"strings"

Expand Down Expand Up @@ -216,24 +217,12 @@ func Unmarshal(ctx context.Context, in map[string][]byte) (*CTLogConfig, error)
return nil, fmt.Errorf("Not a valid PEMKeyFile in proto")
}

privPEM, _ := pem.Decode(private)
if privPEM == nil {
return nil, fmt.Errorf("did not find valid private PEM data")
}
ret.PrivKeyPassword = pb.Password

privatePEMBlock, err := x509.DecryptPEMBlock(privPEM, []byte(pb.Password))
ret.PrivKey, _, err = DecryptExistingPrivateKey(private, ret.PrivKeyPassword)
if err != nil {
return nil, fmt.Errorf("failed to decrypt private PEMKeyFile: %w", err)
}

if ret.PrivKey, err = x509.ParsePKCS8PrivateKey(privatePEMBlock); err != nil {
// Try it as RSA
if ret.PrivKey, err = x509.ParsePKCS1PrivateKey(privatePEMBlock); err != nil {
return nil, fmt.Errorf("failed to parse private key PEM: %w", err)
}
return nil, fmt.Errorf("decrypting existing private key: %w", err)
}

// If there's legacy rootCA entry, check it first.
if legacyRoot, ok := in[LegacyRootCAKey]; ok && len(legacyRoot) > 0 {
ret.FulcioCerts = append(ret.FulcioCerts, legacyRoot)
Expand Down Expand Up @@ -375,3 +364,33 @@ func mustMarshalAny(pb proto.Message) *anypb.Any {
}
return ret
}

// DecryptExistingPrivateKey reads in an encrypted private key, decrypts with
// the given password, and returns private, public keys for it.
func DecryptExistingPrivateKey(privateKey []byte, password string) (crypto.PrivateKey, crypto.PublicKey, error) {
privPEM, _ := pem.Decode(privateKey)
if privPEM == nil {
return nil, nil, fmt.Errorf("did not find valid private PEM data")
}
privatePEMBlock, err := x509.DecryptPEMBlock(privPEM, []byte(password))
if err != nil {
return nil, nil, fmt.Errorf("failed to decrypt private PEMKeyFile: %w", err)
}

var priv crypto.PrivateKey
if priv, err = x509.ParsePKCS8PrivateKey(privatePEMBlock); err != nil {
// Try it as RSA
if priv, err = x509.ParsePKCS1PrivateKey(privatePEMBlock); err != nil {
if priv, err = x509.ParseECPrivateKey(privatePEMBlock); err != nil {
return nil, nil, fmt.Errorf("failed to parse private key PEM: %w", err)
}
}
}
var ok bool
var signer crypto.Signer
if signer, ok = priv.(crypto.Signer); !ok {
return nil, nil, errors.New("failed to convert private key to Signer")
}

return priv, signer.Public(), nil
}
36 changes: 35 additions & 1 deletion pkg/ctlog/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ KTkomoSY/OxE/5doBCACehThH+96joWfgC0rXi9qAwZ6hwIMJAKy
privateKeyEncodedRSA = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpQcm9jLVR5cGU6IDQsRU5DUllQVEVECkRFSy1JbmZvOiBBRVMtMjU2LUNCQyw3NWUxNTkxNzQ0NTc4MjMwNGUzYjY1NGQ5NjhjY2M4MAoKV1pPQ1QrQXlaUmlaaFpDdXMveGxuR2dFbzNwTk1GRSsra0YvWVdBZUxMQjhmclNuL2NlL3VjbURuOURGQ01VZApORlNhSks1YzNvWEJCckt0Uk1sQ0I2S2RGblJucHNpVHUzbU1sVzVPdzRNTVh0L3JJaEFXbDFDaUFYUkdqL0NWClg4clRvQldpOFN4dXh3aWgrOHlrY0VpaVg3Ti9aWkNYOUppbjFQeTc0QUczWHBPT28rbFhwKzRTN1BwQmlZbzAKU0pzaUZ4Mlk0LzF4RXBWMEVWdmZobmN1R0k1R0ROcm0wUnBBNnNraGRSbU5iMW1HYkR5ZXdnMndPTTJTRHRGQwpSWEE5aFAxV1czUWx0VGhXRml2VTU0SngrYktMc3Fnem9JMzNZRmRFdnRPNmNxWCtoOVprN1pORmxaMDNaREk4Ck5RdzEyT3Z3VnpEeE5XdmFYVFhIMEpJc2tUSTE5cjFCTnB6aW1xdWg4ZWRYSTFuT2ppbUM5VjlRQTF0TVNmWmkKVmM2RW9VSG55N0xNVXkydG1yN3R2M2pLRWJHT09nclNRcXhJejAxcjFtV0dpREU2YkNDeFFueUhOUHExQmlIRQp1WTR3K25iU2V5UDhVc3h6YjlVNkRSd2IxVzZkMjlmbGNsdFp1TFlqdEhRL1JwRUdxbWRNc1RmRU1wRUVTNU9jClJPVmtsQlpQM0NHN3I4NGN0aVBMUGpvZnk0aG4rai9SeTBtT2tzcFcyVjNlQ2FvdGQwU0lQZFhxT3h6K2p3U1kKaDRBelg1VHdMSlg2UDlSaVdVZ2xQUWZKNjhCclpOT1Ywc3IwaEIwc1NXY25mSWorWWxSSzMvUXJTZGdhellRRQo0ZHBrK0hDUUE4bkdwN1M1Uks4ZGdxek1QYS96Z1AvR1dnN0t5K0dVWFB3cXRhalBFd1ZVWFJPNGViWUJCQ1RwClFHYnRSSmdRRjFzSmtqN1F0d0J4NzVoM25ZSjlWdEhiMWR2d2FKL09mWklhSklKQkRROVlyRGtqMjVmdDdtWlEKZVlGN1c5NlhCU0xHc2ZhdzlDMXhNRXZVY081UGtkS3ArR3pvMFhUaXhNb1U1Q0h6Yk0rQnFqMFZycGpNV29XbQphbHZpYVc4RlNYQkZQZUNoNFIrOXhwN1Q3ZWl6OU9uRFpKRVdnR1B1YXZyN29XL0t6blE1RS9SVlJtRllaZVY2CkluRXlmUVlRVE5QMnVBWjdibFRCeEc0VlhWdjA4ZUhWVHJ4YkVBcmE4VXJrZkQ1Nm02U3M3YWsrYU1mdG0vSnkKZHBxbTJ5YWlpSDd1SmRiZ1hyNTBnNEFDUThtZlE1QjNpbk1Ea0NFZ2RyQTRTQXg1YXNaQjJ0V2l1VC9SZFVSLworMUpXbjNKdXBEL2dhWU5CTVBTRzhjL0hKa0xmeE5UdzZVaHBBTlg5TkErTlE1UVdCUTVaaWNhbUNLQWJUczEvCjhUUlJlbnBLdUdhZXVsazhneVNOTm5xa0plZUNlZ1c5RGR1d3BZcUpjVkJ3L3lrY3BDc0hleVVZSTFOZkd0dCsKcTJ0Z2h0WGhaSGpFV1ZhcWVIb0JOTHlxZ0NET0l6U1QyTnFSeC9yYXhXckl1K0JwMTJTazNpQm5pc1Y0cE02NQprMnFaTDVhY2FDb3lIWTlSWStKSThYdHBzcHVjclViZnp6K0F3ZVZpdkcwN0hkOWRnV0dMRHRwMDJ4VGFMb09pCnp1NnV1dU9heUtZaUI4N2RBYlJlZUY0RVNrTlZOM3k4c1hIS3lnRlFvN2pqRExWVnBwRVVYWC8vN081VU9aZ0wKMWtWcVJ4K2hLeTQvTnVqQUVReWJubnMzRlpIMHBDMDQvcnAwS2xBeHlmRzBRNWJvTWdBeUR0VGlyUFBzK2lwTQpveDh1aWdlQlFaTmZyWW41TVA2UWVUSWY0QWx4NWNzSktxb1Nzb2dZclljbWhoSkhkc1Q4QUpidlpXSUo4L01JCjRFKzJ6UEZSNUlOYzNGbjVoVFpnRzNMQjh4N0ErTHlCbEdNR2owdW9melVzdnZMNnpxeEtqZ3F1Qm5DbTNmTHYKSjFnaDFYbkUyeENVekZhSlpQOVVNU1N2bmVmci92TzBFMjFxL0NlSGRUNWZsaUl4UjBZQ0t5MENvd3ZIeUdyYwpmc2JWWS92dGhIcUxLYmx0Vkh0bndPOExFTmhWZmVweGhFUy9sQUZrWmgrbmZFYjVsUnRZb2hZSW9RUkFOR1A0CmhCS1BhWldua0kwbFl5TmJNU1h3d3U0R2lScFdUUjhUYW84WDlXSWlJdmgyc3hHd0NleTBPSGZCVGtoYnR3Y2sKQzlaT0pERW9SNXBlOGZXSitzWXBia1laYjd3TzhSVEMyNlBGZTRQdEtKRFNGWXlOMzM2T1ZVdzM2RkZmVzR0QQpvcGtBdkRVbDdXVEZ1TlB4RVZ3SXZQSnN2ZDdnaG9Kdm1MYm4xQldQTS8wY0lobkQ0YkdrbjBsVURTTUFjUXIvCkV2R0h4Z2xpeU4wdktnOWU5SE9VNkVOYVdMaTRzemhwdE05RzF6UnBic01CV05zRW5TTEEwL3BaS01TOXdGdk8KL1N2VEVFc3dlM2xKWjV3WFc2R3lUdURFMzQ4Z011UFk4RmpCajZjQVo3RUJLTmYrWG1TY3VQTHYxVzd3Nm52cApKTGtQRS8wQmswdEZWRndlZUlERHJOTEg4Z0dseTY3MHk5cUxQSi8rMUhwdXpwR2tqc3RwWEs2QkRqWXUzeEFlCkhsd3E2RDNmRTMrZ0VkcW5RUmhZeHRacWxqaGIydFIxYUErZndhcWVBT2dNOG43RkNaY0gvK0ZBakdhRis3YjEKQ0RIdjA0cktKdVFGZjZTKzNzQktaVW9aVllJakxidE9VWko4c2QvZEZaQ01mNGhnN3RiaXNQeVFxMjQ2MUI3Wgp1SnFidlozdHhiT0lpd3k0cklCT2VtTnJaR3ArYmMzT3FuOHZQaEtpM3c2aDd4M2lvUzBxS250bStMbG11MXBqCnZOZnQwNmFZYklGcUhkY3ZqQ1AxajZNemY1Rm9TMGhmVnlpRmltOVFUOVpGeDl4bDNGeHBkK2VsYkxYY09pM0YKU2dISWE5SUdYQXNsSmo5dE5zdC9GaHBxeFdQbmt5c3dNTjRCQkJ2SDJNZU5odWpVUGdWblp5bEVodU1jQTBrcgpzdWMrNmliMEdRYUhRSW1pOHpmQ1FyQUVXMzZ2WWRxK1M0ZjBOeEZVNFZkclFzd2tpYlJhSytBTkZGY1ZKUzFJClcxWFdoU0FKV2VPUjJONmxJVFNqZVNDbXc5bnlXb1prZXBvSEkrcTlDZ0cvV09qRy9ZUkdjZUJNSFZQbk1zNDYKanA1NitvQkdXSUVpK3dvRU51UFV0aDNlZnZNT2dGTlBGZWh1QUFUUHBOeGtaMlBheHpRVmp6NXJGR09tNmJtZgoyWExIQVZxcTFjYVhEY1RidGxoSWh0Q3A5cmlGcXIzc2R6YlFxWThCWUsyQjdyQ0JHbXFjZld0akgvWUZadkNrCnFWNWpoOHQ2MFp2Z2F1bU15Y2h2NGNVaFRWMFJzZ1BteE9GMzdUenY1T0d3OVBKeS9sdFphNncveFFZQWVMaHQKRnVWN0I0WFJvdERyYklvZkNNM1ZObXdXTnN4R29LNWY1LzV6bVBEQ1JQNjZDNkkwbWVLNjZXb3prY0N2NTRMcQpJZDJaZTN5aUY2bjE0K05xZUZMWGVsdnRvay9RSWdiTEd3ME9XVEQyaFJtZGVYMjhUMEVMMW5kZ0ZUYU0xV3NlCkVJdXQxWXNLWXk1Vml2bDg1V0JiZEsvKzZuMjVIa2l3SGV5bHRsOWZ1cFEwSlcyM01yc1I2RWwybU1qQ0FFTEwKQ0l4TjdrOGFRTk92SndmV25LWjQ0U3BIalFPUXdtTTJySlVpZzBhZURUMWNMck9sVDNSVndUeG5DK00rN2V6SwpTZElza0ZZR0ZXdW12NlBZSVZBMy9MOE16T3dWeGs1WWwzcnpJaVh4UGlrdU1FeEtqNlRsNU8rQjBXQ2c0UVVUCjFGdk1zZksxNUwrRjdaeExuVi96WTVmQ2VBUEY2dXZDYjJ4VFBBeGZwN0VxK0tsSEdybzBWb1UwSGRSNFJLR2YKZlg0TytkZ3NNUHB1K1lQWTBWVGZTVjdVN2dWdklPcHhzc2lQbXQwdmRLSjJLK04xWUV5TmdKVlBCNUtyVXZveQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo="

publicKeyEncodedRSA = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUNDZ0tDQWdFQXVhTkpwYmdWT2VSQ2g4TmFGaEVBYytTZGplKzhPV1NLdGxPZnFFd1ExWVlGQytVTVRjNHkKdHZDQWxRRjNvRURTdDBhR0tvRkVyamY5MldzeXZITis0WG5ZRnZZSkVnM0ttd29OR3BmbXN1ckErcnhscFhDSQpsNG8zLy9IT2RoemZDc2l3TGR6VWUvbCtFQ1NXdkRnZzRoN0MwdlBIYVE0dGpyYUVMZ1VYSmVTaEQ4ek9qVlZXCk9SaEVKYWVEVHlKNnArZGtkS0ZFVEt6bmVuSnNFTmRBbmwzd1pORlNYbDg1L2FjL05DRDNFMXpQNmNkc0F5bnAKWTlzQk9ER1pmZTh5ajBWVFNQOW4rTXRKdWJtRG9xYlFBV09IUDNNRCs0ZnBTTnA1QS9Oa1hicDdneHkzWEtZRwpQUlBrNjlQZUVhZFdYMnRRaThDTi9QcG51TUJtUzdRdFljMVQ3RzZLZ1V4ZEJBQ3Y0Vm5VbHc5aUlRTzZPWFlsCkRJUHhHYXc2QzNUOGhsR3l6UEM3TWU2cjRFUURuQzNkYXFTSU1sbFFWSkFTakVSNWRNMXZjRFVmZWYxMnh4YXcKQkRFTmRFTVpOeVFRMHVFZGtFVVZLZTZwaTh6dE9uV2c1QkRVWWxuOEJNT3pKczZCVWQxR1VFWHhhenBhdHB0dAphaFhYVjhYZTUwSHloOXNWNEVDUENZUFpsb0JGU1IybHJKTExabnFDaFROM2s0SHV4R3Uydmxsc2xCbzZZT1R5CkRQQ2dJMys2TDFyUU9uM2pBZ3RWVy9ZdDAxS2RDK0tSaE5tU2w1eFVmL0ZZc091c3J4bU1oL2JnR2sxZldtZmMKbC9KNFBhRlNiS3VkWXZIeFp6MVdqMm5zZitQNEg5c2JZdUJya2RmSFhnQVVOTk5YNjYxVjFkOENBd0VBQVE9PQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCg=="

// Testing importing an existing key that's been added to TUF already.
// Generated with:
// openssl ecparam -name prime256v1 -genkey -noout -out privkey.pem
// openssl ec -in privkey.pem -pubout -out pubkey.pem
// openssl ec -in privkey.pem -out privatekey_encrypted.pem -aes256
// And encrypted with this supersecretpassword
existingEncryptedPrivateKeyPassword = "supersecretpassword"
existingEncryptedPrivateKey = `
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,3C33CA88DF439D434ABDB2DD03491BEC

A9UPVwTxy82/vDcG9q/e5SDKYokAGYvMyS5KD9rfyS5RGGQDdpkQPK0q6v9AFJbn
VCphFSJvnjFAR90XgF2EK+fVpX2GQjFEPhODVzAmqjawZHfTeGeMU5cJ+nNW+O6A
71ay3pGMAEQAvrzEErTLzCsBf2HZV1ioeFZVwHysvAA=
-----END EC PRIVATE KEY-----`
)

// testConfig wraps the private,public, and config into a single struct
Expand Down Expand Up @@ -138,7 +155,11 @@ func TestRoundTrip(t *testing.T) {
if err != nil {
t.Fatalf("Failed to generate Private Key: %v", err)
}
for k, v := range map[string]crypto.PrivateKey{"rsa": privateKeyRSA, "ecdsa": privateKeyECDSA} {
privateKeyEC, _, err := DecryptExistingPrivateKey([]byte(existingEncryptedPrivateKey), existingEncryptedPrivateKeyPassword)
if err != nil {
t.Fatalf("Failed to parse encrypted Private Key: %v", err)
}
for k, v := range map[string]crypto.PrivateKey{"rsa": privateKeyRSA, "ecdsa": privateKeyECDSA, "ec": privateKeyEC} {
t.Logf("testing with %s", k)
var ok bool
var signer crypto.Signer
Expand Down Expand Up @@ -318,3 +339,16 @@ func validateFulcioEntries(ctx context.Context, config map[string][]byte, fulcio
}
}
}

func TestDecrypteExistingPrivateKey(t *testing.T) {
priv, pub, err := DecryptExistingPrivateKey([]byte(existingEncryptedPrivateKey), existingEncryptedPrivateKeyPassword)
if err != nil {
t.Fatalf("Failed to decrypt existing private key %v", err)
}
if priv == nil {
t.Fatalf("got back a nil private key")
}
if pub == nil {
t.Fatalf("got back a nil public key")
}
}