diff --git a/cmd/tailscale/cli/cert.go b/cmd/tailscale/cli/cert.go index ba4679378..8a03965d5 100644 --- a/cmd/tailscale/cli/cert.go +++ b/cmd/tailscale/cli/cert.go @@ -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 */) +} diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index f6b9778be..5772e88fd 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -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+ diff --git a/go.mod b/go.mod index 2805c6892..2bd53cb49 100644 --- a/go.mod +++ b/go.mod @@ -73,6 +73,7 @@ require ( inet.af/peercred v0.0.0-20210906144145-0893ea02156a inet.af/wf v0.0.0-20220728202103-50d96caab2f6 nhooyr.io/websocket v1.8.7 + software.sslmate.com/src/go-pkcs12 v0.2.0 ) require ( @@ -281,5 +282,4 @@ require ( mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect mvdan.cc/unparam v0.0.0-20211002134041-24922b6997ca // indirect - software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 // indirect ) diff --git a/go.sum b/go.sum index e423daedf..9eb09a67a 100644 --- a/go.sum +++ b/go.sum @@ -1250,7 +1250,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= @@ -1259,6 +1258,7 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/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-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1870,5 +1870,5 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ= -software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4= -software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg= +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=