advancedtls: add new module for advanced TLS handshaker (#3187)

This commit is contained in:
ZhenLian
2020-01-07 15:47:01 -08:00
committed by Doug Fawley
parent 4346c5926d
commit 4a4d179f28
12 changed files with 1609 additions and 0 deletions

View File

@ -0,0 +1,412 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package advancedtls is a utility library containing functions to construct
// credentials.TransportCredentials that can perform credential reloading and custom
// server authorization.
package advancedtls
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net"
"syscall"
"time"
"google.golang.org/grpc/credentials"
)
// VerificationFuncParams contains the parameters available to users when implementing CustomVerificationFunc.
type VerificationFuncParams struct {
ServerName string
RawCerts [][]byte
VerifiedChains [][]*x509.Certificate
}
// VerificationResults contains the information about results of CustomVerificationFunc.
// VerificationResults is an empty struct for now. It may be extended in the future to include more information.
type VerificationResults struct{}
// CustomVerificationFunc is the function defined by users to perform custom server authorization.
// CustomVerificationFunc returns nil if the authorization fails; otherwise returns an empty struct.
type CustomVerificationFunc func(params *VerificationFuncParams) (*VerificationResults, error)
// GetRootCAsParams contains the parameters available to users when implementing GetRootCAs.
type GetRootCAsParams struct {
RawConn net.Conn
RawCerts [][]byte
}
// GetRootCAsResults contains the results of GetRootCAs.
// If users want to reload the root trust certificate, it is required to return the proper TrustCerts in GetRootCAs.
type GetRootCAsResults struct {
TrustCerts *x509.CertPool
}
// RootCertificateOptions contains a field and a function for obtaining root trust certificates.
// It is used by both ClientOptions and ServerOptions. Note that RootCertificateOptions is required
// to be correctly set on client side; on server side, it is only required when mutual TLS is
// enabled(RequireClientCert in ServerOptions is true).
type RootCertificateOptions struct {
// If field RootCACerts is set, field GetRootCAs will be ignored. RootCACerts will be used
// every time when verifying the peer certificates, without performing root certificate reloading.
RootCACerts *x509.CertPool
// If GetRootCAs is set and RootCACerts is nil, GetRootCAs will be invoked every time
// asked to check certificates sent from the server when a new connection is established.
// This is known as root CA certificate reloading.
GetRootCAs func(params *GetRootCAsParams) (*GetRootCAsResults, error)
}
// ClientOptions contains all the fields and functions needed to be filled by the client.
// General rules for certificate setting on client side:
// Certificates or GetClientCertificate indicates the certificates sent from the client to the
// server to prove client's identities. The rules for setting these two fields are:
// If requiring mutual authentication on server side:
// Either Certificates or GetClientCertificate must be set; the other will be ignored
// Otherwise:
// Nothing needed(the two fields will be ignored)
type ClientOptions struct {
// If field Certificates is set, field GetClientCertificate will be ignored. The client will use
// Certificates every time when asked for a certificate, without performing certificate reloading.
Certificates []tls.Certificate
// If GetClientCertificate is set and Certificates is nil, the client will invoke this
// function every time asked to present certificates to the server when a new connection is
// established. This is known as peer certificate reloading.
GetClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
// VerifyPeer is a custom server authorization checking after certificate signature check.
// If this is set, we will replace the hostname check with this customized authorization check.
// If this is nil, we fall back to typical hostname check.
VerifyPeer CustomVerificationFunc
// ServerNameOverride is for testing only. If set to a non-empty string,
// it will override the virtual host name of authority (e.g. :authority header field) in requests.
ServerNameOverride string
RootCertificateOptions
}
// ServerOptions contains all the fields and functions needed to be filled by the client.
// General rules for certificate setting on server side:
// Certificates or GetClientCertificate indicates the certificates sent from the server to
// the client to prove server's identities. The rules for setting these two fields are:
// Either Certificates or GetCertificate must be set; the other will be ignored
type ServerOptions struct {
// If field Certificates is set, field GetClientCertificate will be ignored. The server will use
// Certificates every time when asked for a certificate, without performing certificate reloading.
Certificates []tls.Certificate
// If GetClientCertificate is set and Certificates is nil, the server will invoke this
// function every time asked to present certificates to the client when a new connection is
// established. This is known as peer certificate reloading.
GetCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error)
RootCertificateOptions
// If the server want the client to send certificates.
RequireClientCert bool
}
func (o *ClientOptions) config() (*tls.Config, error) {
if o.RootCACerts == nil && o.GetRootCAs == nil && o.VerifyPeer == nil {
return nil, fmt.Errorf(
"client needs to provide root CA certs, or a custom verification function")
}
// We have to set InsecureSkipVerify to true to skip the default checks and use the
// verification function we built from buildVerifyFunc.
config := &tls.Config{
ServerName: o.ServerNameOverride,
Certificates: o.Certificates,
GetClientCertificate: o.GetClientCertificate,
RootCAs: o.RootCACerts,
InsecureSkipVerify: true,
}
return config, nil
}
func (o *ServerOptions) config() (*tls.Config, error) {
if o.Certificates == nil && o.GetCertificate == nil {
return nil, fmt.Errorf("either Certificates or GetCertificate must be specified")
}
if o.RequireClientCert && o.GetRootCAs == nil && o.RootCACerts == nil {
return nil, fmt.Errorf("server needs to provide root CA certs if requiring client cert")
}
clientAuth := tls.NoClientCert
if o.RequireClientCert {
// We fall back to normal config settings if users don't need to reload root certificates.
// If using RequireAndVerifyClientCert, the underlying stack would use the default
// checking and ignore the verification function we built from buildVerifyFunc.
// If using RequireAnyClientCert, the code would skip all the checks and use the
// function from buildVerifyFunc.
if o.RootCACerts != nil {
clientAuth = tls.RequireAndVerifyClientCert
} else {
clientAuth = tls.RequireAnyClientCert
}
}
config := &tls.Config{
ClientAuth: clientAuth,
Certificates: o.Certificates,
GetCertificate: o.GetCertificate,
}
if o.RootCACerts != nil {
config.ClientCAs = o.RootCACerts
}
return config, nil
}
// advancedTLSCreds is the credentials required for authenticating a connection using TLS.
type advancedTLSCreds struct {
config *tls.Config
verifyFunc CustomVerificationFunc
getRootCAs func(params *GetRootCAsParams) (*GetRootCAsResults, error)
isClient bool
}
func (c advancedTLSCreds) Info() credentials.ProtocolInfo {
return credentials.ProtocolInfo{
SecurityProtocol: "tls",
SecurityVersion: "1.2",
ServerName: c.config.ServerName,
}
}
func (c *advancedTLSCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
// Use local cfg to avoid clobbering ServerName if using multiple endpoints.
cfg := cloneTLSConfig(c.config)
// We return the full authority name to users if ServerName is empty without
// stripping the trailing port.
if cfg.ServerName == "" {
cfg.ServerName = authority
}
cfg.VerifyPeerCertificate = buildVerifyFunc(c, cfg.ServerName, rawConn)
conn := tls.Client(rawConn, cfg)
errChannel := make(chan error, 1)
go func() {
errChannel <- conn.Handshake()
close(errChannel)
}()
select {
case err := <-errChannel:
if err != nil {
conn.Close()
return nil, nil, err
}
case <-ctx.Done():
conn.Close()
return nil, nil, ctx.Err()
}
return WrapSyscallConn(rawConn, conn), credentials.TLSInfo{State: conn.ConnectionState()}, nil
}
func (c *advancedTLSCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
cfg := cloneTLSConfig(c.config)
// We build server side verification function only when root cert reloading is needed.
if c.getRootCAs != nil {
cfg.VerifyPeerCertificate = buildVerifyFunc(c, "", rawConn)
}
conn := tls.Server(rawConn, cfg)
if err := conn.Handshake(); err != nil {
conn.Close()
return nil, nil, err
}
return WrapSyscallConn(rawConn, conn), credentials.TLSInfo{State: conn.ConnectionState()}, nil
}
func (c *advancedTLSCreds) Clone() credentials.TransportCredentials {
return &advancedTLSCreds{
config: cloneTLSConfig(c.config),
verifyFunc: c.verifyFunc,
getRootCAs: c.getRootCAs,
isClient: c.isClient,
}
}
func (c *advancedTLSCreds) OverrideServerName(serverNameOverride string) error {
c.config.ServerName = serverNameOverride
return nil
}
// The function buildVerifyFunc is used when users want root cert reloading, and possibly custom
// server authorization check.
// We have to build our own verification function here because current tls module:
// 1. does not have a good support on root cert reloading
// 2. will ignore basic certificate check when setting InsecureSkipVerify to true
func buildVerifyFunc(c *advancedTLSCreds,
serverName string,
rawConn net.Conn) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// If users didn't specify either rootCAs or getRootCAs on client side,
// as we see some use cases such as https://github.com/grpc/grpc/pull/20530,
// instead of failing, we just don't validate the server cert and let
// application decide via VerifyPeer
if c.isClient && c.config.RootCAs == nil && c.getRootCAs == nil {
if c.verifyFunc != nil {
_, err := c.verifyFunc(&VerificationFuncParams{
ServerName: serverName,
RawCerts: rawCerts,
VerifiedChains: verifiedChains,
})
return err
}
}
var rootCAs *x509.CertPool
if c.isClient {
rootCAs = c.config.RootCAs
} else {
rootCAs = c.config.ClientCAs
}
// reload root CA certs
if rootCAs == nil && c.getRootCAs != nil {
results, err := c.getRootCAs(&GetRootCAsParams{
RawConn: rawConn,
RawCerts: rawCerts,
})
if err != nil {
return err
}
rootCAs = results.TrustCerts
}
// verify peers' certificates against RootCAs and get verifiedChains
certs := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
return err
}
certs[i] = cert
}
opts := x509.VerifyOptions{
Roots: rootCAs,
CurrentTime: time.Now(),
Intermediates: x509.NewCertPool(),
}
if !c.isClient {
opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
} else {
opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
}
for _, cert := range certs[1:] {
opts.Intermediates.AddCert(cert)
}
// We use default hostname check if users don't specify verifyFunc function
if c.isClient && c.verifyFunc == nil && serverName != "" {
opts.DNSName = serverName
}
verifiedChains, err := certs[0].Verify(opts)
if err != nil {
return err
}
if c.isClient && c.verifyFunc != nil {
if c.verifyFunc != nil {
_, err := c.verifyFunc(&VerificationFuncParams{
ServerName: serverName,
RawCerts: rawCerts,
VerifiedChains: verifiedChains,
})
return err
}
}
return nil
}
}
// NewClient uses ClientOptions to construct a TransportCredentials based on TLS.
func NewClient(o *ClientOptions) (credentials.TransportCredentials, error) {
conf, err := o.config()
if err != nil {
return nil, err
}
tc := &advancedTLSCreds{
config: conf,
isClient: true,
getRootCAs: o.GetRootCAs,
verifyFunc: o.VerifyPeer,
}
tc.config.NextProtos = appendH2ToNextProtos(tc.config.NextProtos)
return tc, nil
}
// NewServer uses ServerOptions to construct a TransportCredentials based on TLS.
func NewServer(o *ServerOptions) (credentials.TransportCredentials, error) {
conf, err := o.config()
if err != nil {
return nil, err
}
tc := &advancedTLSCreds{
config: conf,
isClient: false,
getRootCAs: o.GetRootCAs,
}
tc.config.NextProtos = appendH2ToNextProtos(tc.config.NextProtos)
return tc, nil
}
// TODO(ZhenLian): The code below are duplicates with gRPC-Go under
// credentials/internal. Consider refactoring in the future.
const alpnProtoStrH2 = "h2"
func appendH2ToNextProtos(ps []string) []string {
for _, p := range ps {
if p == alpnProtoStrH2 {
return ps
}
}
ret := make([]string, 0, len(ps)+1)
ret = append(ret, ps...)
return append(ret, alpnProtoStrH2)
}
// We give syscall.Conn a new name here since syscall.Conn and net.Conn used
// below have the same names.
type sysConn = syscall.Conn
// syscallConn keeps reference of rawConn to support syscall.Conn for channelz.
// SyscallConn() (the method in interface syscall.Conn) is explicitly
// implemented on this type,
//
// Interface syscall.Conn is implemented by most net.Conn implementations (e.g.
// TCPConn, UnixConn), but is not part of net.Conn interface. So wrapper conns
// that embed net.Conn don't implement syscall.Conn. (Side note: tls.Conn
// doesn't embed net.Conn, so even if syscall.Conn is part of net.Conn, it won't
// help here).
type syscallConn struct {
net.Conn
// sysConn is a type alias of syscall.Conn. It's necessary because the name
// `Conn` collides with `net.Conn`.
sysConn
}
// WrapSyscallConn tries to wrap rawConn and newConn into a net.Conn that
// implements syscall.Conn. rawConn will be used to support syscall, and newConn
// will be used for read/write.
//
// This function returns newConn if rawConn doesn't implement syscall.Conn.
func WrapSyscallConn(rawConn, newConn net.Conn) net.Conn {
sysConn, ok := rawConn.(syscall.Conn)
if !ok {
return newConn
}
return &syscallConn{
Conn: newConn,
sysConn: sysConn,
}
}
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
if cfg == nil {
return &tls.Config{}
}
return cfg.Clone()
}

