kes: add support for encrypted private keys (#14650)

This commit adds support for encrypted KES
client private keys.

Now, it is possible to encrypt the KES client
private key (`MINIO_KMS_KES_KEY_FILE`) with
a password.

For example, KES CLI already supports the
creation of encrypted private keys:
```
kes identity new --encrypt --key client.key --cert client.crt MinIO
```

To decrypt an encrypted private key, the password
needs to be provided:
```
MINIO_KMS_KES_KEY_PASSWORD=<password>
```

Signed-off-by: Andreas Auernhammer <hi@aead.dev>
This commit is contained in:
Andreas Auernhammer 2022-03-29 18:53:33 +02:00 committed by GitHub
parent 81d2b54dfd
commit e955aa7f2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 8 deletions

View File

@ -24,6 +24,7 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/gob" "encoding/gob"
"encoding/pem"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -798,7 +799,29 @@ func handleCommonEnvVars() {
endpoints = append(endpoints, strings.Join(lbls, "")) endpoints = append(endpoints, strings.Join(lbls, ""))
} }
} }
certificate, err := tls.LoadX509KeyPair(env.Get(config.EnvKESClientCert, ""), env.Get(config.EnvKESClientKey, "")) // Manually load the certificate and private key into memory.
// We need to check whether the private key is encrypted, and
// if so, decrypt it using the user-provided password.
certBytes, err := os.ReadFile(env.Get(config.EnvKESClientCert, ""))
if err != nil {
logger.Fatal(err, "Unable to load KES client certificate as specified by the shell environment")
}
keyBytes, err := os.ReadFile(env.Get(config.EnvKESClientKey, ""))
if err != nil {
logger.Fatal(err, "Unable to load KES client private key as specified by the shell environment")
}
privateKeyPEM, rest := pem.Decode(bytes.TrimSpace(keyBytes))
if len(rest) != 0 {
logger.Fatal(errors.New("private key contains additional data"), "Unable to load KES client private key as specified by the shell environment")
}
if x509.IsEncryptedPEMBlock(privateKeyPEM) {
keyBytes, err = x509.DecryptPEMBlock(privateKeyPEM, []byte(env.Get(config.EnvKESClientPassword, "")))
if err != nil {
logger.Fatal(err, "Unable to decrypt KES client private key as specified by the shell environment")
}
keyBytes = pem.EncodeToMemory(&pem.Block{Type: privateKeyPEM.Type, Bytes: keyBytes})
}
certificate, err := tls.X509KeyPair(certBytes, keyBytes)
if err != nil { if err != nil {
logger.Fatal(err, "Unable to load KES client certificate as specified by the shell environment") logger.Fatal(err, "Unable to load KES client certificate as specified by the shell environment")
} }

View File

@ -120,6 +120,20 @@ Encrypted :
X-Amz-Server-Side-Encryption: AES256 X-Amz-Server-Side-Encryption: AES256
``` ```
## Encrypted Private Key
MinIO supports encrypted KES client private keys. Therefore, you can use
an password-protected private keys for `MINIO_KMS_KES_KEY_FILE`.
When using password-protected private keys for accessing KES you need to
provide the password via:
```
export MINIO_KMS_KES_KEY_PASSWORD=<your-password>
```
Note that MinIO only supports encrypted private keys - not encrypted certificates.
Certificates are no secrets and sent in plaintext as part of the TLS handshake.
## Explore Further ## Explore Further
- [Use `mc` with MinIO Server](https://docs.min.io/docs/minio-client-quickstart-guide) - [Use `mc` with MinIO Server](https://docs.min.io/docs/minio-client-quickstart-guide)

View File

@ -67,6 +67,7 @@ const (
EnvKESEndpoint = "MINIO_KMS_KES_ENDPOINT" EnvKESEndpoint = "MINIO_KMS_KES_ENDPOINT"
EnvKESKeyName = "MINIO_KMS_KES_KEY_NAME" EnvKESKeyName = "MINIO_KMS_KES_KEY_NAME"
EnvKESClientKey = "MINIO_KMS_KES_KEY_FILE" EnvKESClientKey = "MINIO_KMS_KES_KEY_FILE"
EnvKESClientPassword = "MINIO_KMS_KES_KEY_PASSWORD"
EnvKESClientCert = "MINIO_KMS_KES_CERT_FILE" EnvKESClientCert = "MINIO_KMS_KES_CERT_FILE"
EnvKESServerCA = "MINIO_KMS_KES_CAPATH" EnvKESServerCA = "MINIO_KMS_KES_CAPATH"