Skip to content

Commit

Permalink
feat(common.tls): Add support for passphrase-protected private key (i…
Browse files Browse the repository at this point in the history
  • Loading branch information
rajiv-k authored Jun 1, 2023
1 parent 6c49584 commit 7427ea3
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 11 deletions.
4 changes: 4 additions & 0 deletions docs/TLS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ For client TLS support we have the following options:
## contain intermediate certificates.
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
# passphrase for encrypted private key, if it is in PKCS#8 format. Encrypted PKCS#1 private keys are not supported.
# tls_key_pwd = "changeme"
## Skip TLS verification.
# insecure_skip_verify = false
## Send the specified TLS server name via SNI.
Expand All @@ -47,6 +49,8 @@ The server TLS configuration provides support for TLS mutual authentication:
## Add service certificate and key.
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
# passphrase for encrypted private key, if it is in PKCS#8 format. Encrypted PKCS#1 private keys are not supported.
# tls_key_pwd = "changeme"
```

#### Advanced Configuration
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ require (
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/xdg/stringprep v1.0.3 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
Expand Down
52 changes: 45 additions & 7 deletions plugins/common/tls/config.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package tls

import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"os"
"strings"

"github.com/influxdata/telegraf/internal/choice"
"github.com/youmark/pkcs8"
)

const TLSMinVersionDefault = tls.VersionTLS12
Expand Down Expand Up @@ -108,7 +112,7 @@ func (c *ClientConfig) TLSConfig() (*tls.Config, error) {
}

if c.TLSCert != "" && c.TLSKey != "" {
err := loadCertificate(tlsConfig, c.TLSCert, c.TLSKey)
err := loadCertificate(tlsConfig, c.TLSCert, c.TLSKey, c.TLSKeyPwd)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -153,7 +157,7 @@ func (c *ServerConfig) TLSConfig() (*tls.Config, error) {
}

if c.TLSCert != "" && c.TLSKey != "" {
err := loadCertificate(tlsConfig, c.TLSCert, c.TLSKey)
err := loadCertificate(tlsConfig, c.TLSCert, c.TLSKey, c.TLSKeyPwd)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -206,23 +210,57 @@ func (c *ServerConfig) TLSConfig() (*tls.Config, error) {
func makeCertPool(certFiles []string) (*x509.CertPool, error) {
pool := x509.NewCertPool()
for _, certFile := range certFiles {
pem, err := os.ReadFile(certFile)
cert, err := os.ReadFile(certFile)
if err != nil {
return nil, fmt.Errorf("could not read certificate %q: %w", certFile, err)
}
if !pool.AppendCertsFromPEM(pem) {
if !pool.AppendCertsFromPEM(cert) {
return nil, fmt.Errorf("could not parse any PEM certificates %q: %w", certFile, err)
}
}
return pool, nil
}

func loadCertificate(config *tls.Config, certFile, keyFile string) error {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
func loadCertificate(config *tls.Config, certFile, keyFile, privateKeyPassphrase string) error {
certBytes, err := os.ReadFile(certFile)
if err != nil {
return fmt.Errorf("could not load keypair %s:%s: %w", certFile, keyFile, err)
return fmt.Errorf("could not load certificate %q: %w", certFile, err)
}

keyBytes, err := os.ReadFile(keyFile)
if err != nil {
return fmt.Errorf("could not load private key %q: %w", keyFile, err)
}

keyPEMBlock, _ := pem.Decode(keyBytes)
if keyPEMBlock == nil {
return errors.New("failed to decode private key: no PEM data found")
}

var cert tls.Certificate
if keyPEMBlock.Type == "ENCRYPTED PRIVATE KEY" {
if privateKeyPassphrase == "" {
return errors.New("missing password for PKCS#8 encrypted private key")
}
var decryptedKey *rsa.PrivateKey
decryptedKey, err = pkcs8.ParsePKCS8PrivateKeyRSA(keyPEMBlock.Bytes, []byte(privateKeyPassphrase))
if err != nil {
return fmt.Errorf("failed to parse encrypted PKCS#8 private key: %w", err)
}
cert, err = tls.X509KeyPair(certBytes, pem.EncodeToMemory(&pem.Block{Type: keyPEMBlock.Type, Bytes: x509.MarshalPKCS1PrivateKey(decryptedKey)}))
if err != nil {
return fmt.Errorf("failed to load cert/key pair: %w", err)
}
} else if keyPEMBlock.Headers["Proc-Type"] == "4,ENCRYPTED" {
// The key is an encrypted private key with the DEK-Info header.
// This is currently unsupported because of the deprecation of x509.IsEncryptedPEMBlock and x509.DecryptPEMBlock.
return fmt.Errorf("password-protected keys in pkcs#1 format are not supported")
} else {
cert, err = tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return fmt.Errorf("failed to load cert/key pair: %w", err)
}
}
config.Certificates = []tls.Certificate{cert}
return nil
}
Expand Down
50 changes: 50 additions & 0 deletions plugins/common/tls/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,56 @@ func TestClientConfig(t *testing.T) {
TLSKeyPwd: "",
},
},
{
name: "success with unencrypted pkcs#8 key",
client: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientPKCS8KeyPath(),
},
},
{
name: "encrypted pkcs#8 key but missing password",
client: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientEncPKCS8KeyPath(),
},
expNil: true,
expErr: true,
},
{
name: "encrypted pkcs#8 key and incorrect password",
client: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientEncPKCS8KeyPath(),
TLSKeyPwd: "incorrect",
},
expNil: true,
expErr: true,
},
{
name: "success with encrypted pkcs#8 key and password set",
client: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientEncPKCS8KeyPath(),
TLSKeyPwd: "changeme",
},
},
{
name: "error with encrypted pkcs#1 key and password set",
client: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientEncKeyPath(),
TLSKeyPwd: "changeme",
},
expNil: true,
expErr: true,
},

{
name: "invalid ca",
client: tls.ClientConfig{
Expand Down
30 changes: 30 additions & 0 deletions testutil/pki/clientenckey.pkcs8.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI0QKPLrW1mlACAggA
MBQGCCqGSIb3DQMHBAhz7px4LL4+gASCBMi4KiB3IZOQfDiRdfnQHHpdS8cOk7fc
0fEd6EnDeb7wczkUg0NDerrsfvV6JVzvmFIYB2Eqmg1mW20sP3U6tXKke7zn1BnF
3SFpMT5i4/YoiULPmkDK07N+36VqfBCqOscVGm32DXBBqNHLi1xYoTUQA5BCxdka
s8NAArHmSHOZGOODa46LKmepBQBoI+vvoYrSlWX2Zq/KHjrhtWZy25bJP78xhvoU
GDvzbmglXaqfWLGYBZRYw9ixkSNu2QkyKCJZ1QYAX2pbdloMmYfSQ9RRCKIKQqqY
1C7AswWcqWQDlqoNTpmn51Ss6h6M8oLzUYdER11+KsEVViWeOADCeEb0V5mbVJqC
F1ltVgI+JNY8DgStrshZzEMLYMtjaa2u64Xd/XIhShbNBZ03rRdg0Ev56NSPDM70
w16inaCMUumEIR5nxAswfxO6+ukVxlRLwn/+KtSyajpCjeg/D258ryLM91702CAF
8D5VaJ80q1ZVoMWDgJnB3Oy2aE8EtIWPXQIfsLk0Fy62EjhO99EhE+rkuFzma9Px
ICFN3HIKxsTPLbOnduqFZOUp8rRTaju6KGsXOXgHReBAl/14rEJ+YN+dasMhhEi7
VNwdjodkOeNpM13G3D2sA92LqnWYBbiHS/AJykoyWUSxdcMUNH6PBa1ASWGgMo9o
rB/HBbxbEGgiGYMqAUZ89HB//J3fxAV7+kDF9feGLk2hlpdh9FMAYs9+L/Bjsz5e
caxmP1go8qapvNhDScKAuaR/2JHlptowE9mRw92UaMB4WgNmzkqu3KxBRDM12iXv
2OJEKo6ntHVvOwN0ZAQsqyosPmmXXnif245ubGzZYxkuOA/mHckZ8aFwn+CE1xwi
NHmk3pXnbpwFeh4a/4yOh7vcR25NyS9zhOgXhfIRz4E+GrHzTQbpLgOlpoCm43tG
PDdyc0qsFQ2NcDSZBto2KJzukWYj8u2zoIEvYCDF90t81y6P3CVgPaIXW1J2Qmhi
esnxWhJdDlSS0fDZbfhDpD4zz0tHwd45OdMpg+x8fKJXi/K14fDMNb1LjvdyhmvE
cZRwevapOL9NtoVHoaYTLOCJm1RUmKUcjAoOagq0jMNtM1SbtMsenWoioyj6RItE
G9b3oSqfYjOmp0RBeR5ryEdJPliPuf8xvvoC6AntKlHEmyasSOf/k+t/X1IKYrfZ
Rird+/24P15NjblfBRXYudVCIeepuoJGwm7bad2cD4WB6j9soIAGv7XThamo7tGo
/IAJ1U8tSNXSR+DihXme1ZfEzbxhCkJX0HLDUZ1P2JSpzaVquE8cRd24uP5utNgY
nZsQRlD/O1avpVlja10VtAC44FNrF714E92t15C97JXnqe9iHEeAKmCNX0s7eM0r
qSPaVkVUkPom/QPE3oYGzI73TvgktJ+RXz3Z5t395MZH0ldHQ/imRGw+K8St5Hte
+uNy2JNQWBAmM/eCK6zIQTrJnuVnAJPgk5pTI92sUbMx60BHTlfDwIZLSmsVPbQp
hDIjrFHnKGda4hQ8gRiVFYW11ROcVF5rNLfHIcse7WaQ0vX+6P9C/xJgit6CVoOV
ZdtAB7sNRYy1ttlZv5LTi31tGrvJi1Waeu+ZL4Tr3Tm0YH3RrAtbkRf9XXxCTd3D
0yg=
-----END ENCRYPTED PRIVATE KEY-----
28 changes: 28 additions & 0 deletions testutil/pki/clientkey.pkcs8.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDAU4sKV0pZNr8G
fxGAg5C3+gibh3Wn8FKZmdZ4ND1CGSxATDvC2+wyE5h5x+DCtmFGxrMnBF4CPoyw
HiOgW+vODQQ8rKXc9qR2e1yUl3gLu43IOr5zD5uUgU+TrjKh+mBzAwjuHQUSm+7c
L/LuRMqbCRP89fXtGWb5aiLs2rrlD4ttOGDivkrlJw0CweybbF5k5Fjzpd56xoT/
WjlGSXuP+7ovw5ui1jSrvNDAzEmq1q+vMDInD5OHiizk2zHKHrhwU99iZE53cpZN
3/Vn2lZvxDcArJZuwVhWr94PVeNdleuF65PGMl1mJAYJj8U4ZTmmDEMEzfluru8+
byKdsAezAgMBAAECggEABe2NR5BRNla9pJGeFHv5pfIK+eA+/CIoKZvYRHQ3vyXP
e1veK69jpFd+R6KlhtrgPLI7K2SzERakd3k2V+b9aah1DPm1Uw43G3/J5rqUdKgf
QqCs5hcBEJ67uS2RWfTBrEnXqJ80XNJ5lkfUEQwFwqmQ+CcMoXY6+k0PLoW/ZPf7
7BgiN9tgCFpmxpbIvAzopfuoupMdmsetHME1VobqdR9N9cwWiNAvmfied/sLuBoW
4H9fef0HJDZqvatPdhdmYWsypzVr5Z8UTiefdxwsvss0IHbEWwH7uCXWf/HTzSdb
qUyMlVP9jtLif8yd+or2pCiBnLOs1Sy6UjUhgveUQQKBgQD4bfzufOgGOj/oPyQv
FJryfpxJDZgTYxSMhu6+OjeG/1Yri9T9+Wm8pk74DKWbLpzWafHTpdQGHmwN1bze
h+0L3vPzPFDVa3ff0F9YmQmdabSvezBLaAvooWY+S6cH+y2QzT03epAbduam5jxF
ObmPLAXJoo3fB7Bjmkq1rwPHaQKBgQDGL+P2M3Lsfv+WMOpfYp4iBzFp2WZ7xWj8
eKH2xZMZDyf0ZNAoclSXLXLcLR2YSc+FzlYyvHrGiV0dBToQKoM66WFvI4guJHEX
KFjd2wb7j/PI9SaEl2T+5kM0k3uwbKccSSEx+3dPQ9dPlwSzTSAcI/SB2TdKc/oM
B5H1SMSuuwKBgHQX4hUI5dzTJ74+k4g01tvvctVotuKIcLfi7nqS9hPuCrS62cP9
4/22sjyUnGdSdxRz2gCQA+8AFHq6oVJUrt/XRqUqUJQLbq92zre2fJnFJhzDMqRq
cPY7Rm5iKCJArOB4KN1eNy2+eLcR7KRk/2P6rJG3lxjcnP9OwQ76RIfRAoGAbSfz
ZqbvpVJNc2t9qwumDRfpH2OeDo2sRWIr0r7/Rc4TM4hTHfPao2lk2d//bOE6a3g6
AbfN54vAvTsjv8Oqg8gG4i+v0bpAj4CpcYgxUFz46LYdgOnhtoMcgNi6R3GQmQOu
RNk73WyAKlYDQL458UXcZag9y8QU9Is22OI7cgECgYAjlK7hACJ9UkNlrJSHDt1X
6WcxKPib/1gpDOgujHPKzyuyzjhR+cvsUs6DUr3diHY66wYi3UgMFvNQ17OKsmuj
ZxK0BQMDw0v6ZZr7EnD0MK/jndShPtxXbJCc/mendstW1atsGcocaHHxkyQy7PmG
CrJm+I0R+zTdec261fjsjQ==
-----END PRIVATE KEY-----
7 changes: 5 additions & 2 deletions testutil/pki/tls-certs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,11 @@ openssl ca -config ./openssl.conf -in ./certs/servercsr.pem -out ./certs/serverc
openssl genrsa -out ./private/clientkey.pem 2048 &&
openssl req -new -key ./private/clientkey.pem -out ./certs/clientcsr.pem -outform PEM -subj "/CN=$(cat /proc/sys/kernel/hostname)/O=client/" &&
openssl ca -config ./openssl.conf -in ./certs/clientcsr.pem -out ./certs/clientcert.pem -notext -batch -extensions client_ca_extensions &&
cp ./private/clientkey.pem ./private/clientkeyenc.pem &&
ssh-keygen -p -f ./private/clientkeyenc.pem -m PEM -N 'changeme'
cp ./private/clientkey.pem ./private/clientenckey.pem &&
ssh-keygen -p -f ./private/clientenckey.pem -m PEM -N 'changeme' &&
# Generate a pkcs#8 encrypted private key using pkcs#5 v2.0 algorithm
openssl pkcs8 -topk8 -v2 des3 -in ./private/clientkey.pem -out ./private/clientenckey.pkcs8.pem -passout pass:changeme &&
openssl pkcs8 -topk8 -in clientenckey.pem -passin pass:changeme -nocrypt -out clientkey.pkcs8.pem &&

# Combine crt and key to create pem formatted keyfile
cat ./certs/clientcert.pem ./private/clientkey.pem > ./private/client.pem &&
Expand Down
10 changes: 9 additions & 1 deletion testutil/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,15 @@ func (p *pki) ClientCertAndKeyPath() string {
}

func (p *pki) ClientEncKeyPath() string {
return path.Join(p.keyPath, "clientkeyenc.pem")
return path.Join(p.keyPath, "clientenckey.pem")
}

func (p *pki) ClientPKCS8KeyPath() string {
return path.Join(p.keyPath, "clientkey.pkcs8.pem")
}

func (p *pki) ClientEncPKCS8KeyPath() string {
return path.Join(p.keyPath, "clientenckey.pkcs8.pem")
}

func (p *pki) ClientCertAndEncKeyPath() string {
Expand Down

0 comments on commit 7427ea3

Please sign in to comment.