View File

@ -0,0 +1,635 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package advancedtls
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"net"
"reflect"
"strings"
"syscall"
"testing"
"google.golang.org/grpc/credentials"
)
func TestClientServerHandshake(t *testing.T) {
// ------------------Load Client Trust Cert and Peer Cert-------------------
clientTrustPool, err := readTrustCert("testdata/client_trust_cert_1.pem")
if err != nil {
t.Fatalf("Client is unable to load trust certs. Error: %v", err)
}
getRootCAsForClient := func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
return &GetRootCAsResults{TrustCerts: clientTrustPool}, nil
}
verifyFunc := func(params *VerificationFuncParams) (*VerificationResults, error) {
results := &VerificationResults{}
if strings.HasPrefix(params.ServerName, "127.0.0.1") {
return results, nil
}
return results, fmt.Errorf("custom verification function failed")
}
clientPeerCert, err := tls.LoadX509KeyPair("testdata/client_cert_1.pem",
"testdata/client_key_1.pem")
if err != nil {
t.Fatalf("Client is unable to parse peer certificates. Error: %v", err)
}
// ------------------Load Server Trust Cert and Peer Cert-------------------
serverTrustPool, err := readTrustCert("testdata/server_trust_cert_1.pem")
if err != nil {
t.Fatalf("Server is unable to load trust certs. Error: %v", err)
}
getRootCAsForServer := func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
return &GetRootCAsResults{TrustCerts: serverTrustPool}, nil
}
serverPeerCert, err := tls.LoadX509KeyPair("testdata/server_cert_1.pem",
"testdata/server_key_1.pem")
if err != nil {
t.Fatalf("Server is unable to parse peer certificates. Error: %v", err)
}
getRootCAsForServerBad := func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
return nil, fmt.Errorf("bad root certificate reloading")
}
for _, test := range []struct {
desc string
clientCert []tls.Certificate
clientGetClientCert func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
clientRoot *x509.CertPool
clientGetRoot func(params *GetRootCAsParams) (*GetRootCAsResults, error)
clientVerifyFunc CustomVerificationFunc
clientExpectCreateError bool
clientExpectHandshakeError bool
serverMutualTLS bool
serverCert []tls.Certificate
serverGetCert func(*tls.ClientHelloInfo) (*tls.Certificate, error)
serverRoot *x509.CertPool
serverGetRoot func(params *GetRootCAsParams) (*GetRootCAsResults, error)
serverExpectError bool
}{
// Client: nil setting
// Server: only set serverCert with mutual TLS off
// Expected Behavior: server side failure
// Reason: if either clientCert or clientGetClientCert is not set and
// verifyFunc is not set, we will fail directly
{
"Client_no_trust_cert_Server_peer_cert",
nil,
nil,
nil,
nil,
nil,
true,
false,
false,
[]tls.Certificate{serverPeerCert},
nil,
nil,
nil,
true,
},
// Client: nil setting except verifyFunc
// Server: only set serverCert with mutual TLS off
// Expected Behavior: success
// Reason: we will use verifyFunc to verify the server,
// if either clientCert or clientGetClientCert is not set
{
"Client_no_trust_cert_verifyFunc_Server_peer_cert",
nil,
nil,
nil,
nil,
verifyFunc,
false,
false,
false,
[]tls.Certificate{serverPeerCert},
nil,
nil,
nil,
false,
},
// Client: only set clientRoot
// Server: only set serverCert with mutual TLS off
// Expected Behavior: server side failure and client handshake failure
// Reason: not setting advanced TLS features will fall back to normal check, and will hence fail
// on default host name check. All the default hostname checks will fail in this test suites
// (since it connects to 127.0.0.1).
{
"Client_root_cert_Server_peer_cert",
nil,
nil,
clientTrustPool,
nil,
nil,
false,
true,
false,
[]tls.Certificate{serverPeerCert},
nil,
nil,
nil,
true,
},
// Client: only set clientGetRoot
// Server: only set serverCert with mutual TLS off
// Expected Behavior: server side failure and client handshake failure
// Reason: setting root reloading function without custom verifyFunc will also fail,
// since it will also fall back to default host name check
{
"Client_reload_root_Server_peer_cert",
nil,
nil,
nil,
getRootCAsForClient,
nil,
false,
true,
false,
[]tls.Certificate{serverPeerCert},
nil,
nil,
nil,
true,
},
// Client: set clientGetRoot and clientVerifyFunc
// Server: only set serverCert with mutual TLS off
// Expected Behavior: success
{
"Client_reload_root_verifyFunc_Server_peer_cert",
nil,
nil,
nil,
getRootCAsForClient,
verifyFunc,
false,
false,
false,
[]tls.Certificate{serverPeerCert},
nil,
nil,
nil,
false,
},
// Client: set clientGetRoot and clientVerifyFunc
// Server: nil setting
// Expected Behavior: server side failure
// Reason: server side must either set serverCert or serverGetCert
{
"Client_reload_root_verifyFunc_Server_nil",
nil,
nil,
nil,
getRootCAsForClient,
verifyFunc,
false,
false,
false,
nil,
nil,
nil,
nil,
true,
},
// Client: set clientGetRoot and clientVerifyFunc
// Server: only set serverCert with mutual TLS on
// Expected Behavior: server side failure
// Reason: server side must either set serverRoot or serverGetRoot when using mutual TLS
{
"Client_reload_root_verifyFunc_Server_peer_cert_no_root_cert_mutualTLS",
nil,
nil,
nil,
getRootCAsForClient,
verifyFunc,
false,
false,
true,
[]tls.Certificate{serverPeerCert},
nil,
nil,
nil,
true,
},
// Client: set clientGetRoot, clientVerifyFunc and clientCert
// Server: set serverRoot and serverCert with mutual TLS on
// Expected Behavior: success
{
"Client_peer_cert_reload_root_verifyFunc_Server_peer_cert_root_cert_mutualTLS",
[]tls.Certificate{clientPeerCert},
nil,
nil,
getRootCAsForClient,
verifyFunc,
false,
false,
true,
[]tls.Certificate{serverPeerCert},
nil,
serverTrustPool,
nil,
false,
},
// Client: set clientGetRoot, clientVerifyFunc and clientCert
// Server: set serverGetRoot and serverCert with mutual TLS on
// Expected Behavior: success
{
"Client_peer_cert_reload_root_verifyFunc_Server_peer_cert_reload_root_mutualTLS",
[]tls.Certificate{clientPeerCert},
nil,
nil,
getRootCAsForClient,
verifyFunc,
false,
false,
true,
[]tls.Certificate{serverPeerCert},
nil,
nil,
getRootCAsForServer,
false,
},
// Client: set clientGetRoot, clientVerifyFunc and clientCert
// Server: set serverGetRoot returning error and serverCert with mutual TLS on
// Expected Behavior: server side failure
// Reason: server side reloading returns failure
{
"Client_peer_cert_reload_root_verifyFunc_Server_peer_cert_bad_reload_root_mutualTLS",
[]tls.Certificate{clientPeerCert},
nil,
nil,
getRootCAsForClient,
verifyFunc,
false,
false,
true,
[]tls.Certificate{serverPeerCert},
nil,
nil,
getRootCAsForServerBad,
true,
},
// Client: set clientGetRoot, clientVerifyFunc and clientGetClientCert
// Server: set serverGetRoot and serverGetCert with mutual TLS on
// Expected Behavior: success
{
"Client_reload_both_certs_verifyFunc_Server_reload_both_certs_mutualTLS",
nil,
func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return &clientPeerCert, nil
},
nil,
getRootCAsForClient,
verifyFunc,
false,
false,
true,
nil,
func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return &serverPeerCert, nil
},
nil,
getRootCAsForServer,
false,
},
// Client: set everything but with the wrong peer cert not trusted by server
// Server: set serverGetRoot and serverGetCert with mutual TLS on
// Expected Behavior: server side returns failure because of
// certificate mismatch
{
"Client_wrong_peer_cert_Server_reload_both_certs_mutualTLS",
nil,
func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return &serverPeerCert, nil
},
nil,
getRootCAsForClient,
verifyFunc,
false,
false,
true,
nil,
func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return &serverPeerCert, nil
},
nil,
getRootCAsForServer,
true,
},
// Client: set everything but with the wrong trust cert not trusting server
// Server: set serverGetRoot and serverGetCert with mutual TLS on
// Expected Behavior: server side and client side return failure due to
// certificate mismatch and handshake failure
{
"Client_wrong_trust_cert_Server_reload_both_certs_mutualTLS",
nil,
func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return &clientPeerCert, nil
},
nil,
getRootCAsForServer,
verifyFunc,
false,
true,
true,
nil,
func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return &serverPeerCert, nil
},
nil,
getRootCAsForServer,
true,
},
// Client: set clientGetRoot, clientVerifyFunc and clientCert
// Server: set everything but with the wrong peer cert not trusted by client
// Expected Behavior: server side and client side return failure due to
// certificate mismatch and handshake failure
{
"Client_reload_both_certs_verifyFunc_Server_wrong_peer_cert",
nil,
func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return &clientPeerCert, nil
},
nil,
getRootCAsForClient,
verifyFunc,
false,
false,
true,
nil,
func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return &clientPeerCert, nil
},
nil,
getRootCAsForServer,
true,
},
// Client: set clientGetRoot, clientVerifyFunc and clientCert
// Server: set everything but with the wrong trust cert not trusting client
// Expected Behavior: server side and client side return failure due to
// certificate mismatch and handshake failure
{
"Client_reload_both_certs_verifyFunc_Server_wrong_trust_cert",
nil,
func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return &clientPeerCert, nil
},
nil,
getRootCAsForClient,
verifyFunc,
false,
true,
true,
nil,
func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return &serverPeerCert, nil
},
nil,
getRootCAsForClient,
true,
},
} {
test := test
t.Run(test.desc, func(t *testing.T) {
done := make(chan credentials.AuthInfo, 1)
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("Failed to listen: %v", err)
}
// Start a server using ServerOptions in another goroutine.
serverOptions := &ServerOptions{
Certificates: test.serverCert,
GetCertificate: test.serverGetCert,
RootCertificateOptions: RootCertificateOptions{
RootCACerts: test.serverRoot,
GetRootCAs: test.serverGetRoot,
},
RequireClientCert: test.serverMutualTLS,
}
go func(done chan credentials.AuthInfo, lis net.Listener, serverOptions *ServerOptions) {
serverRawConn, err := lis.Accept()
if err != nil {
close(done)
return
}
serverTLS, err := NewServer(serverOptions)
if err != nil {
serverRawConn.Close()
close(done)
return
}
_, serverAuthInfo, err := serverTLS.ServerHandshake(serverRawConn)
if err != nil {
serverRawConn.Close()
close(done)
return
}
done <- serverAuthInfo
}(done, lis, serverOptions)
defer lis.Close()
// Start a client using ClientOptions and connects to the server.
lisAddr := lis.Addr().String()
conn, err := net.Dial("tcp", lisAddr)
if err != nil {
t.Fatalf("Client failed to connect to %s. Error: %v", lisAddr, err)
}
defer conn.Close()
clientOptions := &ClientOptions{
Certificates: test.clientCert,
GetClientCertificate: test.clientGetClientCert,
VerifyPeer: test.clientVerifyFunc,
RootCertificateOptions: RootCertificateOptions{
RootCACerts: test.clientRoot,
GetRootCAs: test.clientGetRoot,
},
}
clientTLS, newClientErr := NewClient(clientOptions)
if newClientErr != nil && test.clientExpectCreateError {
return
}
if newClientErr != nil && !test.clientExpectCreateError ||
newClientErr == nil && test.clientExpectCreateError {
t.Fatalf("Expect error: %v, but err is %v",
test.clientExpectCreateError, newClientErr)
}
_, clientAuthInfo, handshakeErr := clientTLS.ClientHandshake(context.Background(),
lisAddr, conn)
// wait until server sends serverAuthInfo or fails.
serverAuthInfo, ok := <-done
if !ok && test.serverExpectError {
return
}
if ok && test.serverExpectError || !ok && !test.serverExpectError {
t.Fatalf("Server side error mismatch, got %v, want %v", !ok, test.serverExpectError)
}
if handshakeErr != nil && test.clientExpectHandshakeError {
return
}
if handshakeErr != nil && !test.clientExpectHandshakeError ||
handshakeErr == nil && test.clientExpectHandshakeError {
t.Fatalf("Expect error: %v, but err is %v",
test.clientExpectHandshakeError, handshakeErr)
}
if !compare(clientAuthInfo, serverAuthInfo) {
t.Fatalf("c.ClientHandshake(_, %v, _) = %v, want %v.", lisAddr,
clientAuthInfo, serverAuthInfo)
}
})
}
}
func readTrustCert(fileName string) (*x509.CertPool, error) {
trustData, err := ioutil.ReadFile(fileName)
if err != nil {
return nil, err
}
trustBlock, _ := pem.Decode(trustData)
if trustBlock == nil {
return nil, err
}
trustCert, err := x509.ParseCertificate(trustBlock.Bytes)
if err != nil {
return nil, err
}
trustPool := x509.NewCertPool()
trustPool.AddCert(trustCert)
return trustPool, nil
}
func compare(a1, a2 credentials.AuthInfo) bool {
if a1.AuthType() != a2.AuthType() {
return false
}
switch a1.AuthType() {
case "tls":
state1 := a1.(credentials.TLSInfo).State
state2 := a2.(credentials.TLSInfo).State
if state1.Version == state2.Version &&
state1.HandshakeComplete == state2.HandshakeComplete &&
state1.CipherSuite == state2.CipherSuite &&
state1.NegotiatedProtocol == state2.NegotiatedProtocol {
return true
}
return false
default:
return false
}
}
func TestAdvancedTLSOverrideServerName(t *testing.T) {
expectedServerName := "server.name"
clientTrustPool, err := readTrustCert("testdata/client_trust_cert_1.pem")
if err != nil {
t.Fatalf("Client is unable to load trust certs. Error: %v", err)
}
clientOptions := &ClientOptions{
RootCertificateOptions: RootCertificateOptions{
RootCACerts: clientTrustPool,
},
ServerNameOverride: expectedServerName,
}
c, err := NewClient(clientOptions)
if err != nil {
t.Fatalf("Client is unable to create credentials. Error: %v", err)
}
c.OverrideServerName(expectedServerName)
if c.Info().ServerName != expectedServerName {
t.Fatalf("c.Info().ServerName = %v, want %v", c.Info().ServerName, expectedServerName)
}
}
func TestTLSClone(t *testing.T) {
expectedServerName := "server.name"
clientTrustPool, err := readTrustCert("testdata/client_trust_cert_1.pem")
if err != nil {
t.Fatalf("Client is unable to load trust certs. Error: %v", err)
}
clientOptions := &ClientOptions{
RootCertificateOptions: RootCertificateOptions{
RootCACerts: clientTrustPool,
},
ServerNameOverride: expectedServerName,
}
c, err := NewClient(clientOptions)
cc := c.Clone()
if cc.Info().ServerName != expectedServerName {
t.Fatalf("cc.Info().ServerName = %v, want %v", cc.Info().ServerName, expectedServerName)
}
cc.OverrideServerName("")
if c.Info().ServerName != expectedServerName {
t.Fatalf("Change in clone should not affect the original, "+
"c.Info().ServerName = %v, want %v", c.Info().ServerName, expectedServerName)
}
}
func TestAppendH2ToNextProtos(t *testing.T) {
tests := []struct {
name string
ps []string
want []string
}{
{
name: "empty",
ps: nil,
want: []string{"h2"},
},
{
name: "only h2",
ps: []string{"h2"},
want: []string{"h2"},
},
{
name: "with h2",
ps: []string{"alpn", "h2"},
want: []string{"alpn", "h2"},
},
{
name: "no h2",
ps: []string{"alpn"},
want: []string{"alpn", "h2"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := appendH2ToNextProtos(tt.ps); !reflect.DeepEqual(got, tt.want) {
t.Errorf("appendH2ToNextProtos() = %v, want %v", got, tt.want)
}
})
}
}
type nonSyscallConn struct {
net.Conn
}
func TestWrapSyscallConn(t *testing.T) {
sc := &syscallConn{}
nsc := &nonSyscallConn{}
wrapConn := WrapSyscallConn(sc, nsc)
if _, ok := wrapConn.(syscall.Conn); !ok {
t.Errorf("returned conn (type %T) doesn't implement syscall.Conn, want implement",
wrapConn)
}
}

