@@ -12,6 +12,7 @@ import (
12
12
"crypto/tls"
13
13
"crypto/x509"
14
14
"encoding/base64"
15
+ "encoding/pem"
15
16
"errors"
16
17
"fmt"
17
18
"io/ioutil"
@@ -25,6 +26,7 @@ import (
25
26
"github.com/jhump/protoreflect/desc"
26
27
"github.com/jhump/protoreflect/desc/protoprint"
27
28
"github.com/jhump/protoreflect/dynamic"
29
+ "golang.org/x/crypto/pkcs12"
28
30
"google.golang.org/grpc"
29
31
"google.golang.org/grpc/credentials"
30
32
"google.golang.org/grpc/credentials/insecure"
@@ -523,16 +525,40 @@ func ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertF
523
525
return credentials .NewTLS (tlsConf ), nil
524
526
}
525
527
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
+
526
537
// ClientTLSConfig builds transport-layer config for a gRPC client using the
527
538
// given properties. If cacertFile is blank, only standard trusted certs are used to
528
539
// verify the server certs. If clientCertFile is blank, the client will not use a client
529
540
// certificate. If clientCertFile is not blank then clientKeyFile must not be blank.
530
541
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 ) {
531
548
var tlsConf tls.Config
532
549
533
550
if clientCertFile != "" {
534
551
// 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
+ }
536
562
if err != nil {
537
563
return nil , fmt .Errorf ("could not load client key pair: %v" , err )
538
564
}
@@ -560,6 +586,27 @@ func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, client
560
586
return & tlsConf , nil
561
587
}
562
588
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
+
563
610
// ServerTransportCredentials builds transport credentials for a gRPC server using the
564
611
// given properties. If cacertFile is blank, the server will not request client certs
565
612
// unless requireClientCerts is true. When requireClientCerts is false and cacertFile is
0 commit comments