cmd/tailscale/cli: start making cert output support pkcs12 (p12) output

If the --key-file output filename ends in ".pfx" or ".p12", use pkcs12
format.

This might not be working entirely correctly yet but might be enough for
others to help out or experiment.

Updates #2928
Updates #5011

Change-Id: I62eb0eeaa293b9fd5e27b97b9bc476c23dd27cf6
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2022-11-11 07:27:59 -08:00
committed by Brad Fitzpatrick
parent d585cbf02a
commit 6aab4fb696
4 changed files with 48 additions and 8 deletions
+41 -4
View File
@@ -7,7 +7,9 @@ package cli
import (
"bytes"
"context"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"errors"
"flag"
"fmt"
@@ -17,6 +19,7 @@ import (
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
"software.sslmate.com/src/go-pkcs12"
"tailscale.com/atomicfile"
"tailscale.com/ipn"
"tailscale.com/version"
@@ -130,17 +133,25 @@ func runCert(ctx context.Context, args []string) error {
}
}
}
if certArgs.keyFile != "" {
keyChanged, err := writeIfChanged(certArgs.keyFile, keyPEM, 0600)
if dst := certArgs.keyFile; dst != "" {
contents := keyPEM
if isPKCS12(dst) {
var err error
contents, err = convertToPKCS12(certPEM, keyPEM)
if err != nil {
return err
}
}
keyChanged, err := writeIfChanged(dst, contents, 0600)
if err != nil {
return err
}
if certArgs.keyFile != "-" {
macWarn()
if keyChanged {
printf("Wrote private key to %v\n", certArgs.keyFile)
printf("Wrote private key to %v\n", dst)
} else {
printf("Private key unchanged at %v\n", certArgs.keyFile)
printf("Private key unchanged at %v\n", dst)
}
}
}
@@ -160,3 +171,29 @@ func writeIfChanged(filename string, contents []byte, mode os.FileMode) (changed
}
return true, nil
}
func isPKCS12(dst string) bool {
return strings.HasSuffix(dst, ".p12") || strings.HasSuffix(dst, ".pfx")
}
func convertToPKCS12(certPEM, keyPEM []byte) ([]byte, error) {
cert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
return nil, err
}
var certs []*x509.Certificate
for _, c := range cert.Certificate {
cert, err := x509.ParseCertificate(c)
if err != nil {
return nil, err
}
certs = append(certs, cert)
}
if len(certs) == 0 {
return nil, errors.New("no certs")
}
// TODO(bradfitz): I'm not sure this is right yet. The goal was to make this
// work for https://github.com/tailscale/tailscale/issues/2928 but I'm still
// fighting Windows.
return pkcs12.Encode(rand.Reader, cert.PrivateKey, certs[0], certs[1:], "" /* no password */)
}
+3
View File
@@ -37,6 +37,8 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
nhooyr.io/websocket from tailscale.com/derp/derphttp+
nhooyr.io/websocket/internal/errd from nhooyr.io/websocket
nhooyr.io/websocket/internal/xsync from nhooyr.io/websocket
software.sslmate.com/src/go-pkcs12 from tailscale.com/cmd/tailscale/cli
software.sslmate.com/src/go-pkcs12/internal/rc2 from software.sslmate.com/src/go-pkcs12
tailscale.com from tailscale.com/version
tailscale.com/atomicfile from tailscale.com/ipn+
tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli+
@@ -119,6 +121,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
golang.org/x/crypto/hkdf from crypto/tls+
golang.org/x/crypto/nacl/box from tailscale.com/types/key
golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box
golang.org/x/crypto/pbkdf2 from software.sslmate.com/src/go-pkcs12
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
golang.org/x/net/bpf from github.com/mdlayher/netlink+
golang.org/x/net/dns/dnsmessage from net+