View File

@ -0,0 +1,5 @@
module google.golang.org/grpc/security/advancedtls
go 1.13
require google.golang.org/grpc v1.25.1

View File

@ -0,0 +1,43 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -0,0 +1,122 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 5 (0x5)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=VA, O=Internet Widgits Pty Ltd, CN=foo.bar.hoo.ca.com
Validity
Not Before: Nov 15 19:15:24 2019 GMT
Not After : Aug 29 19:15:24 2293 GMT
Subject: C=US, ST=CA, O=Internet Widgits Pty Ltd, CN=foo.bar.hoo.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:c3:3e:b5:d8:bc:73:5f:b4:e5:60:a8:73:0e:8c:
9c:ff:28:2c:a9:bc:68:12:8b:ae:3e:aa:7b:f5:b9:
2d:b3:73:f8:9e:64:e9:8e:26:ca:36:11:5f:f9:73:
39:f0:19:55:d1:ba:f4:4e:2b:ab:ee:01:3b:eb:f0:
5f:6e:b3:24:39:c1:f5:f0:bd:6a:d0:6c:56:cf:96:
33:5d:05:48:c4:b5:b3:3e:55:8e:89:8e:6b:79:c5:
3b:99:1c:e3:03:69:0f:74:a2:97:7b:bf:c4:11:1d:
da:d7:cb:87:0d:90:25:64:29:3e:f6:62:bc:f9:a5:
56:de:56:e1:27:77:51:1a:30:f1:88:89:01:c2:c8:
35:40:d3:2e:2d:30:ef:d7:de:3b:28:15:4a:a4:a9:
ba:f0:40:f0:79:3a:16:f9:ae:52:32:c3:52:ad:53:
9c:94:07:d5:9b:63:50:90:ff:f1:8c:fd:4e:59:b8:
5e:0a:73:9b:b4:b7:60:e1:7c:07:02:50:74:f3:48:
69:6a:74:7c:b2:96:70:86:19:2f:82:4c:95:57:aa:
4c:2f:38:75:8b:9b:a1:3e:7d:dd:da:bf:d2:a4:a3:
3a:02:17:43:35:0a:52:03:f5:fb:1a:a1:60:28:c3:
e7:41:eb:4a:0c:f4:43:6e:81:64:ba:41:8d:61:40:
97:9f:e2:67:51:7c:2d:2f:17:72:b9:a0:27:5c:fc:
e3:b6:a6:de:f4:1e:34:95:2c:c5:7f:13:c4:bb:25:
76:3e:3b:39:b6:36:d0:60:17:1e:c7:01:9c:3d:65:
9a:96:4c:d8:4c:10:85:32:76:c7:6e:53:64:80:c9:
33:1a:44:39:a7:c7:69:d3:64:c3:4c:06:20:56:d2:
eb:d9:65:56:02:65:c4:ba:72:db:89:c4:00:3f:89:
f4:75:d5:6d:83:ce:ad:66:fb:73:f8:8e:bb:dc:01:
c0:4f:86:c1:57:45:68:34:3f:55:1f:0e:ef:82:3f:
9a:26:1c:9c:8d:88:5e:27:ab:b6:b9:58:a7:c5:b0:
36:0f:99:ba:d8:cc:89:41:ed:ab:26:b8:8a:16:17:
21:67:b6:4d:83:d1:dd:53:de:67:ab:76:a3:af:f8:
60:99:29:6a:0a:4f:f2:ad:32:54:69:33:8c:f2:ca:
9b:d6:59:cd:8c:69:cd:3f:d3:8f:05:28:d1:29:04:
bf:b2:de:98:0f:9d:62:13:6d:fe:de:be:2d:c6:be:
d6:f8:10:cb:b5:b3:4f:ad:a4:60:36:b3:19:29:29:
b9:b4:37:5d:13:e7:36:cb:f9:fa:7f:9e:63:7e:f3:
05:ee:9e:e6:4d:ff:e3:46:a4:7b:1f:12:72:89:b6:
10:5f:bd
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
7F:9D:9C:C6:86:DF:9E:07:93:94:EF:18:2D:0A:0A:50:AA:1F:A2:B7
X509v3 Authority Key Identifier:
keyid:B4:19:08:1C:FC:10:23:C5:30:86:22:BC:CB:B1:5F:AD:EA:7A:5D:F1
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Key Encipherment
Signature Algorithm: sha256WithRSAEncryption
31:b0:6d:25:5e:8e:9b:73:01:ac:08:b9:a6:70:8e:de:18:fd:
b8:2b:bb:2d:7c:c0:84:20:c8:d2:32:8a:d9:ca:24:b9:75:e2:
c8:91:40:db:0a:4e:e5:05:bd:a6:bb:22:85:3c:8e:be:d3:65:
0a:7f:cd:c0:fc:eb:94:61:91:30:53:1d:4d:9f:4e:d7:38:0f:
ab:d9:3d:a1:1c:48:c1:e6:3c:35:cc:db:47:31:a6:b2:44:b8:
db:34:6c:28:20:49:ff:e1:2b:cd:48:e1:7e:78:7a:05:0a:31:
3d:dd:45:51:95:06:ad:5c:8c:0e:ff:0c:98:77:4f:5c:42:dc:
da:d8:d3:30:58:e4:3c:ef:b3:64:3f:f2:e2:19:d9:36:04:1a:
b4:87:c2:1b:89:5d:52:17:fb:27:a2:83:2d:55:6d:1f:80:d5:
a7:ea:20:b0:0a:23:4d:0f:48:36:ae:42:f9:fc:c8:86:f4:69:
30:e8:cd:52:34:62:ee:b9:fd:12:4b:ba:4d:a2:75:47:d4:b6:
b2:dd:ea:6f:6b:a2:86:f5:c0:3b:06:09:c1:5f:30:96:b6:79:
32:45:b3:d1:8c:0a:d2:58:d3:39:2f:21:ba:7a:3e:a7:38:cc:
88:16:1e:75:62:30:fd:79:a3:1d:a9:bd:df:66:dc:b9:f5:79:
bc:fb:bd:bd:e5:f0:46:60:d1:03:7b:58:06:00:f5:d8:36:a0:
a9:b0:2d:4f:4e:1b:6f:17:f0:d9:51:0c:25:a2:48:ac:e3:f4:
a6:52:59:84:83:e3:79:df:ca:9e:5c:24:d3:f9:55:39:8c:3e:
2a:91:3f:53:0b:d4:22:55:c7:a3:80:41:05:e3:41:7d:16:d1:
af:a2:1e:f7:fa:ee:f3:a7:6e:19:66:af:dd:23:39:5a:33:f9:
61:3d:e7:90:e2:9a:0e:8e:8b:a0:3b:27:55:e2:ed:09:c5:ca:
71:14:95:10:be:03:8e:2a:6d:48:c5:85:a5:f4:39:0e:2d:f5:
64:50:f4:b6:35:f9:63:58:d0:5d:09:01:f9:bc:99:60:dc:25:
94:36:3b:ee:b9:9d:23:2f:52:80:9c:f1:e4:9b:5f:a4:37:c9:
63:32:cf:ca:d6:2a:b7:3b:c8:10:54:21:ca:03:d3:ae:0e:da:
cd:08:fe:71:10:f8:db:d4:e6:cf:d2:59:9b:3d:96:4a:a8:80:
42:69:ff:7f:4b:4b:52:42:aa:e7:e9:6e:7f:84:98:f5:13:16:
14:b0:4e:22:a6:80:03:29:6b:2e:33:ac:05:b5:75:25:58:72:
34:ff:ad:95:f0:52:9e:46:81:91:7b:6c:12:b1:43:af:70:06:
03:d8:c8:cb:4a:85:f2:37
-----BEGIN CERTIFICATE-----
MIIFiDCCA3CgAwIBAgIBBTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCVkExITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEb
MBkGA1UEAwwSZm9vLmJhci5ob28uY2EuY29tMCAXDTE5MTExNTE5MTUyNFoYDzIy
OTMwODI5MTkxNTI0WjBXMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEYMBYGA1UEAwwPZm9vLmJhci5o
b28uY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwz612LxzX7Tl
YKhzDoyc/ygsqbxoEouuPqp79bkts3P4nmTpjibKNhFf+XM58BlV0br0Tiur7gE7
6/BfbrMkOcH18L1q0GxWz5YzXQVIxLWzPlWOiY5recU7mRzjA2kPdKKXe7/EER3a
18uHDZAlZCk+9mK8+aVW3lbhJ3dRGjDxiIkBwsg1QNMuLTDv1947KBVKpKm68EDw
eToW+a5SMsNSrVOclAfVm2NQkP/xjP1OWbheCnObtLdg4XwHAlB080hpanR8spZw
hhkvgkyVV6pMLzh1i5uhPn3d2r/SpKM6AhdDNQpSA/X7GqFgKMPnQetKDPRDboFk
ukGNYUCXn+JnUXwtLxdyuaAnXPzjtqbe9B40lSzFfxPEuyV2Pjs5tjbQYBcexwGc
PWWalkzYTBCFMnbHblNkgMkzGkQ5p8dp02TDTAYgVtLr2WVWAmXEunLbicQAP4n0
ddVtg86tZvtz+I673AHAT4bBV0VoND9VHw7vgj+aJhycjYheJ6u2uVinxbA2D5m6
2MyJQe2rJriKFhchZ7ZNg9HdU95nq3ajr/hgmSlqCk/yrTJUaTOM8sqb1lnNjGnN
P9OPBSjRKQS/st6YD51iE23+3r4txr7W+BDLtbNPraRgNrMZKSm5tDddE+c2y/n6
f55jfvMF7p7mTf/jRqR7HxJyibYQX70CAwEAAaNaMFgwHQYDVR0OBBYEFH+dnMaG
354Hk5TvGC0KClCqH6K3MB8GA1UdIwQYMBaAFLQZCBz8ECPFMIYivMuxX63qel3x
MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQAxsG0l
Xo6bcwGsCLmmcI7eGP24K7stfMCEIMjSMorZyiS5deLIkUDbCk7lBb2muyKFPI6+
02UKf83A/OuUYZEwUx1Nn07XOA+r2T2hHEjB5jw1zNtHMaayRLjbNGwoIEn/4SvN
SOF+eHoFCjE93UVRlQatXIwO/wyYd09cQtza2NMwWOQ877NkP/LiGdk2BBq0h8Ib
iV1SF/snooMtVW0fgNWn6iCwCiNND0g2rkL5/MiG9Gkw6M1SNGLuuf0SS7pNonVH
1Lay3epva6KG9cA7BgnBXzCWtnkyRbPRjArSWNM5LyG6ej6nOMyIFh51YjD9eaMd
qb3fZty59Xm8+7295fBGYNEDe1gGAPXYNqCpsC1PThtvF/DZUQwlokis4/SmUlmE
g+N538qeXCTT+VU5jD4qkT9TC9QiVcejgEEF40F9FtGvoh73+u7zp24ZZq/dIzla
M/lhPeeQ4poOjougOydV4u0JxcpxFJUQvgOOKm1IxYWl9DkOLfVkUPS2NfljWNBd
CQH5vJlg3CWUNjvuuZ0jL1KAnPHkm1+kN8ljMs/K1iq3O8gQVCHKA9OuDtrNCP5x
EPjb1ObP0lmbPZZKqIBCaf9/S0tSQqrn6W5/hJj1ExYUsE4ipoADKWsuM6wFtXUl
WHI0/62V8FKeRoGRe2wSsUOvcAYD2MjLSoXyNw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFeTCCA2GgAwIBAgIURfVPAG6lOcTq29Ht/6KNbaKhm7IwDQYJKoZIhvcNAQEL
BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0xOTExMDQyMTQyNTFa
GA8yMjI1MDMwOTIxNDI1MVowSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQww
CgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCC
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL95INSZmsn3AgGX2Z9bu+6T
7n9VevXuD5bv/oc/Wpfb3d2rueBizpo1xRWMiNFq59wf+6o80LlRsJwqRCnMo7AU
FrR9zaryGzKa7mqkGnrX0oPIe/KP6sw+nUNtpr7m3PMpi3CkqTtZ1Oo1PhphyEu/
pTF2mmS/Blaez3BKFuX/zMLbnxedUgKD2ok8VqOPUGr8uQ4IPp3eZL9FptQ8Tt7N
7H4U/Wng4YNvCBDSlLP60NGycJ8yrdL1aJSWAHJ1vDbYlSo3JeO4cjHdGUq6HTx0
7USgg6ZX2OSEiE/DXRQbu7QkGjetssaURmUjUB3vbFCqmWZ9HDtkE3YYhk2vks6H
PQXlunNHUS7Ain+IgYsqK9cNRLWqcBbdo2IEKYYwAaK0xsQox/m4TuWacHMZw4Tg
Zh2Y984n6Hyq1H5FgWMYpng45VihT/iKZpD0r0vUBsDJQSQzsQkHjIJe6333gtey
8nWXm/dcRUZotcL+eJ6essniJ0ZBFz2m2DB/BKJ/5rA3hf1uQCPAdaLCho0QVUeE
gQShwTiP0og/0V6dHhqoDjnEnII9ZItGVn0NTl688a9VpzPGyCDcNtTuB089KtLs
UcE0vLtEmhlM0NI3CpP+ahQfxmF6i37VEzBoEzZfyzeaO2MrvmH7djfP3HYmx85M
yutAMo9NSpGWOiPYi/lFAgMBAAGjUzBRMB0GA1UdDgQWBBRapdqxmdTlDuYelOr/
/GLi7QnxBjAfBgNVHSMEGDAWgBRapdqxmdTlDuYelOr//GLi7QnxBjAPBgNVHRMB
Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBpeytWYsMSa3sTZD1pA39IvQ4v
xAbzmJB6P86sjmt2hNkCZTJYnOdSnwr8mrsKnLgjf/6kpg2OO3RqN+aXUnDeCpjl
9mzDJ4kcoVxbOXYAhTkhgugT/b8HOlMEvC+3yULrKN9deUmQPCdyVOtMBnTHE3++
VyAlP7T7R2xbgeVpOurO8SM7NLt04xxbtOS1cPVkhU2AE0+KlQBQYK9bProHTynR
zhVixFWtyS9asrMUwGh/xye85xTpy+unUxkzZoPqYvK6YiKv/WB3U3SURuLcOqAD
T/BMpOUwbYAP0KVEL5K82uo7csADLMwBkPvFhDsMaFGTkUdb014NQqRjTXOOK8ad
Xwnm4ur4GgT0Rr/iJ990JTOQkWYWlW3ZO6DSiUZCqeRWWhju290+aviOcrJeXS0V
XKkeJiYjbdnvFp/LIcg+V+n/HCDwwQgC3vQqlwd8PNvl0gKRX4EGjV+1lobZoKvD
WdIuSIIUkIDbv547n2ldp3GhJHIft6jlTOLAd3jonURO2/lZVyxj8yGFPbTWRUa0
aK7IWkcOzof0+v2BrEhQQoL+lwJahqYEPSKw6WNehQxYWaxr3TL/xeawFzEVW1ve
v8Vh1LvZ/qyucpP3dgDuj7gpVg0xshKpKEbGwzPKMz9PGcHJvgF1GOXVRIdoB9nU
IdXOcawI6rpqTXrTgA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
+xZ1BJ0Xfq/t2SnXDABwi9hvqTNOGqY=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,122 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3 (0x3)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=CA, L=SVL, O=Internet Widgits Pty Ltd
Validity
Not Before: Nov 4 21:43:00 2019 GMT
Not After : Aug 18 21:43:00 2293 GMT
Subject: C=US, ST=CA, L=DUMMYCITY, O=Internet Widgits Pty Ltd, CN=foo.bar.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:ec:3f:24:2d:91:3a:bd:c3:fc:15:72:42:b3:fb:
28:e6:04:a3:be:26:20:e6:ea:30:a8:aa:48:78:36:
0e:0b:99:29:3b:4b:f9:f1:d5:bf:bd:0c:13:7c:ea:
52:06:f4:bc:34:9e:2b:c0:b4:82:2c:87:fa:2f:e2:
cd:7c:d7:b9:e1:8f:04:71:6d:85:77:ae:18:40:e4:
b1:3a:4a:6b:e5:33:bf:3e:65:db:cf:94:64:87:1a:
20:46:c0:37:3a:9f:93:3f:d4:4f:ac:c4:e4:e0:28:
b6:0f:28:53:2a:cf:b9:fe:50:f2:ef:47:dc:7e:b6:
60:c2:47:85:b8:cb:ca:48:5b:fa:9f:8a:97:30:01:
f4:b3:51:0f:68:e1:60:ab:2f:a0:ad:fc:f0:10:4f:
60:e1:92:db:be:83:04:5c:40:87:ce:51:3e:9a:9e:
d6:1c:1b:19:cb:8c:c2:6c:57:74:6f:7b:af:94:3d:
53:ad:17:a5:99:69:7c:41:f5:3e:7a:5b:48:c7:78:
ff:d7:3b:a8:1f:f7:30:e7:83:26:78:e2:cb:a2:8f:
58:92:61:cd:ca:e9:b8:d1:80:c0:40:58:e9:d8:d3:
42:64:82:8f:e4:0c:b9:b1:36:db:9f:65:3f:3f:5b:
24:59:31:b3:60:0c:fa:41:5a:1b:b8:9d:ec:99:37:
90:fa:b5:e7:3f:cb:7c:e0:f9:ed:ea:27:ce:15:24:
c7:77:3b:45:45:2d:19:8e:2e:7f:65:0e:85:df:66:
50:69:24:2c:a4:6a:07:e5:3f:eb:28:84:53:94:4d:
5f:9c:a8:65:a6:50:4c:c0:35:06:40:6a:a5:62:b1:
93:60:e5:1c:85:28:34:9b:29:81:6f:e2:4f:cd:15:
30:b9:19:d7:4b:bb:30:0c:4b:2d:64:fe:3b:dd:0e:
a4:25:2c:4a:5c:de:d7:74:1f:5e:93:7b:1c:e8:c8:
fa:72:1f:4a:eb:8d:3f:98:e4:55:98:b8:e0:8a:29:
92:33:af:75:6b:05:84:05:d3:0c:2c:07:78:bc:0e:
b2:6d:a7:00:35:c4:53:1f:7b:e6:ba:07:72:a8:24:
c1:0a:a7:c4:46:e6:f2:6f:3a:79:23:00:0b:b8:e5:
1f:e0:e2:ee:c6:13:a3:57:d9:86:1a:95:f7:a3:04:
f1:46:d5:5f:21:d2:aa:d2:30:fb:f6:cb:e0:da:24:
c6:c3:30:2f:d2:1f:21:fe:bc:0f:99:ac:ac:9b:65:
9b:e4:83:9a:00:b8:2f:40:fc:3b:42:d3:7a:e8:b7:
52:d7:f4:67:2a:a5:f7:eb:78:f1:0a:56:8b:56:12:
d5:48:d8:48:70:ab:b8:69:5a:21:d3:71:b0:59:9d:
17:b4:4b
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
C0:82:DA:FA:69:46:30:AE:FF:6F:CD:BB:93:49:94:A6:D0:E2:17:EB
X509v3 Authority Key Identifier:
keyid:5A:A5:DA:B1:99:D4:E5:0E:E6:1E:94:EA:FF:FC:62:E2:ED:09:F1:06
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Key Encipherment
Signature Algorithm: sha256WithRSAEncryption
36:fd:cf:ec:f5:20:4b:52:dc:2e:38:f3:92:b1:e4:b6:a1:06:
86:aa:2d:c0:e6:f5:0a:58:97:a9:e3:be:13:09:61:79:ed:d4:
41:83:26:ad:ee:0b:43:83:d1:dd:19:1a:e8:7b:b2:1f:fe:d4:
c1:57:7d:6d:6b:d4:42:ea:7d:cd:34:8c:a4:1f:5b:3b:fa:de:
bb:2f:ae:56:b6:18:e5:53:a9:a3:99:58:ad:36:be:19:54:61:
0d:52:b6:a7:53:fc:60:e5:ff:f5:7f:82:3f:c1:49:06:cd:b2:
af:25:ee:de:bd:e0:e5:5e:ad:0b:dc:2e:b1:ec:7a:52:6f:9d:
e0:b9:84:18:db:49:53:ee:df:93:ee:8b:9d:9b:8e:3b:2a:82:
86:7f:45:c8:dd:d1:b0:40:17:ed:63:52:a1:5b:6e:d3:5c:a2:
72:05:fb:3a:39:71:0d:b4:2c:9d:15:23:1b:1f:8d:ac:89:dc:
c9:56:f2:19:c7:f3:2f:bb:d5:de:40:17:f1:52:ea:e8:93:ff:
56:43:f5:1d:cb:c0:51:52:25:d7:b0:81:a9:0e:4d:92:24:e7:
10:81:c7:31:26:ac:cb:66:c1:3f:f6:5f:69:7b:74:87:0d:b0:
8c:27:d4:24:29:59:e9:5b:a2:cb:0c:c0:f5:9b:1d:42:38:6b:
e3:c3:43:1e:ba:df:b1:51:0a:b7:33:55:26:39:01:2f:9f:c7:
88:ac:2f:4a:89:f3:69:de:72:43:48:49:08:59:36:86:84:09:
db:6a:82:84:3e:71:6a:9d:f9:bd:d8:b5:1e:7c:2c:29:e1:27:
45:4c:47:5b:88:b8:e6:fa:9d:9b:ff:d4:e9:8d:2d:5e:64:7f:
27:87:b2:8c:d8:7e:f5:52:3c:c4:d8:30:03:24:d7:ac:f8:53:
91:80:98:42:24:5a:6b:cb:34:48:57:e0:82:ac:96:d9:55:6c:
c2:c3:8c:19:7c:56:39:0a:a8:f1:b8:77:64:70:83:a8:04:c8:
3a:5d:0b:00:4c:e5:ba:f1:40:e5:57:cd:d9:67:48:21:e9:9c:
d3:f2:b8:01:b8:d1:c0:d1:3a:44:c0:97:db:e6:bc:8f:2e:33:
d5:e2:38:3d:d7:7b:50:13:01:36:28:61:cc:28:98:3c:f8:21:
5d:8c:fe:f5:d0:ab:e0:60:ec:36:22:8d:0b:71:30:1b:3d:56:
ae:96:e9:d2:89:c2:43:8b:ef:25:b7:d6:0d:82:e6:5a:c6:91:
8a:ad:8c:28:2a:2b:5c:4e:a1:de:cb:7d:cb:29:11:a2:66:c8:
a1:33:35:75:16:fe:28:0b:78:31:0a:1f:fa:d0:a8:f4:f1:69:
c7:97:1e:5d:fb:53:08:b5
-----BEGIN CERTIFICATE-----
MIIFiTCCA3GgAwIBAgIBAzANBgkqhkiG9w0BAQsFADBLMQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCQ0ExDDAKBgNVBAcMA1NWTDEhMB8GA1UECgwYSW50ZXJuZXQgV2lk
Z2l0cyBQdHkgTHRkMCAXDTE5MTEwNDIxNDMwMFoYDzIyOTMwODE4MjE0MzAwWjBn
MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCURVTU1ZQ0lUWTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtmb28u
YmFyLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOw/JC2ROr3D
/BVyQrP7KOYEo74mIObqMKiqSHg2DguZKTtL+fHVv70ME3zqUgb0vDSeK8C0giyH
+i/izXzXueGPBHFthXeuGEDksTpKa+Uzvz5l28+UZIcaIEbANzqfkz/UT6zE5OAo
tg8oUyrPuf5Q8u9H3H62YMJHhbjLykhb+p+KlzAB9LNRD2jhYKsvoK388BBPYOGS
276DBFxAh85RPpqe1hwbGcuMwmxXdG97r5Q9U60XpZlpfEH1PnpbSMd4/9c7qB/3
MOeDJnjiy6KPWJJhzcrpuNGAwEBY6djTQmSCj+QMubE2259lPz9bJFkxs2AM+kFa
G7id7Jk3kPq15z/LfOD57eonzhUkx3c7RUUtGY4uf2UOhd9mUGkkLKRqB+U/6yiE
U5RNX5yoZaZQTMA1BkBqpWKxk2DlHIUoNJspgW/iT80VMLkZ10u7MAxLLWT+O90O
pCUsSlze13QfXpN7HOjI+nIfSuuNP5jkVZi44IopkjOvdWsFhAXTDCwHeLwOsm2n
ADXEUx975roHcqgkwQqnxEbm8m86eSMAC7jlH+Di7sYTo1fZhhqV96ME8UbVXyHS
qtIw+/bL4NokxsMwL9IfIf68D5msrJtlm+SDmgC4L0D8O0LTeui3Utf0Zyql9+t4
8QpWi1YS1UjYSHCruGlaIdNxsFmdF7RLAgMBAAGjWjBYMB0GA1UdDgQWBBTAgtr6
aUYwrv9vzbuTSZSm0OIX6zAfBgNVHSMEGDAWgBRapdqxmdTlDuYelOr//GLi7Qnx
BjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQsFAAOCAgEANv3P
7PUgS1LcLjjzkrHktqEGhqotwOb1CliXqeO+Ewlhee3UQYMmre4LQ4PR3Rka6Huy
H/7UwVd9bWvUQup9zTSMpB9bO/reuy+uVrYY5VOpo5lYrTa+GVRhDVK2p1P8YOX/
9X+CP8FJBs2yryXu3r3g5V6tC9wusex6Um+d4LmEGNtJU+7fk+6LnZuOOyqChn9F
yN3RsEAX7WNSoVtu01yicgX7OjlxDbQsnRUjGx+NrIncyVbyGcfzL7vV3kAX8VLq
6JP/VkP1HcvAUVIl17CBqQ5NkiTnEIHHMSasy2bBP/ZfaXt0hw2wjCfUJClZ6Vui
ywzA9ZsdQjhr48NDHrrfsVEKtzNVJjkBL5/HiKwvSonzad5yQ0hJCFk2hoQJ22qC
hD5xap35vdi1HnwsKeEnRUxHW4i45vqdm//U6Y0tXmR/J4eyjNh+9VI8xNgwAyTX
rPhTkYCYQiRaa8s0SFfggqyW2VVswsOMGXxWOQqo8bh3ZHCDqATIOl0LAEzluvFA
5VfN2WdIIemc0/K4AbjRwNE6RMCX2+a8jy4z1eI4Pdd7UBMBNihhzCiYPPghXYz+
9dCr4GDsNiKNC3EwGz1Wrpbp0onCQ4vvJbfWDYLmWsaRiq2MKCorXE6h3st9yykR
ombIoTM1dRb+KAt4MQof+tCo9PFpx5ceXftTCLU=
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFlzCCA3+gAwIBAgIUdkt73feqv3fH1K1fBBp2ryU4TUMwDQYJKoZIhvcNAQEL
BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5l
dCBXaWRnaXRzIFB0eSBMdGQxGzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTAg
Fw0xOTExMTUxOTE1MTFaGA8yMjI1MDMyMDE5MTUxMVowWjELMAkGA1UEBhMCVVMx
CzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQx
GzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAMLHM55ChPP7dk+1uWz2DSkLE/xkoBaagJkjXuBgpgtdcxfp
hG9qTQ/vPAnqRFNSqQPU/A0dbKlnlK2ibrSb1LD4CPiXMqMAVojbEBRiNZK2+E1p
6FDaUO/CiurX8QPsVHUp2Zol38FoGdHL3tHSEf2xfJzs1Ka4g54FASOn+wJSAdAG
Ai+TUT137NqmeIVMIhg8x2vtKJpIH016mBqPENpccb3wsk9kNLj9TonKP16Nkngm
YKdLBnhB5Coz9gFqnTFEXp54ESOKttNtAAdFfhBqJhYMAdoFxSsuDdpr23Nyfuzf
uT5QnIffD0JCxH6bGYpMgJMVLiWJSuZ6wohFl04lwQTj3UXC8GU9o8YGC1UnvJoZ
rTgC8bM+yNJnEsrU90dPMLAi6qN5pl0y18/jtyaP5YXjv2TCGAjmB3dUyFa4nCg+
7w9tAi4pC3cBusN7e4cOseOM/23qKbcudHWAQ46VkTMs36DQyzxZutgZUI9lesol
o3eCR00v4N3Uf0yXff866EaDg3NmcZzhn1stJMHJMkhPOQZZmD8dd3Pi4DuQZMa/
74vMcjLxXo2xKTQklBUDCAFVEIR0y0oHwYUCk+AuS0PAXbGred0KOs6Ey8c68JYZ
OfgD/jjY/emYzyNeGGKUkMtNA9xUqWNEnqmIQgpMndzy1c5UlnGpoOt/cfztAgMB
AAGjUzBRMB0GA1UdDgQWBBS0GQgc/BAjxTCGIrzLsV+t6npd8TAfBgNVHSMEGDAW
gBS0GQgc/BAjxTCGIrzLsV+t6npd8TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4ICAQARMgDs180PFUSjXrRQ6hfpmYaauoq2atSYfzECfo92iemE1KiK
qUAVX/fjRy69/r6BKAo/j8F7BJVRqKhfiZm+EUIGWCtNRpVCx6WCtfJ1G8rEEH+U
E4kNpPC1OyVAhFMYKFJVXkyzpxjggLeY0bGs7BrX4wSid7vj6HM/pzfOShvB6qv0
VfpAGwTKnqw64kpy+9QPwS0sDH17oJAteJ3WeRopsqCjK9eXljmGBKVZjv2m9/TT
7Jd6VCBm/x1yxPeuJfPTxkfGR3UEcKPgXG84N0nfbTLspQcBf2QqQtW4yL/PyRC/
8sFAPanSkNc3u1ERQub0oUtd+jQalvxXqW1N0GAJHLvtXa5Etrz3WMfOVdqthEKK
CjGXdt4JoO+gvCGZH9jKa6HTgy+0QZrbOxBsJpbxSjXrJOeeJ2OgGZg8qBe5LqUD
Z3o45x6j3RiQrK24luZE/6A25VUvUke4Hr9oTBQFgMlIPuTeRw6XGkNzScaPrXEU
MnijDX8n7OcME+lCVCpgSd1SZzkTn4JYqlx8U33j1hRD1m5quO9+GOLQpWvZC5A5
FsikGXULKuIxVCJMuCXeWdY1aDAJ/6cwz77eDzNkySUfDEhxjQGhCmNlNDHN3dCM
NtSqXJSDIwqikj7izot3evkoYa9j6w3qkNyg9fyGbdNHXp135RP5HIhqjA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
HASpvDH+panUrtqSvjDZMuDvkA6qft8=
-----END PRIVATE KEY-----