Browse Source

Add PEM to PKCS12 template function (#3101)

* Add PEM to PKCS12 template function

Signed-off-by: Mathias Maes <mathias.maes@aloxy.io>

* add docs

Signed-off-by: Mathias Maes <mathias.maes@aloxy.io>

* add pemToPkcs12Pass

Signed-off-by: Mathias Maes <mathias.maes@aloxy.io>

* fix formatting

Signed-off-by: Mathias Maes <mathias.maes@aloxy.io>

---------

Signed-off-by: Mathias Maes <mathias.maes@aloxy.io>
Mathias Maes 2 years ago
parent
commit
74ed3facb7
7 changed files with 39 additions and 4 deletions
  1. 2 0
      docs/guides/templating.md
  2. 1 1
      e2e/go.mod
  3. 2 3
      e2e/go.sum
  4. 1 0
      go.mod
  5. 2 0
      go.sum
  6. 28 0
      pkg/template/v2/pkcs12.go
  7. 3 0
      pkg/template/v2/template.go

+ 2 - 0
docs/guides/templating.md

@@ -156,6 +156,8 @@ In addition to that you can use over 200+ [sprig functions](http://masterminds.g
 | pkcs12keyPass    | Same as `pkcs12key`. Uses the provided password to decrypt the PKCS#12 archive.                                                                                                                                              |
 | pkcs12cert       | Extracts all certificates from a PKCS#12 archive and orders them if possible. If disjunct or multiple leaf certs are provided they are returned as-is. <br/> Sort order: `leaf / intermediate(s) / root`.                    |
 | pkcs12certPass   | Same as `pkcs12cert`. Uses the provided password to decrypt the PKCS#12 archive.                                                                                                                                             |
+| pemToPkcs12      | Takes a PEM encoded certificate and key and creates a base64 enoded PKCS#12 archive.                                                                                                                                         |
+| pemToPkcs12Pass  | Same as `pemToPkcs12`. Uses the provided password to encrypt the PKCS#12 archive.                                                                                                                                            |
 | filterPEM        | Filters PEM blocks with a specific type from a list of PEM blocks.                                                                                                                                                           |
 | jwkPublicKeyPem  | Takes an json-serialized JWK and returns an PEM block of type `PUBLIC KEY` that contains the public key. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey) for details.                                   |
 | jwkPrivateKeyPem | Takes an json-serialized JWK as `string` and returns an PEM block of type `PRIVATE KEY` that contains the private key in PKCS #8 format. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKCS8PrivateKey) for details. |

+ 1 - 1
e2e/go.mod

@@ -68,7 +68,7 @@ require (
 	k8s.io/utils v0.0.0-20240102154912-e7106e64919e
 	sigs.k8s.io/controller-runtime v0.17.2
 	sigs.k8s.io/yaml v1.4.0
-	software.sslmate.com/src/go-pkcs12 v0.2.0
+	software.sslmate.com/src/go-pkcs12 v0.4.0
 )
 
 require (

+ 2 - 3
e2e/go.sum

@@ -525,7 +525,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
@@ -957,5 +956,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+s
 sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
 sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
 sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
-software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
-software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=
+software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
+software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=

+ 1 - 0
go.mod

@@ -86,6 +86,7 @@ require (
 	github.com/spf13/pflag v1.0.5
 	github.com/tidwall/sjson v1.2.5
 	sigs.k8s.io/yaml v1.4.0
+	software.sslmate.com/src/go-pkcs12 v0.4.0
 )
 
 require (

+ 2 - 0
go.sum

@@ -1340,3 +1340,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77Vzej
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
 sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
+software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=

+ 28 - 0
pkg/template/v2/pkcs12.go

@@ -17,10 +17,12 @@ package template
 import (
 	"bytes"
 	"crypto/x509"
+	"encoding/base64"
 	"encoding/pem"
 	"fmt"
 
 	"golang.org/x/crypto/pkcs12"
+	gopkcs12 "software.sslmate.com/src/go-pkcs12"
 )
 
 func pkcs12keyPass(pass, input string) (string, error) {
@@ -108,3 +110,29 @@ func pkcs12certPass(pass, input string) (string, error) {
 func pkcs12cert(input string) (string, error) {
 	return pkcs12certPass("", input)
 }
+
+func pemToPkcs12(cert, key string) (string, error) {
+	return pemToPkcs12Pass(cert, key, "")
+}
+
+func pemToPkcs12Pass(cert, key, pass string) (string, error) {
+	certPem, _ := pem.Decode([]byte(cert))
+	keyPem, _ := pem.Decode([]byte(key))
+
+	parsedCert, err := x509.ParseCertificate(certPem.Bytes)
+	if err != nil {
+		return "", err
+	}
+
+	parsedKey, err := parsePrivateKey(keyPem.Bytes)
+	if err != nil {
+		return "", err
+	}
+
+	pfx, err := gopkcs12.Modern.Encode(parsedKey, parsedCert, nil, pass)
+	if err != nil {
+		return "", err
+	}
+
+	return base64.StdEncoding.EncodeToString(pfx), nil
+}

+ 3 - 0
pkg/template/v2/template.go

@@ -32,6 +32,9 @@ var tplFuncs = tpl.FuncMap{
 	"pkcs12cert":     pkcs12cert,
 	"pkcs12certPass": pkcs12certPass,
 
+	"pemToPkcs12":     pemToPkcs12,
+	"pemToPkcs12Pass": pemToPkcs12Pass,
+
 	"filterPEM": filterPEM,
 
 	"jwkPublicKeyPem":  jwkPublicKeyPem,