Skip to content

Commit a445f06

Browse files
committed
support p12 certificate format, add param -cert-type and -pass
1 parent bc2944d commit a445f06

File tree

5 files changed

+87
-10
lines changed

5 files changed

+87
-10
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ dist/
22
.idea/
33
VERSION
44
.tmp/
5+
*.swp

cmd/grpcurl/grpcurl.go

+35-4
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,13 @@ var (
6666
Ignored if -insecure is specified.`))
6767
cert = flags.String("cert", "", prettify(`
6868
File containing client certificate (public key), to present to the
69-
server. Not valid with -plaintext option. Must also provide -key option.`))
69+
server. Not valid with -plaintext option. Must also provide -key option
70+
when use PEM certificate file.`))
71+
certTypeString = flags.String("cert-type", "", prettify(`
72+
Client certificate file type. (PEM/P12)`))
73+
certType = grpcurl.CertTypePEM
74+
pass = flags.String("pass", "", prettify(`
75+
Pass phrase for the key`))
7076
key = flags.String("key", "", prettify(`
7177
File containing client private key, to present to the server. Not valid
7278
with -plaintext option. Must also provide -cert option.`))
@@ -289,6 +295,17 @@ func main() {
289295
// default behavior is to use tls
290296
usetls := !*plaintext && !*usealts
291297

298+
// converto to CertificateFileType
299+
if len(*certTypeString) == 0 {
300+
certType = grpcurl.CertTypePEM // default PEM
301+
} else if strings.EqualFold(*certTypeString, "PEM") {
302+
certType = grpcurl.CertTypePEM
303+
} else if strings.EqualFold(*certTypeString, "P12") {
304+
certType = grpcurl.CertTypeP12
305+
} else {
306+
fail(nil, "The -cert-type argument must be PEM or P12.")
307+
}
308+
292309
// Do extra validation on arguments and figure out what user asked us to do.
293310
if *connectTimeout < 0 {
294311
fail(nil, "The -connect-timeout argument must not be negative.")
@@ -314,9 +331,23 @@ func main() {
314331
if *key != "" && !usetls {
315332
fail(nil, "The -key argument can only be used with TLS.")
316333
}
317-
if (*key == "") != (*cert == "") {
318-
fail(nil, "The -cert and -key arguments must be used together and both be present.")
334+
335+
switch certType {
336+
case grpcurl.CertTypePEM:
337+
if (*key == "") != (*cert == "") {
338+
fail(nil, "The -cert and -key arguments must be used together and both be present when -cert-type is PEM.")
339+
}
340+
case grpcurl.CertTypeP12:
341+
if *key != "" {
342+
fail(nil, "The -key arguments must not be used when -cert-type is P12.")
343+
}
344+
if *cert == "" {
345+
fail(nil, "The -cert arguments must be used when -cert-type is P12.")
346+
}
347+
default:
348+
fail(nil, "Not support cert type %v.", certType)
319349
}
350+
320351
if *altsHandshakerServiceAddress != "" && !*usealts {
321352
fail(nil, "The -alts-handshaker-service argument must be used with the -alts argument.")
322353
}
@@ -451,7 +482,7 @@ func main() {
451482
}
452483
creds = alts.NewClientCreds(clientOptions)
453484
} else if usetls {
454-
tlsConf, err := grpcurl.ClientTLSConfig(*insecure, *cacert, *cert, *key)
485+
tlsConf, err := grpcurl.ClientTLSConfigV2(*insecure, *cacert, *cert, *key, certType, *pass)
455486
if err != nil {
456487
fail(err, "Failed to create TLS config")
457488
}

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.18
55
require (
66
github.com/golang/protobuf v1.5.3
77
github.com/jhump/protoreflect v1.15.3
8+
golang.org/x/crypto v0.14.0
89
google.golang.org/grpc v1.57.0
910
google.golang.org/protobuf v1.31.0
1011
)

go.sum

+2-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
2525
github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8=
2626
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
2727
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
28-
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
2928
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
3029
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
3130
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -37,20 +36,18 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
3736
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
3837
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
3938
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
40-
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
4139
github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls=
4240
github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k=
4341
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
44-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4542
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
46-
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
4743
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
4844
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
45+
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
46+
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
4947
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
5048
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
5149
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
5250
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
53-
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
5451
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
5552
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
5653
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

grpcurl.go

+48-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"crypto/tls"
1313
"crypto/x509"
1414
"encoding/base64"
15+
"encoding/pem"
1516
"errors"
1617
"fmt"
1718
"io/ioutil"
@@ -25,6 +26,7 @@ import (
2526
"github.com/jhump/protoreflect/desc"
2627
"github.com/jhump/protoreflect/desc/protoprint"
2728
"github.com/jhump/protoreflect/dynamic"
29+
"golang.org/x/crypto/pkcs12"
2830
"google.golang.org/grpc"
2931
"google.golang.org/grpc/credentials"
3032
"google.golang.org/grpc/credentials/insecure"
@@ -523,16 +525,40 @@ func ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertF
523525
return credentials.NewTLS(tlsConf), nil
524526
}
525527

528+
type CertificateType int
529+
530+
const (
531+
// The certificate file contains PEM encoded data
532+
CertTypePEM CertificateType = 1
533+
// The certificate file contains PFX data describing PKCS#12.
534+
CertTypeP12 CertificateType = 2
535+
)
536+
526537
// ClientTLSConfig builds transport-layer config for a gRPC client using the
527538
// given properties. If cacertFile is blank, only standard trusted certs are used to
528539
// verify the server certs. If clientCertFile is blank, the client will not use a client
529540
// certificate. If clientCertFile is not blank then clientKeyFile must not be blank.
530541
func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (*tls.Config, error) {
542+
return ClientTLSConfigV2(insecureSkipVerify, cacertFile, clientCertFile, clientKeyFile, CertTypePEM, "")
543+
}
544+
545+
// ClientTLSConfigV2 builds transport-layer config for a gRPC client using the
546+
// given properties. Support certificate file both PEM and P12.
547+
func ClientTLSConfigV2(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string, clientCertType CertificateType, clientPass string) (*tls.Config, error) {
531548
var tlsConf tls.Config
532549

533550
if clientCertFile != "" {
534551
// Load the client certificates from disk
535-
certificate, err := tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
552+
var certificate tls.Certificate
553+
var err error
554+
switch clientCertType {
555+
case CertTypeP12:
556+
certificate, err = loadClientCertP12(clientCertFile, clientPass)
557+
case CertTypePEM:
558+
certificate, err = tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
559+
default:
560+
return nil, fmt.Errorf("not support client certificate type: %v", clientCertType)
561+
}
536562
if err != nil {
537563
return nil, fmt.Errorf("could not load client key pair: %v", err)
538564
}
@@ -560,6 +586,27 @@ func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, client
560586
return &tlsConf, nil
561587
}
562588

589+
func loadClientCertP12(pfxFile, pfxPassword string) (tls.Certificate, error) {
590+
b, err := os.ReadFile(pfxFile)
591+
if err != nil {
592+
return tls.Certificate{}, fmt.Errorf("os.ReadFile err: %w", err)
593+
}
594+
pemBlocks, err := pkcs12.ToPEM(b, pfxPassword)
595+
if err != nil {
596+
return tls.Certificate{}, fmt.Errorf("pkcs12.ToPEM err: %w", err)
597+
}
598+
599+
var pemBytes []byte
600+
for _, block := range pemBlocks {
601+
pemBytes = append(pemBytes, pem.EncodeToMemory(block)...)
602+
}
603+
certificate, err := tls.X509KeyPair(pemBytes, pemBytes)
604+
if err != nil {
605+
return tls.Certificate{}, err
606+
}
607+
return certificate, nil
608+
}
609+
563610
// ServerTransportCredentials builds transport credentials for a gRPC server using the
564611
// given properties. If cacertFile is blank, the server will not request client certs
565612
// unless requireClientCerts is true. When requireClientCerts is false and cacertFile is

0 commit comments

Comments
 (0)