snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-15 20:40:15 +05:00
commit e241b33f26
1365 changed files with 339570 additions and 0 deletions

View File

@@ -0,0 +1,690 @@
/*
*
* 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 provides gRPC transport credentials that allow easy
// configuration of advanced TLS features. The APIs here give the user more
// customizable control to fit their security landscape, thus the "advanced"
// moniker. This package provides both interfaces and generally useful
// implementations of those interfaces, for example periodic credential
// reloading, support for certificate revocation lists, and customizable
// certificate verification behaviors. If the provided implementations do not
// fit a given use case, a custom implementation of the interface can be
// injected.
package advancedtls
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net"
"reflect"
"time"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/tls/certprovider"
credinternal "google.golang.org/grpc/internal/credentials"
)
// CertificateChains represents a slice of certificate chains, each consisting
// of a sequence of certificates. Each chain represents a path from a leaf
// certificate up to a root certificate in the certificate hierarchy.
type CertificateChains [][]*x509.Certificate
// HandshakeVerificationInfo contains information about a handshake needed for
// verification for use when implementing the `PostHandshakeVerificationFunc`
// The fields in this struct are read-only.
type HandshakeVerificationInfo struct {
// The target server name that the client connects to when establishing the
// connection. This field is only meaningful for client side. On server side,
// this field would be an empty string.
ServerName string
// The raw certificates sent from peer.
RawCerts [][]byte
// The verification chain obtained by checking peer RawCerts against the
// trust certificate bundle(s), if applicable.
VerifiedChains CertificateChains
// The leaf certificate sent from peer, if choosing to verify the peer
// certificate(s) and that verification passed. This field would be nil if
// either user chose not to verify or the verification failed.
Leaf *x509.Certificate
// ConnectionState is the TLS connection state for the connection.
ConnectionState tls.ConnectionState
}
// PostHandshakeVerificationResults contains the information about results of
// PostHandshakeVerificationFunc.
// PostHandshakeVerificationResults is an empty struct for now. It may be extended in the
// future to include more information.
type PostHandshakeVerificationResults struct{}
// PostHandshakeVerificationFunc is the function defined by users to perform
// custom verification checks after chain building and regular handshake
// verification has been completed.
// PostHandshakeVerificationFunc should return (nil, error) if the authorization
// should fail, with the error containing information on why it failed.
type PostHandshakeVerificationFunc func(params *HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error)
// ConnectionInfo contains the parameters available to users when
// implementing GetRootCertificates.
type ConnectionInfo struct {
// RawConn is the raw net.Conn representing a connection.
RawConn net.Conn
// RawCerts is the byte representation of the presented peer cert chain.
RawCerts [][]byte
}
// RootCertificates is the result of GetRootCertificates.
// If users want to reload the root trust certificate, it is required to return
// the proper TrustCerts in GetRootCAs.
type RootCertificates struct {
// TrustCerts is the pool of trusted certificates.
TrustCerts *x509.CertPool
}
// RootCertificateOptions contains options to obtain root trust certificates
// for both the client and the server.
// At most one field should be set. If none of them are set, we use the system
// default trust certificates. Setting more than one field will result in
// undefined behavior.
type RootCertificateOptions struct {
// If RootCertificates is set, it will be used every time when verifying
// the peer certificates, without performing root certificate reloading.
RootCertificates *x509.CertPool
// If GetRootCertificates is set, it will be invoked to obtain root certs for
// every new connection.
GetRootCertificates func(params *ConnectionInfo) (*RootCertificates, error)
// If RootProvider is set, we will use the root certs from the Provider's
// KeyMaterial() call in the new connections. The Provider must have initial
// credentials if specified. Otherwise, KeyMaterial() will block forever.
RootProvider certprovider.Provider
}
// nonNilFieldCount returns the number of set fields in RootCertificateOptions.
func (o RootCertificateOptions) nonNilFieldCount() int {
cnt := 0
rv := reflect.ValueOf(o)
for i := 0; i < rv.NumField(); i++ {
if !rv.Field(i).IsNil() {
cnt++
}
}
return cnt
}
// IdentityCertificateOptions contains options to obtain identity certificates
// for both the client and the server.
// At most one field should be set. Setting more than one field will result in undefined behavior.
type IdentityCertificateOptions struct {
// If Certificates is set, it will be used every time when needed to present
// identity certificates, without performing identity certificate reloading.
Certificates []tls.Certificate
// If GetIdentityCertificatesForClient is set, it will be invoked to obtain
// identity certs for every new connection.
// This field is only relevant when set on the client side.
GetIdentityCertificatesForClient func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
// If GetIdentityCertificatesForServer is set, it will be invoked to obtain
// identity certs for every new connection.
// This field is only relevant when set on the server side.
GetIdentityCertificatesForServer func(*tls.ClientHelloInfo) ([]*tls.Certificate, error)
// If IdentityProvider is set, we will use the identity certs from the
// Provider's KeyMaterial() call in the new connections. The Provider must
// have initial credentials if specified. Otherwise, KeyMaterial() will block
// forever.
IdentityProvider certprovider.Provider
}
// nonNilFieldCount returns the number of set fields in IdentityCertificateOptions.
func (o IdentityCertificateOptions) nonNilFieldCount() int {
cnt := 0
rv := reflect.ValueOf(o)
for i := 0; i < rv.NumField(); i++ {
if !rv.Field(i).IsNil() {
cnt++
}
}
return cnt
}
// VerificationType is the enum type that represents different levels of
// verification users could set, both on client side and on server side.
type VerificationType int
const (
// CertAndHostVerification indicates doing both certificate signature check
// and hostname check.
CertAndHostVerification VerificationType = iota
// CertVerification indicates doing certificate signature check only. Setting
// this field without proper custom verification check would leave the
// application susceptible to the MITM attack.
CertVerification
// SkipVerification indicates skipping both certificate signature check and
// hostname check. If setting this field, proper custom verification needs to
// be implemented in order to complete the authentication. Setting this field
// with a nil custom verification would raise an error.
SkipVerification
)
// Options contains the fields a user can configure when setting up TLS clients
// and servers
type Options struct {
// IdentityOptions is OPTIONAL on client side. This field only needs to be
// set if mutual authentication is required on server side.
// IdentityOptions is REQUIRED on server side.
IdentityOptions IdentityCertificateOptions
// AdditionalPeerVerification is a custom verification check after certificate signature
// check.
// If this is set, we will perform this customized check after doing the
// normal check(s) indicated by setting VerificationType.
AdditionalPeerVerification PostHandshakeVerificationFunc
// RootOptions is OPTIONAL on server side. This field only needs to be set if
// mutual authentication is required(RequireClientCert is true).
RootOptions RootCertificateOptions
// If the server requires the client to send certificates. This value is only
// relevant when configuring options for the server. Is not used for
// client-side configuration.
RequireClientCert bool
// VerificationType defines what type of peer verification is done. See
// the `VerificationType` enum for the different options.
// Default: CertAndHostVerification
VerificationType VerificationType
// RevocationOptions is the configurations for certificate revocation checks.
// It could be nil if such checks are not needed.
RevocationOptions *RevocationOptions
// MinTLSVersion contains the minimum TLS version that is acceptable.
// The value should be set using tls.VersionTLSxx from https://pkg.go.dev/crypto/tls
// By default, TLS 1.2 is currently used as the minimum when acting as a
// client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum
// supported by this package, both as a client and as a server. This
// default may be changed over time affecting backwards compatibility.
MinTLSVersion uint16
// MaxTLSVersion contains the maximum TLS version that is acceptable.
// The value should be set using tls.VersionTLSxx from https://pkg.go.dev/crypto/tls
// By default, the maximum version supported by this package is used,
// which is currently TLS 1.3. This default may be changed over time
// affecting backwards compatibility.
MaxTLSVersion uint16
// CipherSuites is an unordered list of supported TLS 1.0–1.2
// ciphersuites. TLS 1.3 ciphersuites are not configurable. If nil, a
// safe default list is used.
CipherSuites []uint16
// CurvePreferences contains the elliptic curves that will be used during the
// key exchange, in preference order. If empty, the default will be used.
CurvePreferences []tls.CurveID
// serverNameOverride is for testing only and only relevant on the client
// side. If set to a non-empty string, it will override the virtual host
// name of authority (e.g. :authority header field) in requests and the
// target hostname used during server cert verification.
serverNameOverride string
// SkipServerAuthEKU is an UNSAFE option that, if set to true, skips checking
// for the Server Auth Extended Key Usage (EKU) extension in peer
// certificates during server authentication.
//
// Doing so goes against the RFC 5280 specification (Section 4.2.1.12),
// which mandates that the certificate key usage should be compatible with
// the purpose of the key.
// See: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.12
//
// Behavior:
// - Client-side: If set to true, the client will verify the server's
// certificate using x509.ExtKeyUsageAny instead of requiring
// x509.ExtKeyUsageServerAuth. This is useful when the server's certificate
// does not possess the Server Auth EKU.
// - Server-side: No-op.
SkipServerAuthEKU bool
}
func (o *Options) clientConfig() (*tls.Config, error) {
if o.VerificationType == SkipVerification && o.AdditionalPeerVerification == nil {
return nil, fmt.Errorf("client needs to provide custom verification mechanism if choose to skip default verification")
}
// If the MinTLSVersion isn't set, default to 1.2
if o.MinTLSVersion == 0 {
o.MinTLSVersion = tls.VersionTLS12
}
// If the MaxTLSVersion isn't set, default to 1.3
if o.MaxTLSVersion == 0 {
o.MaxTLSVersion = tls.VersionTLS13
}
// Make sure users didn't specify more than one fields in
// RootCertificateOptions and IdentityCertificateOptions.
if num := o.RootOptions.nonNilFieldCount(); num > 1 {
return nil, fmt.Errorf("at most one field in RootCertificateOptions could be specified")
}
if num := o.IdentityOptions.nonNilFieldCount(); num > 1 {
return nil, fmt.Errorf("at most one field in IdentityCertificateOptions could be specified")
}
if o.IdentityOptions.GetIdentityCertificatesForServer != nil {
return nil, fmt.Errorf("GetIdentityCertificatesForServer cannot be specified on the client side")
}
if o.MinTLSVersion > o.MaxTLSVersion {
return nil, fmt.Errorf("the minimum TLS version is larger than the maximum TLS version")
}
config := &tls.Config{
ServerName: o.serverNameOverride,
// We have to set InsecureSkipVerify to true to skip the default checks and
// use the verification function we built from buildVerifyFunc.
InsecureSkipVerify: true,
MinVersion: o.MinTLSVersion,
MaxVersion: o.MaxTLSVersion,
CipherSuites: o.CipherSuites,
CurvePreferences: o.CurvePreferences,
}
// Propagate root-certificate-related fields in tls.Config.
switch {
case o.RootOptions.RootCertificates != nil:
config.RootCAs = o.RootOptions.RootCertificates
case o.RootOptions.GetRootCertificates != nil:
// In cases when users provide GetRootCertificates callback, since this
// callback is not contained in tls.Config, we have nothing to set here.
// We will invoke the callback in ClientHandshake.
case o.RootOptions.RootProvider != nil:
o.RootOptions.GetRootCertificates = func(*ConnectionInfo) (*RootCertificates, error) {
km, err := o.RootOptions.RootProvider.KeyMaterial(context.Background())
if err != nil {
return nil, err
}
return &RootCertificates{TrustCerts: km.Roots}, nil
}
default:
// No root certificate options specified by user. Use the certificates
// stored in system default path as the last resort.
if o.VerificationType != SkipVerification {
systemRootCAs, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
config.RootCAs = systemRootCAs
}
}
// Propagate identity-certificate-related fields in tls.Config.
switch {
case o.IdentityOptions.Certificates != nil:
config.Certificates = o.IdentityOptions.Certificates
case o.IdentityOptions.GetIdentityCertificatesForClient != nil:
config.GetClientCertificate = o.IdentityOptions.GetIdentityCertificatesForClient
case o.IdentityOptions.IdentityProvider != nil:
config.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
km, err := o.IdentityOptions.IdentityProvider.KeyMaterial(context.Background())
if err != nil {
return nil, err
}
if len(km.Certs) != 1 {
return nil, fmt.Errorf("there should always be only one identity cert chain on the client side in IdentityProvider")
}
return &km.Certs[0], nil
}
default:
// It's fine for users to not specify identity certificate options here.
}
return config, nil
}
func (o *Options) serverConfig() (*tls.Config, error) {
if o.RequireClientCert && o.VerificationType == SkipVerification && o.AdditionalPeerVerification == nil {
return nil, fmt.Errorf("server needs to provide custom verification mechanism if choose to skip default verification, but require client certificate(s)")
}
// If the MinTLSVersion isn't set, default to 1.2
if o.MinTLSVersion == 0 {
o.MinTLSVersion = tls.VersionTLS12
}
// If the MaxTLSVersion isn't set, default to 1.3
if o.MaxTLSVersion == 0 {
o.MaxTLSVersion = tls.VersionTLS13
}
// Make sure users didn't specify more than one fields in
// RootCertificateOptions and IdentityCertificateOptions.
if num := o.RootOptions.nonNilFieldCount(); num > 1 {
return nil, fmt.Errorf("at most one field in RootCertificateOptions could be specified")
}
if num := o.IdentityOptions.nonNilFieldCount(); num > 1 {
return nil, fmt.Errorf("at most one field in IdentityCertificateOptions could be specified")
}
if o.IdentityOptions.GetIdentityCertificatesForClient != nil {
return nil, fmt.Errorf("GetIdentityCertificatesForClient cannot be specified on the server side")
}
if o.MinTLSVersion > o.MaxTLSVersion {
return nil, fmt.Errorf("the minimum TLS version is larger than the maximum TLS version")
}
clientAuth := tls.NoClientCert
if o.RequireClientCert {
// We have to set clientAuth to RequireAnyClientCert to force underlying
// TLS package to use the verification function we built from
// buildVerifyFunc.
clientAuth = tls.RequireAnyClientCert
}
config := &tls.Config{
ClientAuth: clientAuth,
MinVersion: o.MinTLSVersion,
MaxVersion: o.MaxTLSVersion,
CipherSuites: o.CipherSuites,
CurvePreferences: o.CurvePreferences,
}
// Propagate root-certificate-related fields in tls.Config.
switch {
case o.RootOptions.RootCertificates != nil:
config.ClientCAs = o.RootOptions.RootCertificates
case o.RootOptions.GetRootCertificates != nil:
// In cases when users provide GetRootCertificates callback, since this
// callback is not contained in tls.Config, we have nothing to set here.
// We will invoke the callback in ServerHandshake.
case o.RootOptions.RootProvider != nil:
o.RootOptions.GetRootCertificates = func(*ConnectionInfo) (*RootCertificates, error) {
km, err := o.RootOptions.RootProvider.KeyMaterial(context.Background())
if err != nil {
return nil, err
}
return &RootCertificates{TrustCerts: km.Roots}, nil
}
default:
// No root certificate options specified by user. Use the certificates
// stored in system default path as the last resort.
if o.VerificationType != SkipVerification && o.RequireClientCert {
systemRootCAs, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
config.ClientCAs = systemRootCAs
}
}
// Propagate identity-certificate-related fields in tls.Config.
switch {
case o.IdentityOptions.Certificates != nil:
config.Certificates = o.IdentityOptions.Certificates
case o.IdentityOptions.GetIdentityCertificatesForServer != nil:
config.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
return buildGetCertificates(clientHello, o)
}
case o.IdentityOptions.IdentityProvider != nil:
o.IdentityOptions.GetIdentityCertificatesForServer = func(*tls.ClientHelloInfo) ([]*tls.Certificate, error) {
km, err := o.IdentityOptions.IdentityProvider.KeyMaterial(context.Background())
if err != nil {
return nil, err
}
var certChains []*tls.Certificate
for i := 0; i < len(km.Certs); i++ {
certChains = append(certChains, &km.Certs[i])
}
return certChains, nil
}
config.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
return buildGetCertificates(clientHello, o)
}
default:
return nil, fmt.Errorf("needs to specify at least one field in IdentityCertificateOptions")
}
return config, nil
}
// advancedTLSCreds is the credentials required for authenticating a connection
// using TLS.
type advancedTLSCreds struct {
config *tls.Config
verifyFunc PostHandshakeVerificationFunc
getRootCertificates func(params *ConnectionInfo) (*RootCertificates, error)
isClient bool
revocationOptions *RevocationOptions
verificationType VerificationType
skipServerAuthEKU 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 := credinternal.CloneTLSConfig(c.config)
// We return the full authority name to users without stripping the trailing
// port.
cfg.ServerName = authority
peerVerifiedChains := CertificateChains{}
cfg.VerifyPeerCertificate, cfg.VerifyConnection = buildVerifyFunc(c, cfg.ServerName, rawConn, &peerVerifiedChains)
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()
}
info := credentials.TLSInfo{
State: conn.ConnectionState(),
CommonAuthInfo: credentials.CommonAuthInfo{
SecurityLevel: credentials.PrivacyAndIntegrity,
},
}
info.SPIFFEID = credinternal.SPIFFEIDFromState(conn.ConnectionState())
info.State.VerifiedChains = peerVerifiedChains
return credinternal.WrapSyscallConn(rawConn, conn), info, nil
}
func (c *advancedTLSCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
cfg := credinternal.CloneTLSConfig(c.config)
peerVerifiedChains := CertificateChains{}
cfg.VerifyPeerCertificate, cfg.VerifyConnection = buildVerifyFunc(c, "", rawConn, &peerVerifiedChains)
conn := tls.Server(rawConn, cfg)
if err := conn.Handshake(); err != nil {
conn.Close()
return nil, nil, err
}
info := credentials.TLSInfo{
State: conn.ConnectionState(),
CommonAuthInfo: credentials.CommonAuthInfo{
SecurityLevel: credentials.PrivacyAndIntegrity,
},
}
info.SPIFFEID = credinternal.SPIFFEIDFromState(conn.ConnectionState())
info.State.VerifiedChains = peerVerifiedChains
return credinternal.WrapSyscallConn(rawConn, conn), info, nil
}
func (c *advancedTLSCreds) Clone() credentials.TransportCredentials {
return &advancedTLSCreds{
config: credinternal.CloneTLSConfig(c.config),
verifyFunc: c.verifyFunc,
getRootCertificates: c.getRootCertificates,
isClient: c.isClient,
revocationOptions: c.revocationOptions,
verificationType: c.verificationType,
skipServerAuthEKU: c.skipServerAuthEKU,
}
}
func (c *advancedTLSCreds) OverrideServerName(serverNameOverride string) error {
c.config.ServerName = serverNameOverride
return nil
}
type verifyPeerCertificateFunc func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
type verifyConnectionFunc func(cs tls.ConnectionState) error
// The function buildVerifyFunc constructs the verification callbacks used when
// users want root cert reloading, certificate revocation checking, and custom
// verification checks.
// We build custom verification callbacks because the standard library tls module:
// 1. does not have native support for dynamic root cert reloading.
// 2. ignores basic certificate verification when InsecureSkipVerify is true.
//
// It returns two callbacks that work together during the TLS handshake:
// 1. VerifyPeerCertificate: performs root CA reloading, peer certificate chain
// verification, and revocation checking, storing the verified chains and
// leaf cert in shared closure variables.
// 2. VerifyConnection: invoked after certificate verification and key exchange
// complete, providing access to tls.ConnectionState and invoking the user's
// custom verification function (verifyFunc).
//
// peerVerifiedChains(output param): verified chain of certs from leaf to the
// trust cert that the peer trusts.
// 1. For server it is, client certs + Root ca that the server trusts
// 2. For client it is, server certs + Root ca that the client trusts
func buildVerifyFunc(c *advancedTLSCreds,
serverName string,
rawConn net.Conn,
peerVerifiedChains *CertificateChains) (verifyPeerCertificateFunc, verifyConnectionFunc) {
var (
chains CertificateChains
leafCert *x509.Certificate
rawCerts [][]byte
)
verifyPeerCertFunc := func(rc [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(rc) == 0 {
return fmt.Errorf("no peer certificates presented")
}
rawCerts = rc
chains = verifiedChains
rawCertList := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
return err
}
rawCertList[i] = cert
}
if c.verificationType == CertAndHostVerification || c.verificationType == CertVerification {
// perform possible trust credential reloading and certificate check
rootCAs := c.config.RootCAs
if !c.isClient {
rootCAs = c.config.ClientCAs
}
// Reload root CA certs.
if rootCAs == nil && c.getRootCertificates != nil {
results, err := c.getRootCertificates(&ConnectionInfo{
RawConn: rawConn,
RawCerts: rawCerts,
})
if err != nil {
return err
}
rootCAs = results.TrustCerts
}
// Verify peers' certificates against RootCAs and get verifiedChains.
keyUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
if c.isClient {
if c.skipServerAuthEKU {
keyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageAny}
} else {
keyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
}
}
opts := x509.VerifyOptions{
Roots: rootCAs,
CurrentTime: time.Now(),
Intermediates: x509.NewCertPool(),
KeyUsages: keyUsages,
}
for _, cert := range rawCertList[1:] {
opts.Intermediates.AddCert(cert)
}
// Perform default hostname check if specified.
if c.isClient && c.verificationType == CertAndHostVerification && serverName != "" {
parsedName, _, err := net.SplitHostPort(serverName)
if err != nil {
// If the serverName had no host port or if the serverName cannot be
// parsed, use it as-is.
parsedName = serverName
}
opts.DNSName = parsedName
}
var err error
chains, err = rawCertList[0].Verify(opts)
if err != nil {
return err
}
leafCert = rawCertList[0]
}
// Perform certificate revocation check if specified.
if c.revocationOptions != nil {
verifiedChains := chains
if verifiedChains == nil {
verifiedChains = CertificateChains{rawCertList}
}
if err := checkChainRevocation(verifiedChains, *c.revocationOptions); err != nil {
return err
}
}
*peerVerifiedChains = chains
return nil
}
if c.verifyFunc == nil {
return verifyPeerCertFunc, nil
}
return verifyPeerCertFunc, func(cs tls.ConnectionState) error {
_, err := c.verifyFunc(&HandshakeVerificationInfo{
ServerName: serverName,
RawCerts: rawCerts,
VerifiedChains: chains,
Leaf: leafCert,
ConnectionState: cs,
})
return err
}
}
// NewClientCreds uses ClientOptions to construct a TransportCredentials based
// on TLS.
func NewClientCreds(o *Options) (credentials.TransportCredentials, error) {
conf, err := o.clientConfig()
if err != nil {
return nil, err
}
tc := &advancedTLSCreds{
config: conf,
isClient: true,
getRootCertificates: o.RootOptions.GetRootCertificates,
verifyFunc: o.AdditionalPeerVerification,
revocationOptions: o.RevocationOptions,
verificationType: o.VerificationType,
skipServerAuthEKU: o.SkipServerAuthEKU,
}
tc.config.NextProtos = credinternal.AppendH2ToNextProtos(tc.config.NextProtos)
return tc, nil
}
// NewServerCreds uses ServerOptions to construct a TransportCredentials based
// on TLS.
func NewServerCreds(o *Options) (credentials.TransportCredentials, error) {
conf, err := o.serverConfig()
if err != nil {
return nil, err
}
tc := &advancedTLSCreds{
config: conf,
isClient: false,
getRootCertificates: o.RootOptions.GetRootCertificates,
verifyFunc: o.AdditionalPeerVerification,
revocationOptions: o.RevocationOptions,
verificationType: o.VerificationType,
skipServerAuthEKU: o.SkipServerAuthEKU,
}
tc.config.NextProtos = credinternal.AppendH2ToNextProtos(tc.config.NextProtos)
return tc, nil
}

View File

@@ -0,0 +1,942 @@
/*
*
* Copyright 2020 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"
"fmt"
"net"
"os"
"sync"
"testing"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/tls/certprovider"
"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"google.golang.org/grpc/security/advancedtls/internal/testutils"
"google.golang.org/grpc/security/advancedtls/testdata"
)
const (
// Default timeout for normal connections.
defaultTestTimeout = 5 * time.Second
// Intervals that set to monitor the credential updates.
credRefreshingInterval = 200 * time.Millisecond
// Time we wait for the credential updates to be picked up.
sleepInterval = 400 * time.Millisecond
)
// stageInfo contains a stage number indicating the current phase of each
// integration test, and a mutex.
// Based on the stage number of current test, we will use different
// certificates and custom verification functions to check if our tests behave
// as expected.
type stageInfo struct {
mutex sync.Mutex
stage int
}
func (s *stageInfo) increase() {
s.mutex.Lock()
defer s.mutex.Unlock()
s.stage = s.stage + 1
}
func (s *stageInfo) read() int {
s.mutex.Lock()
defer s.mutex.Unlock()
return s.stage
}
func (s *stageInfo) reset() {
s.mutex.Lock()
defer s.mutex.Unlock()
s.stage = 0
}
type greeterServer struct {
pb.UnimplementedGreeterServer
}
// sayHello is a simple implementation of the pb.GreeterServer SayHello method.
func (greeterServer) SayHello(_ context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
// TODO(ZhenLian): remove shouldFail to the function signature to provider
// tests.
func callAndVerify(ctx context.Context, msg string, client pb.GreeterClient, shouldFail bool) error {
_, err := client.SayHello(ctx, &pb.HelloRequest{Name: msg})
if want, got := shouldFail == true, err != nil; got != want {
return fmt.Errorf("want and got mismatch, want shouldFail=%v, got fail=%v, rpc error: %v", want, got, err)
}
return nil
}
// TODO(ZhenLian): remove shouldFail and add ...DialOption to the function
// signature to provider cleaner tests.
func callAndVerifyWithClientConn(ctx context.Context, address string, msg string, creds credentials.TransportCredentials, shouldFail bool) (*grpc.ClientConn, pb.GreeterClient, error) {
// Disable service config lookups as it results in a DNS lookup which can
// flake in CI.
conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(creds), grpc.WithDisableServiceConfig())
if err != nil {
return nil, nil, fmt.Errorf("client failed to connect to %s. Error: %v", address, err)
}
greetClient := pb.NewGreeterClient(conn)
err = callAndVerify(ctx, msg, greetClient, shouldFail)
if err != nil {
return nil, nil, err
}
return conn, greetClient, nil
}
// The advanced TLS features are tested in different stages.
// At stage 0, we establish a good connection between client and server.
// At stage 1, we change one factor(it could be we change the server's
// certificate, or custom verification function, etc), and test if the
// following connections would be dropped.
// At stage 2, we re-establish the connection by changing the counterpart of
// the factor we modified in stage 1.
// (could be change the client's trust certificate, or change custom
// verification function, etc)
func (s) TestEnd2End(t *testing.T) {
cs := &testutils.CertStore{}
if err := cs.LoadCerts(); err != nil {
t.Fatalf("cs.LoadCerts() failed, err: %v", err)
}
stage := &stageInfo{}
for _, test := range []struct {
desc string
clientCert []tls.Certificate
clientGetCert func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
clientRoot *x509.CertPool
clientGetRoot func(params *ConnectionInfo) (*RootCertificates, error)
clientVerifyFunc PostHandshakeVerificationFunc
clientVerificationType VerificationType
serverCert []tls.Certificate
serverGetCert func(*tls.ClientHelloInfo) ([]*tls.Certificate, error)
serverRoot *x509.CertPool
serverGetRoot func(params *ConnectionInfo) (*RootCertificates, error)
serverVerifyFunc PostHandshakeVerificationFunc
serverVerificationType VerificationType
}{
// Test Scenarios:
// At initialization(stage = 0), client will be initialized with cert
// ClientCert1 and ClientTrust1, server with ServerCert1 and ServerTrust1.
// The mutual authentication works at the beginning, since ClientCert1 is
// trusted by ServerTrust1, and ServerCert1 by ClientTrust1.
// At stage 1, client changes ClientCert1 to ClientCert2. Since ClientCert2
// is not trusted by ServerTrust1, following rpc calls are expected to
// fail, while the previous rpc calls are still good because those are
// already authenticated.
// At stage 2, the server changes ServerTrust1 to ServerTrust2, and we
// should see it again accepts the connection, since ClientCert2 is trusted
// by ServerTrust2.
{
desc: "test the reloading feature for client identity callback and server trust callback",
clientGetCert: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
switch stage.read() {
case 0:
return &cs.ClientCert1, nil
default:
return &cs.ClientCert2, nil
}
},
clientRoot: cs.ClientTrust1,
clientVerifyFunc: func(*HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
return &PostHandshakeVerificationResults{}, nil
},
clientVerificationType: CertVerification,
serverCert: []tls.Certificate{cs.ServerCert1},
serverGetRoot: func(*ConnectionInfo) (*RootCertificates, error) {
switch stage.read() {
case 0, 1:
return &RootCertificates{TrustCerts: cs.ServerTrust1}, nil
default:
return &RootCertificates{TrustCerts: cs.ServerTrust2}, nil
}
},
serverVerifyFunc: func(*HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
return &PostHandshakeVerificationResults{}, nil
},
serverVerificationType: CertVerification,
},
// Test Scenarios:
// At initialization(stage = 0), client will be initialized with cert
// ClientCert1 and ClientTrust1, server with ServerCert1 and ServerTrust1.
// The mutual authentication works at the beginning, since ClientCert1 is
// trusted by ServerTrust1, and ServerCert1 by ClientTrust1.
// At stage 1, server changes ServerCert1 to ServerCert2. Since ServerCert2
// is not trusted by ClientTrust1, following rpc calls are expected to
// fail, while the previous rpc calls are still good because those are
// already authenticated.
// At stage 2, the client changes ClientTrust1 to ClientTrust2, and we
// should see it again accepts the connection, since ServerCert2 is trusted
// by ClientTrust2.
{
desc: "test the reloading feature for server identity callback and client trust callback",
clientCert: []tls.Certificate{cs.ClientCert1},
clientGetRoot: func(*ConnectionInfo) (*RootCertificates, error) {
switch stage.read() {
case 0, 1:
return &RootCertificates{TrustCerts: cs.ClientTrust1}, nil
default:
return &RootCertificates{TrustCerts: cs.ClientTrust2}, nil
}
},
clientVerifyFunc: func(*HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
return &PostHandshakeVerificationResults{}, nil
},
clientVerificationType: CertVerification,
serverGetCert: func(*tls.ClientHelloInfo) ([]*tls.Certificate, error) {
switch stage.read() {
case 0:
return []*tls.Certificate{&cs.ServerCert1}, nil
default:
return []*tls.Certificate{&cs.ServerCert2}, nil
}
},
serverRoot: cs.ServerTrust1,
serverVerifyFunc: func(*HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
return &PostHandshakeVerificationResults{}, nil
},
serverVerificationType: CertVerification,
},
// Test Scenarios:
// At initialization(stage = 0), client will be initialized with cert
// ClientCert1 and ClientTrust1, server with ServerCert1 and ServerTrust1.
// The mutual authentication works at the beginning, since ClientCert1
// trusted by ServerTrust1, ServerCert1 by ClientTrust1, and also the
// custom verification check allows the CommonName on ServerCert1.
// At stage 1, server changes ServerCert1 to ServerCert2, and client
// changes ClientTrust1 to ClientTrust2. Although ServerCert2 is trusted by
// ClientTrust2, our authorization check only accepts ServerCert1, and
// hence the following calls should fail. Previous connections should
// not be affected.
// At stage 2, the client changes authorization check to only accept
// ServerCert2. Now we should see the connection becomes normal again.
{
desc: "test client custom verification",
clientCert: []tls.Certificate{cs.ClientCert1},
clientGetRoot: func(*ConnectionInfo) (*RootCertificates, error) {
switch stage.read() {
case 0:
return &RootCertificates{TrustCerts: cs.ClientTrust1}, nil
default:
return &RootCertificates{TrustCerts: cs.ClientTrust2}, nil
}
},
clientVerifyFunc: func(params *HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
if len(params.RawCerts) == 0 {
return nil, fmt.Errorf("no peer certs")
}
cert, err := x509.ParseCertificate(params.RawCerts[0])
if err != nil || cert == nil {
return nil, fmt.Errorf("failed to parse certificate: %v", err)
}
authzCheck := false
switch stage.read() {
case 0, 1:
// foo.bar.com is the common name on ServerCert1
if cert.Subject.CommonName == "foo.bar.com" {
authzCheck = true
}
default:
// foo.bar.server2.com is the common name on ServerCert2
if cert.Subject.CommonName == "foo.bar.server2.com" {
authzCheck = true
}
}
if authzCheck {
return &PostHandshakeVerificationResults{}, nil
}
return nil, fmt.Errorf("custom authz check fails")
},
clientVerificationType: CertVerification,
serverGetCert: func(*tls.ClientHelloInfo) ([]*tls.Certificate, error) {
switch stage.read() {
case 0:
return []*tls.Certificate{&cs.ServerCert1}, nil
default:
return []*tls.Certificate{&cs.ServerCert2}, nil
}
},
serverRoot: cs.ServerTrust1,
serverVerifyFunc: func(*HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
return &PostHandshakeVerificationResults{}, nil
},
serverVerificationType: CertVerification,
},
// Test Scenarios:
// At initialization(stage = 0), client will be initialized with cert
// ClientCert1 and ClientTrust1, server with ServerCert1 and ServerTrust1.
// The mutual authentication works at the beginning, since ClientCert1
// trusted by ServerTrust1, ServerCert1 by ClientTrust1, and also the
// custom verification check on server side allows all connections.
// At stage 1, server disallows the connections by setting custom
// verification check. The following calls should fail. Previous
// connections should not be affected.
// At stage 2, server allows all the connections again and the
// authentications should go back to normal.
{
desc: "TestServerCustomVerification",
clientCert: []tls.Certificate{cs.ClientCert1},
clientRoot: cs.ClientTrust1,
clientVerifyFunc: func(*HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
return &PostHandshakeVerificationResults{}, nil
},
clientVerificationType: CertVerification,
serverCert: []tls.Certificate{cs.ServerCert1},
serverRoot: cs.ServerTrust1,
serverVerifyFunc: func(*HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
switch stage.read() {
case 0, 2:
return &PostHandshakeVerificationResults{}, nil
case 1:
return nil, fmt.Errorf("custom authz check fails")
default:
return nil, fmt.Errorf("custom authz check fails")
}
},
serverVerificationType: CertVerification,
},
} {
test := test
t.Run(test.desc, func(t *testing.T) {
// Start a server using ServerOptions in another goroutine.
serverOptions := &Options{
IdentityOptions: IdentityCertificateOptions{
Certificates: test.serverCert,
GetIdentityCertificatesForServer: test.serverGetCert,
},
RootOptions: RootCertificateOptions{
RootCertificates: test.serverRoot,
GetRootCertificates: test.serverGetRoot,
},
RequireClientCert: true,
AdditionalPeerVerification: test.serverVerifyFunc,
VerificationType: test.serverVerificationType,
}
serverTLSCreds, err := NewServerCreds(serverOptions)
if err != nil {
t.Fatalf("failed to create server creds: %v", err)
}
s := grpc.NewServer(grpc.Creds(serverTLSCreds))
defer s.Stop()
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
defer lis.Close()
addr := fmt.Sprintf("localhost:%v", lis.Addr().(*net.TCPAddr).Port)
pb.RegisterGreeterServer(s, greeterServer{})
go s.Serve(lis)
clientOptions := &Options{
IdentityOptions: IdentityCertificateOptions{
Certificates: test.clientCert,
GetIdentityCertificatesForClient: test.clientGetCert,
},
AdditionalPeerVerification: test.clientVerifyFunc,
RootOptions: RootCertificateOptions{
RootCertificates: test.clientRoot,
GetRootCertificates: test.clientGetRoot,
},
VerificationType: test.clientVerificationType,
}
clientTLSCreds, err := NewClientCreds(clientOptions)
if err != nil {
t.Fatalf("clientTLSCreds failed to create: %v", err)
}
// ------------------------Scenario 1------------------------------------
// stage = 0, initial connection should succeed
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
conn, greetClient, err := callAndVerifyWithClientConn(ctx, addr, "rpc call 1", clientTLSCreds, false)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
// ----------------------------------------------------------------------
stage.increase()
// ------------------------Scenario 2------------------------------------
// stage = 1, previous connection should still succeed
err = callAndVerify(ctx, "rpc call 2", greetClient, false)
if err != nil {
t.Fatal(err)
}
// ------------------------Scenario 3------------------------------------
// stage = 1, new connection should fail
ctx2, cancel2 := context.WithTimeout(context.Background(), defaultTestTimeout)
conn2, _, err := callAndVerifyWithClientConn(ctx2, addr, "rpc call 3", clientTLSCreds, true)
if err != nil {
t.Fatal(err)
}
defer conn2.Close()
// Immediately cancel the context so the dialing won't drag the entire timeout still it stops.
cancel2()
// ----------------------------------------------------------------------
stage.increase()
// ------------------------Scenario 4------------------------------------
// stage = 2, new connection should succeed
conn3, _, err := callAndVerifyWithClientConn(ctx, addr, "rpc call 4", clientTLSCreds, false)
if err != nil {
t.Fatal(err)
}
defer conn3.Close()
// ----------------------------------------------------------------------
stage.reset()
})
}
}
type tmpCredsFiles struct {
clientCertTmp *os.File
clientKeyTmp *os.File
clientTrustTmp *os.File
serverCertTmp *os.File
serverKeyTmp *os.File
serverTrustTmp *os.File
}
// Create temp files that are used to hold credentials.
func createTmpFiles() (*tmpCredsFiles, error) {
tmpFiles := &tmpCredsFiles{}
var err error
tmpFiles.clientCertTmp, err = os.CreateTemp(os.TempDir(), "pre-")
if err != nil {
return nil, err
}
tmpFiles.clientKeyTmp, err = os.CreateTemp(os.TempDir(), "pre-")
if err != nil {
return nil, err
}
tmpFiles.clientTrustTmp, err = os.CreateTemp(os.TempDir(), "pre-")
if err != nil {
return nil, err
}
tmpFiles.serverCertTmp, err = os.CreateTemp(os.TempDir(), "pre-")
if err != nil {
return nil, err
}
tmpFiles.serverKeyTmp, err = os.CreateTemp(os.TempDir(), "pre-")
if err != nil {
return nil, err
}
tmpFiles.serverTrustTmp, err = os.CreateTemp(os.TempDir(), "pre-")
if err != nil {
return nil, err
}
return tmpFiles, nil
}
// Copy the credential contents to the temporary files.
func (tmpFiles *tmpCredsFiles) copyCredsToTmpFiles() error {
if err := copyFileContents(testdata.Path("client_cert_1.pem"), tmpFiles.clientCertTmp.Name()); err != nil {
return err
}
if err := copyFileContents(testdata.Path("client_key_1.pem"), tmpFiles.clientKeyTmp.Name()); err != nil {
return err
}
if err := copyFileContents(testdata.Path("client_trust_cert_1.pem"), tmpFiles.clientTrustTmp.Name()); err != nil {
return err
}
if err := copyFileContents(testdata.Path("server_cert_1.pem"), tmpFiles.serverCertTmp.Name()); err != nil {
return err
}
if err := copyFileContents(testdata.Path("server_key_1.pem"), tmpFiles.serverKeyTmp.Name()); err != nil {
return err
}
if err := copyFileContents(testdata.Path("server_trust_cert_1.pem"), tmpFiles.serverTrustTmp.Name()); err != nil {
return err
}
return nil
}
func (tmpFiles *tmpCredsFiles) removeFiles() {
os.Remove(tmpFiles.clientCertTmp.Name())
os.Remove(tmpFiles.clientKeyTmp.Name())
os.Remove(tmpFiles.clientTrustTmp.Name())
os.Remove(tmpFiles.serverCertTmp.Name())
os.Remove(tmpFiles.serverKeyTmp.Name())
os.Remove(tmpFiles.serverTrustTmp.Name())
}
func copyFileContents(sourceFile, destinationFile string) error {
input, err := os.ReadFile(sourceFile)
if err != nil {
return err
}
err = os.WriteFile(destinationFile, input, 0644)
if err != nil {
return err
}
return nil
}
// Create PEMFileProvider(s) watching the content changes of temporary
// files.
func createProviders(tmpFiles *tmpCredsFiles) (certprovider.Provider, certprovider.Provider, certprovider.Provider, certprovider.Provider, error) {
clientIdentityOptions := pemfile.Options{
CertFile: tmpFiles.clientCertTmp.Name(),
KeyFile: tmpFiles.clientKeyTmp.Name(),
RefreshDuration: credRefreshingInterval,
}
clientIdentityProvider, err := pemfile.NewProvider(clientIdentityOptions)
if err != nil {
return nil, nil, nil, nil, err
}
clientRootOptions := pemfile.Options{
RootFile: tmpFiles.clientTrustTmp.Name(),
RefreshDuration: credRefreshingInterval,
}
clientRootProvider, err := pemfile.NewProvider(clientRootOptions)
if err != nil {
return nil, nil, nil, nil, err
}
serverIdentityOptions := pemfile.Options{
CertFile: tmpFiles.serverCertTmp.Name(),
KeyFile: tmpFiles.serverKeyTmp.Name(),
RefreshDuration: credRefreshingInterval,
}
serverIdentityProvider, err := pemfile.NewProvider(serverIdentityOptions)
if err != nil {
return nil, nil, nil, nil, err
}
serverRootOptions := pemfile.Options{
RootFile: tmpFiles.serverTrustTmp.Name(),
RefreshDuration: credRefreshingInterval,
}
serverRootProvider, err := pemfile.NewProvider(serverRootOptions)
if err != nil {
return nil, nil, nil, nil, err
}
return clientIdentityProvider, clientRootProvider, serverIdentityProvider, serverRootProvider, nil
}
// In order to test advanced TLS provider features, we used temporary files to
// hold credential data, and copy the contents under testdata/ to these tmp
// files.
// Initially, we establish a good connection with providers watching contents
// from tmp files.
// Next, we change the identity certs that IdentityProvider is watching. Since
// the identity key is not changed, the IdentityProvider should ignore the
// update, and the connection should still be good.
// Then the identity key is changed. This time IdentityProvider should pick
// up the update, and the connection should fail, due to the trust certs on the
// other side is not changed.
// Finally, the trust certs that other-side's RootProvider is watching get
// changed. The connection should go back to normal again.
func (s) TestPEMFileProviderEnd2End(t *testing.T) {
tmpFiles, err := createTmpFiles()
if err != nil {
t.Fatalf("createTmpFiles() failed, error: %v", err)
}
defer tmpFiles.removeFiles()
for _, test := range []struct {
desc string
certUpdateFunc func()
keyUpdateFunc func()
trustCertUpdateFunc func()
}{
{
desc: "test the reloading feature for clientIdentityProvider and serverTrustProvider",
certUpdateFunc: func() {
err = copyFileContents(testdata.Path("client_cert_2.pem"), tmpFiles.clientCertTmp.Name())
if err != nil {
t.Fatalf("copyFileContents(%s, %s) failed: %v", testdata.Path("client_cert_2.pem"), tmpFiles.clientCertTmp.Name(), err)
}
},
keyUpdateFunc: func() {
err = copyFileContents(testdata.Path("client_key_2.pem"), tmpFiles.clientKeyTmp.Name())
if err != nil {
t.Fatalf("copyFileContents(%s, %s) failed: %v", testdata.Path("client_key_2.pem"), tmpFiles.clientKeyTmp.Name(), err)
}
},
trustCertUpdateFunc: func() {
err = copyFileContents(testdata.Path("server_trust_cert_2.pem"), tmpFiles.serverTrustTmp.Name())
if err != nil {
t.Fatalf("copyFileContents(%s, %s) failed: %v", testdata.Path("server_trust_cert_2.pem"), tmpFiles.serverTrustTmp.Name(), err)
}
},
},
{
desc: "test the reloading feature for serverIdentityProvider and clientTrustProvider",
certUpdateFunc: func() {
err = copyFileContents(testdata.Path("server_cert_2.pem"), tmpFiles.serverCertTmp.Name())
if err != nil {
t.Fatalf("copyFileContents(%s, %s) failed: %v", testdata.Path("server_cert_2.pem"), tmpFiles.serverCertTmp.Name(), err)
}
},
keyUpdateFunc: func() {
err = copyFileContents(testdata.Path("server_key_2.pem"), tmpFiles.serverKeyTmp.Name())
if err != nil {
t.Fatalf("copyFileContents(%s, %s) failed: %v", testdata.Path("server_key_2.pem"), tmpFiles.serverKeyTmp.Name(), err)
}
},
trustCertUpdateFunc: func() {
err = copyFileContents(testdata.Path("client_trust_cert_2.pem"), tmpFiles.clientTrustTmp.Name())
if err != nil {
t.Fatalf("copyFileContents(%s, %s) failed: %v", testdata.Path("client_trust_cert_2.pem"), tmpFiles.clientTrustTmp.Name(), err)
}
},
},
} {
test := test
t.Run(test.desc, func(t *testing.T) {
if err := tmpFiles.copyCredsToTmpFiles(); err != nil {
t.Fatalf("tmpFiles.copyCredsToTmpFiles() failed, error: %v", err)
}
clientIdentityProvider, clientRootProvider, serverIdentityProvider, serverRootProvider, err := createProviders(tmpFiles)
if err != nil {
t.Fatalf("createProviders(%v) failed, error: %v", tmpFiles, err)
}
defer clientIdentityProvider.Close()
defer clientRootProvider.Close()
defer serverIdentityProvider.Close()
defer serverRootProvider.Close()
// Start a server and create a client using advancedtls API with Provider.
serverOptions := &Options{
IdentityOptions: IdentityCertificateOptions{
IdentityProvider: serverIdentityProvider,
},
RootOptions: RootCertificateOptions{
RootProvider: serverRootProvider,
},
RequireClientCert: true,
AdditionalPeerVerification: func(*HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
return &PostHandshakeVerificationResults{}, nil
},
VerificationType: CertVerification,
}
serverTLSCreds, err := NewServerCreds(serverOptions)
if err != nil {
t.Fatalf("failed to create server creds: %v", err)
}
s := grpc.NewServer(grpc.Creds(serverTLSCreds))
defer s.Stop()
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
defer lis.Close()
addr := fmt.Sprintf("localhost:%v", lis.Addr().(*net.TCPAddr).Port)
pb.RegisterGreeterServer(s, greeterServer{})
go s.Serve(lis)
clientOptions := &Options{
IdentityOptions: IdentityCertificateOptions{
IdentityProvider: clientIdentityProvider,
},
AdditionalPeerVerification: func(*HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
return &PostHandshakeVerificationResults{}, nil
},
RootOptions: RootCertificateOptions{
RootProvider: clientRootProvider,
},
VerificationType: CertVerification,
}
clientTLSCreds, err := NewClientCreds(clientOptions)
if err != nil {
t.Fatalf("clientTLSCreds failed to create, error: %v", err)
}
// At initialization, the connection should be good.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
conn, greetClient, err := callAndVerifyWithClientConn(ctx, addr, "rpc call 1", clientTLSCreds, false)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
// Make the identity cert change, and wait 1 second for the provider to
// pick up the change.
test.certUpdateFunc()
time.Sleep(sleepInterval)
// The already-established connection should not be affected.
err = callAndVerify(ctx, "rpc call 2", greetClient, false)
if err != nil {
t.Fatal(err)
}
// New connections should still be good, because the Provider didn't pick
// up the changes due to key-cert mismatch.
conn2, _, err := callAndVerifyWithClientConn(ctx, addr, "rpc call 3", clientTLSCreds, false)
if err != nil {
t.Fatal(err)
}
defer conn2.Close()
// Make the identity key change, and wait 1 second for the provider to
// pick up the change.
test.keyUpdateFunc()
time.Sleep(sleepInterval)
// New connections should fail now, because the Provider picked the
// change, and *_cert_2.pem is not trusted by *_trust_cert_1.pem on the
// other side.
ctx2, cancel2 := context.WithTimeout(context.Background(), defaultTestTimeout)
conn3, _, err := callAndVerifyWithClientConn(ctx2, addr, "rpc call 4", clientTLSCreds, true)
if err != nil {
t.Fatal(err)
}
defer conn3.Close()
// Immediately cancel the context so the dialing won't drag the entire timeout still it stops.
cancel2()
// Make the trust cert change on the other side, and wait 1 second for
// the provider to pick up the change.
test.trustCertUpdateFunc()
time.Sleep(sleepInterval)
// New connections should be good, because the other side is using
// *_trust_cert_2.pem now.
conn4, _, err := callAndVerifyWithClientConn(ctx, addr, "rpc call 5", clientTLSCreds, false)
if err != nil {
t.Fatal(err)
}
defer conn4.Close()
})
}
}
func (s) TestDefaultHostNameCheck(t *testing.T) {
cs := &testutils.CertStore{}
if err := cs.LoadCerts(); err != nil {
t.Fatalf("cs.LoadCerts() failed, err: %v", err)
}
for _, test := range []struct {
desc string
clientRoot *x509.CertPool
clientVerificationType VerificationType
serverCert []tls.Certificate
serverVerificationType VerificationType
expectError bool
}{
// Client side sets vType to CertAndHostVerification, and will do
// default hostname check. Server uses a cert without "localhost" or
// "127.0.0.1" as common name or SAN names, and will hence fail.
{
desc: "Bad default hostname check",
clientRoot: cs.ClientTrust1,
clientVerificationType: CertAndHostVerification,
serverCert: []tls.Certificate{cs.ServerCert1},
serverVerificationType: CertAndHostVerification,
expectError: true,
},
// Client side sets vType to CertAndHostVerification, and will do
// default hostname check. Server uses a certificate with "localhost" as
// common name, and will hence pass the default hostname check.
{
desc: "Good default hostname check",
clientRoot: cs.ClientTrust1,
clientVerificationType: CertAndHostVerification,
serverCert: []tls.Certificate{cs.ServerPeerLocalhost1},
serverVerificationType: CertAndHostVerification,
expectError: false,
},
} {
test := test
t.Run(test.desc, func(t *testing.T) {
// Start a server using ServerOptions in another goroutine.
serverOptions := &Options{
IdentityOptions: IdentityCertificateOptions{
Certificates: test.serverCert,
},
RequireClientCert: false,
VerificationType: test.serverVerificationType,
}
serverTLSCreds, err := NewServerCreds(serverOptions)
if err != nil {
t.Fatalf("failed to create server creds: %v", err)
}
s := grpc.NewServer(grpc.Creds(serverTLSCreds))
defer s.Stop()
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
defer lis.Close()
addr := fmt.Sprintf("localhost:%v", lis.Addr().(*net.TCPAddr).Port)
pb.RegisterGreeterServer(s, greeterServer{})
go s.Serve(lis)
clientOptions := &Options{
RootOptions: RootCertificateOptions{
RootCertificates: test.clientRoot,
},
VerificationType: test.clientVerificationType,
}
clientTLSCreds, err := NewClientCreds(clientOptions)
if err != nil {
t.Fatalf("clientTLSCreds failed to create: %v", err)
}
shouldFail := false
if test.expectError {
shouldFail = true
}
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
conn, _, err := callAndVerifyWithClientConn(ctx, addr, "rpc call 1", clientTLSCreds, shouldFail)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
})
}
}
func (s) TestTLSVersions(t *testing.T) {
cs := &testutils.CertStore{}
if err := cs.LoadCerts(); err != nil {
t.Fatalf("cs.LoadCerts() failed, err: %v", err)
}
for _, test := range []struct {
desc string
expectError bool
clientMinVersion uint16
clientMaxVersion uint16
serverMinVersion uint16
serverMaxVersion uint16
}{
// Client side sets TLS version that is higher than required from the server side.
{
desc: "Client TLS version higher than server",
clientMinVersion: tls.VersionTLS13,
clientMaxVersion: tls.VersionTLS13,
serverMinVersion: tls.VersionTLS12,
serverMaxVersion: tls.VersionTLS12,
expectError: true,
},
// Server side sets TLS version that is higher than required from the client side.
{
desc: "Server TLS version higher than client",
clientMinVersion: tls.VersionTLS12,
clientMaxVersion: tls.VersionTLS12,
serverMinVersion: tls.VersionTLS13,
serverMaxVersion: tls.VersionTLS13,
expectError: true,
},
// Client and server set proper TLS versions.
{
desc: "Good TLS version settings",
clientMinVersion: tls.VersionTLS12,
clientMaxVersion: tls.VersionTLS13,
serverMinVersion: tls.VersionTLS12,
serverMaxVersion: tls.VersionTLS13,
expectError: false,
},
{
desc: "Client 1.2 - 1.3 and server 1.2",
clientMinVersion: tls.VersionTLS12,
clientMaxVersion: tls.VersionTLS13,
serverMinVersion: tls.VersionTLS12,
serverMaxVersion: tls.VersionTLS12,
expectError: false,
},
{
desc: "Client 1.2 - 1.3 and server 1.1 - 1.2",
clientMinVersion: tls.VersionTLS12,
clientMaxVersion: tls.VersionTLS13,
serverMinVersion: tls.VersionTLS11,
serverMaxVersion: tls.VersionTLS12,
expectError: false,
},
{
desc: "Client 1.2 - 1.3 and server 1.3",
clientMinVersion: tls.VersionTLS12,
clientMaxVersion: tls.VersionTLS13,
serverMinVersion: tls.VersionTLS13,
serverMaxVersion: tls.VersionTLS13,
expectError: false,
},
{
desc: "Client 1.2 - 1.2 and server 1.2 - 1.3",
clientMinVersion: tls.VersionTLS12,
clientMaxVersion: tls.VersionTLS12,
serverMinVersion: tls.VersionTLS12,
serverMaxVersion: tls.VersionTLS13,
expectError: false,
},
{
desc: "Client 1.1 - 1.2 and server 1.2 - 1.3",
clientMinVersion: tls.VersionTLS11,
clientMaxVersion: tls.VersionTLS12,
serverMinVersion: tls.VersionTLS12,
serverMaxVersion: tls.VersionTLS13,
expectError: false,
},
{
desc: "Client 1.3 and server 1.2 - 1.3",
clientMinVersion: tls.VersionTLS13,
clientMaxVersion: tls.VersionTLS13,
serverMinVersion: tls.VersionTLS12,
serverMaxVersion: tls.VersionTLS13,
expectError: false,
},
} {
test := test
t.Run(test.desc, func(t *testing.T) {
// Start a server using ServerOptions in another goroutine.
serverOptions := &Options{
IdentityOptions: IdentityCertificateOptions{
Certificates: []tls.Certificate{cs.ServerPeerLocalhost1},
},
RequireClientCert: false,
VerificationType: CertAndHostVerification,
MinTLSVersion: test.serverMinVersion,
MaxTLSVersion: test.serverMaxVersion,
}
serverTLSCreds, err := NewServerCreds(serverOptions)
if err != nil {
t.Fatalf("failed to create server creds: %v", err)
}
s := grpc.NewServer(grpc.Creds(serverTLSCreds))
defer s.Stop()
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
defer lis.Close()
addr := fmt.Sprintf("localhost:%v", lis.Addr().(*net.TCPAddr).Port)
pb.RegisterGreeterServer(s, greeterServer{})
go s.Serve(lis)
clientOptions := &Options{
RootOptions: RootCertificateOptions{
RootCertificates: cs.ClientTrust1,
},
VerificationType: CertAndHostVerification,
MinTLSVersion: test.clientMinVersion,
MaxTLSVersion: test.clientMaxVersion,
}
clientTLSCreds, err := NewClientCreds(clientOptions)
if err != nil {
t.Fatalf("clientTLSCreds failed to create: %v", err)
}
shouldFail := false
if test.expectError {
shouldFail = true
}
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
conn, _, err := callAndVerifyWithClientConn(ctx, addr, "rpc call 1", clientTLSCreds, shouldFail)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
})
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2026 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 (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"testing"
"time"
)
func generateCertificates(t *testing.T, extKeyUsages []x509.ExtKeyUsage) (*x509.CertPool, tls.Certificate) {
t.Helper()
caKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("Failed to generate CA key: %v", err)
}
caTemplate := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "Test CA"},
NotBefore: time.Now().Add(-1 * time.Hour),
NotAfter: time.Now().Add(1 * time.Hour),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
BasicConstraintsValid: true,
}
caDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caKey.PublicKey, caKey)
if err != nil {
t.Fatalf("Failed to create CA cert: %v", err)
}
caCert, err := x509.ParseCertificate(caDER)
if err != nil {
t.Fatalf("Failed to parse CA cert: %v", err)
}
serverKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("Failed to generate server key: %v", err)
}
serverTemplate := &x509.Certificate{
SerialNumber: big.NewInt(2),
Subject: pkix.Name{CommonName: "localhost"},
NotBefore: time.Now().Add(-1 * time.Hour),
NotAfter: time.Now().Add(1 * time.Hour),
ExtKeyUsage: extKeyUsages,
DNSNames: []string{"localhost"},
}
serverDER, err := x509.CreateCertificate(rand.Reader, serverTemplate, caCert, &serverKey.PublicKey, caKey)
if err != nil {
t.Fatalf("Failed to create server cert: %v", err)
}
rootPool := x509.NewCertPool()
rootPool.AddCert(caCert)
serverCert := tls.Certificate{
Certificate: [][]byte{serverDER},
PrivateKey: serverKey,
}
return rootPool, serverCert
}

436
security/advancedtls/crl.go Normal file
View File

@@ -0,0 +1,436 @@
/*
*
* Copyright 2021 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 (
"bytes"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"os"
"golang.org/x/crypto/cryptobyte"
cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
"google.golang.org/grpc/grpclog"
)
var grpclogLogger = grpclog.Component("advancedtls")
// RevocationOptions allows a user to configure certificate revocation behavior.
type RevocationOptions struct {
// DenyUndetermined controls if certificate chains with RevocationUndetermined
// revocation status are allowed to complete.
DenyUndetermined bool
// CRLProvider is an alternative to using RootDir directly for the
// X509_LOOKUP_hash_dir approach to CRL files. If set, the CRLProvider's CRL
// function will be called when looking up and fetching CRLs during the
// handshake.
CRLProvider CRLProvider
}
// revocationStatus is the revocation status for a certificate or chain.
type revocationStatus int
const (
// RevocationUndetermined means we couldn't find or verify a CRL for the cert.
RevocationUndetermined revocationStatus = iota
// RevocationUnrevoked means we found the CRL for the cert and the cert is not revoked.
RevocationUnrevoked
// RevocationRevoked means we found the CRL and the cert is revoked.
RevocationRevoked
)
// CRL contains a pkix.CertificateList and parsed extensions that aren't
// provided by the golang CRL parser.
// All CRLs should be loaded using NewCRL() for bytes directly or ReadCRLFile()
// to read directly from a filepath
type CRL struct {
certList *x509.RevocationList
// RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method.
authorityKeyID []byte
rawIssuer []byte
}
// NewCRL constructs new CRL from the provided byte array.
func NewCRL(b []byte) (*CRL, error) {
crl, err := parseRevocationList(b)
if err != nil {
return nil, fmt.Errorf("fail to parse CRL: %v", err)
}
crlExt, err := parseCRLExtensions(crl)
if err != nil {
return nil, fmt.Errorf("fail to parse CRL extensions: %v", err)
}
crlExt.rawIssuer, err = extractCRLIssuer(b)
if err != nil {
return nil, fmt.Errorf("fail to extract CRL issuer failed err= %v", err)
}
return crlExt, nil
}
// ReadCRLFile reads a file from the provided path, and returns constructed CRL
// struct from it.
func ReadCRLFile(path string) (*CRL, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("cannot read file from provided path %q: %v", path, err)
}
crl, err := NewCRL(b)
if err != nil {
return nil, fmt.Errorf("cannot construct CRL from file %q: %v", path, err)
}
return crl, nil
}
const tagDirectoryName = 4
var (
// RFC5280, 5.2.4 id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 }
oidDeltaCRLIndicator = asn1.ObjectIdentifier{2, 5, 29, 27}
// RFC5280, 5.2.5 id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }
oidIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28}
// RFC5280, 5.3.3 id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 }
oidCertificateIssuer = asn1.ObjectIdentifier{2, 5, 29, 29}
// RFC5290, 4.2.1.1 id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35}
)
// checkChainRevocation checks the verified certificate chain
// for revoked certificates based on RFC5280.
func checkChainRevocation(verifiedChains [][]*x509.Certificate, cfg RevocationOptions) error {
// Iterate the verified chains looking for one that is RevocationUnrevoked.
// A single RevocationUnrevoked chain is enough to allow the connection, and a single RevocationRevoked
// chain does not mean the connection should fail.
count := make(map[revocationStatus]int)
for _, chain := range verifiedChains {
switch checkChain(chain, cfg) {
case RevocationUnrevoked:
// If any chain is RevocationUnrevoked then return no error.
return nil
case RevocationRevoked:
// If this chain is revoked, keep looking for another chain.
count[RevocationRevoked]++
continue
case RevocationUndetermined:
count[RevocationUndetermined]++
if cfg.DenyUndetermined {
continue
}
return nil
}
}
return fmt.Errorf("no unrevoked chains found: %v", count)
}
// checkChain will determine and check all certificates in chain against the CRL
// defined in the certificate with the following rules:
// 1. If any certificate is RevocationRevoked, return RevocationRevoked.
// 2. If any certificate is RevocationUndetermined, return RevocationUndetermined.
// 3. If all certificates are RevocationUnrevoked, return RevocationUnrevoked.
func checkChain(chain []*x509.Certificate, cfg RevocationOptions) revocationStatus {
chainStatus := RevocationUnrevoked
for _, c := range chain {
switch checkCert(c, chain, cfg) {
case RevocationRevoked:
// Easy case, if a cert in the chain is revoked, the chain is revoked.
return RevocationRevoked
case RevocationUndetermined:
// If we couldn't find the revocation status for a cert, the chain is at best RevocationUndetermined
// keep looking to see if we find a cert in the chain that's RevocationRevoked,
// but return RevocationUndetermined at a minimum.
chainStatus = RevocationUndetermined
case RevocationUnrevoked:
// Continue iterating up the cert chain.
continue
}
}
return chainStatus
}
func fetchCRL(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationOptions) (*CRL, error) {
if cfg.CRLProvider == nil {
return nil, fmt.Errorf("trying to fetch CRL but CRLProvider is nil")
}
crl, err := cfg.CRLProvider.CRL(c)
if err != nil {
return nil, fmt.Errorf("CrlProvider failed err = %v", err)
}
if crl == nil {
return nil, fmt.Errorf("no CRL found for certificate's issuer")
}
if err := verifyCRL(crl, crlVerifyCrt); err != nil {
return nil, fmt.Errorf("verifyCRL() failed: %v", err)
}
return crl, nil
}
// checkCert checks a single certificate against the CRL defined in the
// certificate. It will fetch and verify the CRL(s) defined in the root
// directory (or a CRLProvider) specified by cfg. If we can't load (and verify -
// see verifyCRL) any valid authoritative CRL files, the status is
// RevocationUndetermined.
// c is the certificate to check.
// crlVerifyCrt is the group of possible certificates to verify the crl.
func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationOptions) revocationStatus {
crl, err := fetchCRL(c, crlVerifyCrt, cfg)
if err != nil {
// We couldn't load any valid CRL files for the certificate, so we don't
// know if it's RevocationUnrevoked or not. This is not necessarily a
// problem - it's not invalid to have no CRLs if you don't have any
// revocations for an issuer. It also might be an indication that the CRL
// file is invalid.
// We just return RevocationUndetermined and there is a setting for the user
// to control the handling of that.
grpclogLogger.Warningf("fetchCRL() err = %v", err)
return RevocationUndetermined
}
revocation, err := checkCertRevocation(c, crl)
if err != nil {
grpclogLogger.Warningf("checkCertRevocation(CRL %v) failed: %v", crl.certList.Issuer, err)
// We couldn't check the CRL file for some reason, so we don't know if it's RevocationUnrevoked or not.
return RevocationUndetermined
}
// Here we've gotten a CRL that loads and verifies.
// We only handle all-reasons CRL files, so this file
// is authoritative for the certificate.
return revocation
}
func checkCertRevocation(c *x509.Certificate, crl *CRL) (revocationStatus, error) {
// Per section 5.3.3 we prime the certificate issuer with the CRL issuer.
// Subsequent entries use the previous entry's issuer.
rawEntryIssuer := crl.rawIssuer
// Loop through all the revoked certificates.
for _, revCert := range crl.certList.RevokedCertificateEntries {
// 5.3 Loop through CRL entry extensions for needed information.
for _, ext := range revCert.Extensions {
if oidCertificateIssuer.Equal(ext.Id) {
extIssuer, err := parseCertIssuerExt(ext)
if err != nil {
grpclogLogger.Info(err)
if ext.Critical {
return RevocationUndetermined, err
}
// Since this is a non-critical extension, we can skip it even though
// there was a parsing failure.
continue
}
rawEntryIssuer = extIssuer
} else if ext.Critical {
return RevocationUndetermined, fmt.Errorf("checkCertRevocation: Unhandled critical extension: %v", ext.Id)
}
}
// If the issuer and serial number appear in the CRL, the certificate is revoked.
if bytes.Equal(c.RawIssuer, rawEntryIssuer) && c.SerialNumber.Cmp(revCert.SerialNumber) == 0 {
// CRL contains the serial, so return revoked.
return RevocationRevoked, nil
}
}
// We did not find the serial in the CRL file that was valid for the cert
// so the certificate is not revoked.
return RevocationUnrevoked, nil
}
func parseCertIssuerExt(ext pkix.Extension) ([]byte, error) {
// 5.3.3 Certificate Issuer
// CertificateIssuer ::= GeneralNames
// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
var generalNames []asn1.RawValue
if rest, err := asn1.Unmarshal(ext.Value, &generalNames); err != nil || len(rest) != 0 {
return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err)
}
for _, generalName := range generalNames {
// GeneralName ::= CHOICE {
// otherName [0] OtherName,
// rfc822Name [1] IA5String,
// dNSName [2] IA5String,
// x400Address [3] ORAddress,
// directoryName [4] Name,
// ediPartyName [5] EDIPartyName,
// uniformResourceIdentifier [6] IA5String,
// iPAddress [7] OCTET STRING,
// registeredID [8] OBJECT IDENTIFIER }
if generalName.Tag == tagDirectoryName {
return generalName.Bytes, nil
}
}
// Conforming CRL issuers MUST include in this extension the
// distinguished name (DN) from the issuer field of the certificate that
// corresponds to this CRL entry.
// If we couldn't get a directoryName, we can't reason about this file so cert status is
// RevocationUndetermined.
return nil, errors.New("no DN found in certificate issuer")
}
// RFC 5280, 4.2.1.1
type authKeyID struct {
ID []byte `asn1:"optional,tag:0"`
}
// RFC5280, 5.2.5
// id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }
// IssuingDistributionPoint ::= SEQUENCE {
// distributionPoint [0] DistributionPointName OPTIONAL,
// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE,
// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE,
// onlySomeReasons [3] ReasonFlags OPTIONAL,
// indirectCRL [4] BOOLEAN DEFAULT FALSE,
// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
// -- at most one of onlyContainsUserCerts, onlyContainsCACerts,
// -- and onlyContainsAttributeCerts may be set to TRUE.
type issuingDistributionPoint struct {
DistributionPoint asn1.RawValue `asn1:"optional,tag:0"`
OnlyContainsUserCerts bool `asn1:"optional,tag:1"`
OnlyContainsCACerts bool `asn1:"optional,tag:2"`
OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"`
IndirectCRL bool `asn1:"optional,tag:4"`
OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"`
}
// parseCRLExtensions parses the extensions for a CRL
// and checks that they're supported by the parser.
func parseCRLExtensions(c *x509.RevocationList) (*CRL, error) {
if c == nil {
return nil, errors.New("c is nil, expected any value")
}
certList := &CRL{certList: c}
for _, ext := range c.Extensions {
switch {
case oidDeltaCRLIndicator.Equal(ext.Id):
return nil, fmt.Errorf("delta CRLs unsupported")
case oidAuthorityKeyIdentifier.Equal(ext.Id):
var a authKeyID
if rest, err := asn1.Unmarshal(ext.Value, &a); err != nil {
return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err)
} else if len(rest) != 0 {
return nil, errors.New("trailing data after AKID extension")
}
certList.authorityKeyID = a.ID
case oidIssuingDistributionPoint.Equal(ext.Id):
var dp issuingDistributionPoint
if rest, err := asn1.Unmarshal(ext.Value, &dp); err != nil {
return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err)
} else if len(rest) != 0 {
return nil, errors.New("trailing data after IssuingDistributionPoint extension")
}
if dp.OnlyContainsUserCerts || dp.OnlyContainsCACerts || dp.OnlyContainsAttributeCerts {
return nil, errors.New("CRL only contains some certificate types")
}
if dp.IndirectCRL {
return nil, errors.New("indirect CRLs unsupported")
}
if dp.OnlySomeReasons.BitLength != 0 {
return nil, errors.New("onlySomeReasons unsupported")
}
case ext.Critical:
return nil, fmt.Errorf("unsupported critical extension: %v", ext.Id)
}
}
if len(certList.authorityKeyID) == 0 {
return nil, errors.New("authority key identifier extension missing")
}
return certList, nil
}
func verifyCRL(crl *CRL, chain []*x509.Certificate) error {
// RFC5280, 6.3.3 (f) Obtain and validate the certification path for the issuer of the complete CRL
// We intentionally limit our CRLs to be signed with the same certificate path as the certificate
// so we can use the chain from the connection.
for _, c := range chain {
// Use the key where the subject and KIDs match.
// This departs from RFC4158, 3.5.12 which states that KIDs
// cannot eliminate certificates, but RFC5280, 5.2.1 states that
// "Conforming CRL issuers MUST use the key identifier method, and MUST
// include this extension in all CRLs issued."
// So, this is much simpler than RFC4158 and should be compatible.
if bytes.Equal(c.SubjectKeyId, crl.authorityKeyID) && bytes.Equal(c.RawSubject, crl.rawIssuer) {
// RFC5280, 6.3.3 (f) Key usage and cRLSign bit.
if c.KeyUsage != 0 && c.KeyUsage&x509.KeyUsageCRLSign == 0 {
return fmt.Errorf("verifyCRL: The certificate can't be used for issuing CRLs")
}
// RFC5280, 6.3.3 (g) Validate signature.
return crl.certList.CheckSignatureFrom(c)
}
}
return fmt.Errorf("verifyCRL: No certificates matched CRL issuer (%v)", crl.certList.Issuer)
}
// pemType is the type of a PEM encoded CRL.
const pemType string = "X509 CRL"
var crlPemPrefix = []byte("-----BEGIN X509 CRL")
func crlPemToDer(crlBytes []byte) []byte {
block, _ := pem.Decode(crlBytes)
if block != nil && block.Type == pemType {
crlBytes = block.Bytes
}
return crlBytes
}
// extractCRLIssuer extracts the raw ASN.1 encoding of the CRL issuer. Due to the design of
// pkix.CertificateList and pkix.RDNSequence, it is not possible to reliably marshal the
// parsed Issuer to its original raw encoding.
func extractCRLIssuer(crlBytes []byte) ([]byte, error) {
if bytes.HasPrefix(crlBytes, crlPemPrefix) {
crlBytes = crlPemToDer(crlBytes)
}
der := cryptobyte.String(crlBytes)
var issuer cryptobyte.String
// This doubled der.ReadASN1 is intentional, it modifies the input buffer
if !der.ReadASN1(&der, cbasn1.SEQUENCE) ||
!der.ReadASN1(&der, cbasn1.SEQUENCE) ||
!der.SkipOptionalASN1(cbasn1.INTEGER) ||
!der.SkipASN1(cbasn1.SEQUENCE) ||
!der.ReadASN1Element(&issuer, cbasn1.SEQUENCE) {
return nil, errors.New("extractCRLIssuer: invalid ASN.1 encoding")
}
return issuer, nil
}
// parseRevocationList comes largely from here
// x509.go:
// https://github.com/golang/go/blob/e2f413402527505144beea443078649380e0c545/src/crypto/x509/x509.go#L1669-L1690
// We must first convert PEM to DER to be able to use the new
// x509.ParseRevocationList instead of the deprecated x509.ParseCRL
func parseRevocationList(crlBytes []byte) (*x509.RevocationList, error) {
if bytes.HasPrefix(crlBytes, crlPemPrefix) {
crlBytes = crlPemToDer(crlBytes)
}
crl, err := x509.ParseRevocationList(crlBytes)
if err != nil {
return nil, err
}
return crl, nil
}

View File

@@ -0,0 +1,239 @@
/*
*
* Copyright 2023 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 (
"crypto/x509"
"fmt"
"os"
"sync"
"time"
)
const defaultCRLRefreshDuration = 1 * time.Hour
const minCRLRefreshDuration = 1 * time.Minute
// CRLProvider is the interface to be implemented to enable custom CRL provider
// behavior, as defined in [gRFC A69].
//
// The interface defines how gRPC gets CRLs from the provider during handshakes,
// but doesn't prescribe a specific way to load and store CRLs. Such
// implementations can be used in RevocationOptions of advancedtls.ClientOptions
// and/or advancedtls.ServerOptions.
// Please note that checking CRLs is directly on the path of connection
// establishment, so implementations of the CRL function need to be fast, and
// slow things such as file IO should be done asynchronously.
//
// [gRFC A69]: https://github.com/grpc/proposal/pull/382
type CRLProvider interface {
// CRL accepts x509 Cert and returns a related CRL struct, which can contain
// either an empty or non-empty list of revoked certificates. If an error is
// thrown or (nil, nil) is returned, it indicates that we can't load any
// authoritative CRL files (which may not necessarily be a problem). It's not
// considered invalid to have no CRLs if there are no revocations for an
// issuer. In such cases, the status of the check CRL operation is marked as
// RevocationUndetermined, as defined in [RFC5280 - Undetermined].
//
// [RFC5280 - Undetermined]: https://datatracker.ietf.org/doc/html/rfc5280#section-6.3.3
CRL(cert *x509.Certificate) (*CRL, error)
}
// StaticCRLProvider implements CRLProvider interface by accepting raw content
// of CRL files at creation time and storing parsed CRL structs in-memory.
type StaticCRLProvider struct {
crls map[string]*CRL
}
// NewStaticCRLProvider processes raw content of CRL files, adds parsed CRL
// structs into in-memory, and returns a new instance of the StaticCRLProvider.
func NewStaticCRLProvider(rawCRLs [][]byte) *StaticCRLProvider {
p := StaticCRLProvider{}
p.crls = make(map[string]*CRL)
for idx, rawCRL := range rawCRLs {
cRL, err := NewCRL(rawCRL)
if err != nil {
grpclogLogger.Warningf("Can't parse raw CRL number %v from the slice: %v", idx, err)
continue
}
p.addCRL(cRL)
}
return &p
}
// AddCRL adds/updates provided CRL to in-memory storage.
func (p *StaticCRLProvider) addCRL(crl *CRL) {
key := crl.certList.Issuer.ToRDNSequence().String()
p.crls[key] = crl
}
// CRL returns CRL struct if it was passed to NewStaticCRLProvider.
func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) {
return p.crls[cert.Issuer.ToRDNSequence().String()], nil
}
// FileWatcherOptions represents a data structure holding a configuration for
// FileWatcherCRLProvider.
type FileWatcherOptions struct {
CRLDirectory string // Required: Path of the directory containing CRL files
RefreshDuration time.Duration // Optional: Time interval (default 1 hour) between CRLDirectory scans, can't be smaller than 1 minute
CRLReloadingFailedCallback func(err error) // Optional: Custom callback executed when a CRL file can’t be processed
}
// FileWatcherCRLProvider implements the CRLProvider interface by periodically
// scanning CRLDirectory (see FileWatcherOptions) and storing CRL structs
// in-memory. Users should call Close to stop the background refresh of
// CRLDirectory.
type FileWatcherCRLProvider struct {
crls map[string]*CRL
opts FileWatcherOptions
mu sync.Mutex
stop chan struct{}
done chan struct{}
}
// NewFileWatcherCRLProvider returns a new instance of the
// FileWatcherCRLProvider. It uses FileWatcherOptions to validate and apply
// configuration required for creating a new instance. The initial scan of
// CRLDirectory is performed inside this function. Users should call Close to
// stop the background refresh of CRLDirectory.
func NewFileWatcherCRLProvider(o FileWatcherOptions) (*FileWatcherCRLProvider, error) {
if err := o.validate(); err != nil {
return nil, err
}
provider := &FileWatcherCRLProvider{
crls: make(map[string]*CRL),
opts: o,
stop: make(chan struct{}),
done: make(chan struct{}),
}
provider.scanCRLDirectory()
go provider.run()
return provider, nil
}
func (o *FileWatcherOptions) validate() error {
// Checks relates to CRLDirectory.
if o.CRLDirectory == "" {
return fmt.Errorf("advancedtls: CRLDirectory needs to be specified")
}
if _, err := os.ReadDir(o.CRLDirectory); err != nil {
return fmt.Errorf("advancedtls: CRLDirectory %v is not readable: %v", o.CRLDirectory, err)
}
// Checks related to RefreshDuration.
if o.RefreshDuration == 0 {
o.RefreshDuration = defaultCRLRefreshDuration
}
if o.RefreshDuration < minCRLRefreshDuration {
grpclogLogger.Warningf("RefreshDuration must be at least 1 minute: provided value %v, minimum value %v will be used.", o.RefreshDuration, minCRLRefreshDuration)
o.RefreshDuration = minCRLRefreshDuration
}
return nil
}
// Start starts watching the directory for CRL files and updates the provider accordingly.
func (p *FileWatcherCRLProvider) run() {
defer close(p.done)
ticker := time.NewTicker(p.opts.RefreshDuration)
defer ticker.Stop()
for {
select {
case <-p.stop:
grpclogLogger.Infof("Scanning of CRLDirectory %v stopped", p.opts.CRLDirectory)
return
case <-ticker.C:
p.scanCRLDirectory()
}
}
}
// Close waits till the background refresh of CRLDirectory of
// FileWatcherCRLProvider is done and then stops it.
func (p *FileWatcherCRLProvider) Close() {
close(p.stop)
<-p.done
}
// scanCRLDirectory starts the process of scanning
// FileWatcherOptions.CRLDirectory and updating in-memory storage of CRL
// structs, as defined in [gRFC A69]. It's called periodically
// (see FileWatcherOptions.RefreshDuration) by run goroutine.
//
// [gRFC A69]: https://github.com/grpc/proposal/pull/382
func (p *FileWatcherCRLProvider) scanCRLDirectory() {
dir, err := os.Open(p.opts.CRLDirectory)
if err != nil {
grpclogLogger.Errorf("Can't open CRLDirectory %v", p.opts.CRLDirectory, err)
if p.opts.CRLReloadingFailedCallback != nil {
p.opts.CRLReloadingFailedCallback(err)
}
}
defer dir.Close()
files, err := dir.ReadDir(0)
if err != nil {
grpclogLogger.Errorf("Can't access files under CRLDirectory %v", p.opts.CRLDirectory, err)
if p.opts.CRLReloadingFailedCallback != nil {
p.opts.CRLReloadingFailedCallback(err)
}
}
tempCRLs := make(map[string]*CRL)
successCounter := 0
failCounter := 0
for _, file := range files {
filePath := fmt.Sprintf("%s/%s", p.opts.CRLDirectory, file.Name())
crl, err := ReadCRLFile(filePath)
if err != nil {
failCounter++
grpclogLogger.Warningf("Can't add CRL from file %v under CRLDirectory %v", filePath, p.opts.CRLDirectory, err)
if p.opts.CRLReloadingFailedCallback != nil {
p.opts.CRLReloadingFailedCallback(err)
}
continue
}
tempCRLs[crl.certList.Issuer.ToRDNSequence().String()] = crl
successCounter++
}
// Only if all the files are processed successfully we can swap maps (there
// might be deletions of entries in this case).
if len(files) == successCounter {
p.mu.Lock()
defer p.mu.Unlock()
p.crls = tempCRLs
grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files found and processed successfully, in-memory CRL storage flushed and repopulated", p.opts.CRLDirectory, len(files))
} else {
// Since some of the files failed we can only add/update entries in the map.
p.mu.Lock()
defer p.mu.Unlock()
for key, value := range tempCRLs {
p.crls[key] = value
}
grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files found, %v files processing failed, %v entries of in-memory CRL storage added/updated", p.opts.CRLDirectory, len(files), failCounter, successCounter)
}
}
// CRL retrieves the CRL associated with the given certificate's issuer DN from
// in-memory if it was loaded during FileWatcherOptions.CRLDirectory scan before
// the execution of this function.
func (p *FileWatcherCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) {
p.mu.Lock()
defer p.mu.Unlock()
return p.crls[cert.Issuer.ToRDNSequence().String()], nil
}

View File

@@ -0,0 +1,356 @@
/*
*
* Copyright 2023 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 (
"crypto/x509"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"google.golang.org/grpc/security/advancedtls/testdata"
)
// TestStaticCRLProvider tests how StaticCRLProvider handles the major four
// cases for CRL checks. It loads the CRLs under crl directory, constructs
// unrevoked, revoked leaf, and revoked intermediate chains, as well as a chain
// without CRL for issuer, and checks that it’s correctly processed.
func (s) TestStaticCRLProvider(t *testing.T) {
rawCRLs := make([][]byte, 6)
for i := 1; i <= 6; i++ {
rawCRL, err := os.ReadFile(testdata.Path(fmt.Sprintf("crl/%d.crl", i)))
if err != nil {
t.Fatalf("readFile(%v) failed err = %v", fmt.Sprintf("crl/%d.crl", i), err)
}
rawCRLs = append(rawCRLs, rawCRL)
}
p := NewStaticCRLProvider(rawCRLs)
// Each test data entry contains a description of a certificate chain,
// certificate chain itself, and if CRL is not expected to be found.
tests := []struct {
desc string
certs []*x509.Certificate
expectNoCRL bool
}{
{
desc: "Unrevoked chain",
certs: makeChain(t, testdata.Path("crl/unrevoked.pem")),
},
{
desc: "Revoked Intermediate chain",
certs: makeChain(t, testdata.Path("crl/revokedInt.pem")),
},
{
desc: "Revoked leaf chain",
certs: makeChain(t, testdata.Path("crl/revokedLeaf.pem")),
},
{
desc: "Chain with no CRL for issuer",
certs: makeChain(t, testdata.Path("client_cert_1.pem")),
expectNoCRL: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
for _, c := range tt.certs {
crl, err := p.CRL(c)
if err != nil {
t.Fatalf("Expected error fetch from provider: %v", err)
}
if crl == nil && !tt.expectNoCRL {
t.Fatalf("CRL is unexpectedly nil")
}
}
})
}
}
// TestFileWatcherCRLProviderConfig checks creation of FileWatcherCRLProvider,
// and the validation of FileWatcherOptions configuration. The configurations include empty
// one, non existing CRLDirectory, invalid RefreshDuration, and the correct one.
func (s) TestFileWatcherCRLProviderConfig(t *testing.T) {
if _, err := NewFileWatcherCRLProvider(FileWatcherOptions{}); err == nil {
t.Fatalf("Empty FileWatcherOptions should not be allowed")
}
if _, err := NewFileWatcherCRLProvider(FileWatcherOptions{CRLDirectory: "I_do_not_exist"}); err == nil {
t.Fatalf("CRLDirectory must exist")
}
defaultProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{CRLDirectory: testdata.Path("crl")})
if err != nil {
t.Fatal("Unexpected error:", err)
}
if defaultProvider.opts.RefreshDuration != defaultCRLRefreshDuration {
t.Fatalf("RefreshDuration for defaultCRLRefreshDuration case is not properly updated by validate() func")
}
defaultProvider.Close()
tooFastRefreshProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{
CRLDirectory: testdata.Path("crl"),
RefreshDuration: 5 * time.Second,
})
if err != nil {
t.Fatal("Unexpected error:", err)
}
if tooFastRefreshProvider.opts.RefreshDuration != minCRLRefreshDuration {
t.Fatalf("RefreshDuration for minCRLRefreshDuration case is not properly updated by validate() func")
}
tooFastRefreshProvider.Close()
customCallback := func(err error) {
t.Logf("Custom error message: %v", err)
}
regularProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{
CRLDirectory: testdata.Path("crl"),
RefreshDuration: 2 * time.Hour,
CRLReloadingFailedCallback: customCallback,
})
if err != nil {
t.Fatal("Unexpected error while creating regular FileWatcherCRLProvider:", err)
}
if regularProvider.opts.RefreshDuration != 2*time.Hour {
t.Fatalf("Valid refreshDuration was incorrectly updated by validate() func")
}
regularProvider.Close()
}
// TestFileWatcherCRLProvider tests how FileWatcherCRLProvider handles the major
// four cases for CRL checks. It scans the CRLs under crl directory to populate
// the in-memory storage. Then we construct unrevoked, revoked leaf, and revoked
// intermediate chains, as well as a chain without CRL for issuer, and check
// that it’s correctly processed. Additionally, we also check if number of
// invocations of custom callback is correct.
func (s) TestFileWatcherCRLProvider(t *testing.T) {
const nonCRLFilesUnderCRLDirectory = 18
nonCRLFilesSet := make(map[string]struct{})
customCallback := func(err error) {
if strings.Contains(err.Error(), "BUILD") {
return
}
nonCRLFilesSet[err.Error()] = struct{}{}
}
p, err := NewFileWatcherCRLProvider(FileWatcherOptions{
CRLDirectory: testdata.Path("crl"),
RefreshDuration: 1 * time.Hour,
CRLReloadingFailedCallback: customCallback,
})
if err != nil {
t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err)
}
// Each test data entry contains a description of a certificate chain,
// certificate chain itself, and if CRL is not expected to be found.
tests := []struct {
desc string
certs []*x509.Certificate
expectNoCRL bool
}{
{
desc: "Unrevoked chain",
certs: makeChain(t, testdata.Path("crl/unrevoked.pem")),
},
{
desc: "Revoked Intermediate chain",
certs: makeChain(t, testdata.Path("crl/revokedInt.pem")),
},
{
desc: "Revoked leaf chain",
certs: makeChain(t, testdata.Path("crl/revokedLeaf.pem")),
},
{
desc: "Chain with no CRL for issuer",
certs: makeChain(t, testdata.Path("client_cert_1.pem")),
expectNoCRL: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
for _, c := range tt.certs {
crl, err := p.CRL(c)
if err != nil {
t.Fatalf("Expected error fetch from provider: %v", err)
}
if crl == nil && !tt.expectNoCRL {
t.Fatalf("CRL is unexpectedly nil")
}
}
})
}
p.Close()
if diff := cmp.Diff(len(nonCRLFilesSet), nonCRLFilesUnderCRLDirectory); diff != "" {
t.Errorf("Unexpected number Number of callback executions\ndiff (-got +want):\n%s", diff)
}
}
// TestFileWatcherCRLProviderDirectoryScan tests how FileWatcherCRLProvider
// handles different contents of FileWatcherOptions.CRLDirectory.
// We update the content with various (correct and incorrect) CRL files and
// check if in-memory storage was properly updated. Please note that the same
// instance of FileWatcherCRLProvider is used for the whole test so test cases
// are not independent from each other.
func (s) TestFileWatcherCRLProviderDirectoryScan(t *testing.T) {
sourcePath := testdata.Path("crl")
targetPath := createTmpDir(t)
defer os.RemoveAll(targetPath)
p, err := NewFileWatcherCRLProvider(FileWatcherOptions{
CRLDirectory: targetPath,
RefreshDuration: 1 * time.Hour,
})
if err != nil {
t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err)
}
// Each test data entry contains a description of CRL directory content
// (including the expected number of entries in the FileWatcherCRLProvider
// map), the name of the files to be copied there before executing the test
// case, and information regarding whether a specific certificate is expected
// to be found in the map.
tests := []struct {
desc string
crlFileNames []string
certFileNames []struct {
fileName string
expected bool
}
}{
{
desc: "Simple addition (1 map entry)",
crlFileNames: []string{"1.crl"},
certFileNames: []struct {
fileName string
expected bool
}{
{"crl/unrevoked.pem", true},
},
},
{
desc: "Addition and deletion (2 map entries)",
crlFileNames: []string{"3.crl", "5.crl"},
certFileNames: []struct {
fileName string
expected bool
}{
{"crl/revokedInt.pem", true},
{"crl/revokedLeaf.pem", true},
{"crl/unrevoked.pem", false},
},
},
{
desc: "Addition and a corrupt file (3 map entries)",
crlFileNames: []string{"1.crl", "README.md"},
certFileNames: []struct {
fileName string
expected bool
}{
{"crl/revokedInt.pem", true},
{"crl/revokedLeaf.pem", true},
{"crl/unrevoked.pem", true},
}},
{
desc: "Full deletion (0 map entries)",
crlFileNames: []string{},
certFileNames: []struct {
fileName string
expected bool
}{
{"crl/revokedInt.pem", false},
{"crl/revokedLeaf.pem", false},
{"crl/unrevoked.pem", false},
}},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
copyFiles(sourcePath, targetPath, tt.crlFileNames, t)
p.scanCRLDirectory()
for _, certFileName := range tt.certFileNames {
c := makeChain(t, testdata.Path(certFileName.fileName))[0]
crl, err := p.CRL(c)
if err != nil {
t.Errorf("Cannot fetch CRL from provider: %v", err)
}
if crl == nil && certFileName.expected {
t.Errorf("CRL is unexpectedly nil")
}
if crl != nil && !certFileName.expected {
t.Errorf("CRL is unexpectedly not nil")
}
}
})
}
p.Close()
}
func copyFiles(sourcePath string, targetPath string, fileNames []string, t *testing.T) {
t.Helper()
targetDir, err := os.Open(targetPath)
if err != nil {
t.Fatalf("Can't open dir %v: %v", targetPath, err)
}
defer targetDir.Close()
names, err := targetDir.Readdirnames(-1)
if err != nil {
t.Fatalf("Can't read dir %v: %v", targetPath, err)
}
for _, name := range names {
err = os.RemoveAll(filepath.Join(targetPath, name))
if err != nil {
t.Fatalf("Can't remove file %v: %v", name, err)
}
}
for _, fileName := range fileNames {
destinationPath := filepath.Join(targetPath, fileName)
sourceFile, err := os.Open(filepath.Join(sourcePath, fileName))
if err != nil {
t.Fatalf("Can't open file %v: %v", fileName, err)
}
defer sourceFile.Close()
destinationFile, err := os.Create(destinationPath)
if err != nil {
t.Fatalf("Can't create file %v: %v", destinationFile, err)
}
defer destinationFile.Close()
_, err = io.Copy(destinationFile, sourceFile)
if err != nil {
t.Fatalf("Can't copy file %v to %v: %v", sourceFile, destinationFile, err)
}
}
}
func createTmpDir(t *testing.T) string {
t.Helper()
// Create a temp directory. Passing an empty string for the first argument
// uses the system temp directory.
dir, err := os.MkdirTemp("", "filewatcher*")
if err != nil {
t.Fatalf("os.MkdirTemp() failed: %v", err)
}
t.Logf("Using tmpdir: %s", dir)
return dir
}

View File

@@ -0,0 +1,588 @@
/*
*
* Copyright 2021 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 (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"math/big"
"net"
"net/netip"
"os"
"path"
"strings"
"testing"
"time"
"google.golang.org/grpc/security/advancedtls/testdata"
)
func TestUnsupportedCRLs(t *testing.T) {
crlBytesSomeReasons := []byte(`-----BEGIN X509 CRL-----
MIIEeDCCA2ACAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVVMxHjAcBgNV
BAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczETMBEGA1UEAxMKR1RTIENBIDFPMRcN
MjEwNDI2MTI1OTQxWhcNMjEwNTA2MTE1OTQwWjCCAn0wIgIRAPOOG3L4VLC7CAAA
AABxQgEXDTIxMDQxOTEyMTgxOFowIQIQUK0UwBZkVdQIAAAAAHFCBRcNMjEwNDE5
MTIxODE4WjAhAhBRIXBJaKoQkQgAAAAAcULHFw0yMTA0MjAxMjE4MTdaMCICEQCv
qQWUq5UxmQgAAAAAcULMFw0yMTA0MjAxMjE4MTdaMCICEQDdv5k1kKwKTQgAAAAA
cUOQFw0yMTA0MjExMjE4MTZaMCICEQDGIEfR8N9sEAgAAAAAcUOWFw0yMTA0MjEx
MjE4MThaMCECEBHgbLXlj5yUCAAAAABxQ/IXDTIxMDQyMTIzMDAyNlowIQIQE1wT
2GGYqKwIAAAAAHFD7xcNMjEwNDIxMjMwMDI5WjAiAhEAo/bSyDjpVtsIAAAAAHFE
txcNMjEwNDIyMjMwMDI3WjAhAhARdCrSrHE0dAgAAAAAcUS/Fw0yMTA0MjIyMzAw
MjhaMCECEHONohfWn3wwCAAAAABxRX8XDTIxMDQyMzIzMDAyOVowIgIRAOYkiUPA
os4vCAAAAABxRYgXDTIxMDQyMzIzMDAyOFowIQIQRNTow5Eg2gEIAAAAAHFGShcN
MjEwNDI0MjMwMDI2WjAhAhBX32dH4/WQ6AgAAAAAcUZNFw0yMTA0MjQyMzAwMjZa
MCICEQDHnUM1vsaP/wgAAAAAcUcQFw0yMTA0MjUyMzAwMjZaMCECEEm5rvmL8sj6
CAAAAABxRxQXDTIxMDQyNTIzMDAyN1owIQIQW16OQs4YQYkIAAAAAHFIABcNMjEw
NDI2MTI1NDA4WjAhAhAhSohpYsJtDQgAAAAAcUgEFw0yMTA0MjYxMjU0MDlaoGkw
ZzAfBgNVHSMEGDAWgBSY0fhuEOvPm+xgnxiQG6DrfQn9KzALBgNVHRQEBAICBngw
NwYDVR0cAQH/BC0wK6AmoCSGImh0dHA6Ly9jcmwucGtpLmdvb2cvR1RTMU8xY29y
ZS5jcmyBAf8wDQYJKoZIhvcNAQELBQADggEBADPBXbxVxMJ1HC7btXExRUpJHUlU
YbeCZGx6zj5F8pkopbmpV7cpewwhm848Fx4VaFFppZQZd92O08daEC6aEqoug4qF
z6ZrOLzhuKfpW8E93JjgL91v0FYN7iOcT7+ERKCwVEwEkuxszxs7ggW6OJYJNvHh
priIdmcPoiQ3ZrIRH0vE3BfUcNXnKFGATWuDkiRI0I4A5P7NiOf+lAuGZet3/eom
0chgts6sdau10GfeUpHUd4f8e93cS/QeLeG16z7LC8vRLstU3m3vrknpZbdGqSia
97w66mqcnQh9V0swZiEnVLmLufaiuDZJ+6nUzSvLqBlb/ei3T/tKV0BoKJA=
-----END X509 CRL-----`)
crlBytesIndirect := []byte(`-----BEGIN X509 CRL-----
MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU
ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg
Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2
MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG
EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0
MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2
MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV
BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j
BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/
BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG
SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS
TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG
NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq
XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF
6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3
qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4
-----END X509 CRL-----`)
var tests = []struct {
desc string
in []byte
}{
{
desc: "some reasons",
in: crlBytesSomeReasons,
},
{
desc: "indirect",
in: crlBytesIndirect,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
crl, err := parseRevocationList(tt.in)
if err != nil {
t.Fatal(err)
}
if _, err := parseCRLExtensions(crl); err == nil {
t.Error("expected error got ok")
}
})
}
}
func TestCheckCertRevocation(t *testing.T) {
dummyCrlFile := []byte(`-----BEGIN X509 CRL-----
MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU
ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg
Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2
MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG
EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0
MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2
MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV
BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j
BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/
BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG
SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS
TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG
NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq
XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF
6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3
qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4
-----END X509 CRL-----`)
crl, err := parseRevocationList(dummyCrlFile)
if err != nil {
t.Fatalf("parseRevocationList(dummyCrlFile) failed: %v", err)
}
crlExt := &CRL{certList: crl}
var revocationTests = []struct {
desc string
in x509.Certificate
revoked revocationStatus
}{
{
desc: "Single revoked",
in: x509.Certificate{
Issuer: pkix.Name{
Country: []string{"USA"},
Locality: []string{"here"},
Organization: []string{"us"},
CommonName: "Test1",
},
SerialNumber: big.NewInt(2),
CRLDistributionPoints: []string{"test"},
},
revoked: RevocationRevoked,
},
{
desc: "Revoked no entry issuer",
in: x509.Certificate{
Issuer: pkix.Name{
Country: []string{"USA"},
Locality: []string{"here"},
Organization: []string{"us"},
CommonName: "Test1",
},
SerialNumber: big.NewInt(3),
CRLDistributionPoints: []string{"test"},
},
revoked: RevocationRevoked,
},
{
desc: "Revoked new entry issuer",
in: x509.Certificate{
Issuer: pkix.Name{
Country: []string{"USA"},
Locality: []string{"here"},
Organization: []string{"us"},
CommonName: "Test2",
},
SerialNumber: big.NewInt(4),
CRLDistributionPoints: []string{"test"},
},
revoked: RevocationRevoked,
},
{
desc: "Single unrevoked",
in: x509.Certificate{
Issuer: pkix.Name{
Country: []string{"USA"},
Locality: []string{"here"},
Organization: []string{"us"},
CommonName: "Test2",
},
SerialNumber: big.NewInt(1),
CRLDistributionPoints: []string{"test"},
},
revoked: RevocationUnrevoked,
},
{
desc: "Single unrevoked Issuer",
in: x509.Certificate{
Issuer: crl.Issuer,
SerialNumber: big.NewInt(2),
CRLDistributionPoints: []string{"test"},
},
revoked: RevocationUnrevoked,
},
}
for _, tt := range revocationTests {
rawIssuer, err := asn1.Marshal(tt.in.Issuer.ToRDNSequence())
if err != nil {
t.Fatalf("asn1.Marshal(%v) failed: %v", tt.in.Issuer.ToRDNSequence(), err)
}
tt.in.RawIssuer = rawIssuer
t.Run(tt.desc, func(t *testing.T) {
rev, err := checkCertRevocation(&tt.in, crlExt)
if err != nil {
t.Errorf("checkCertRevocation(%v) err = %v", tt.in.Issuer, err)
} else if rev != tt.revoked {
t.Errorf("checkCertRevocation(%v(%v)) returned %v wanted %v",
tt.in.Issuer, tt.in.SerialNumber, rev, tt.revoked)
}
})
}
}
func makeChain(t *testing.T, name string) []*x509.Certificate {
t.Helper()
certChain := make([]*x509.Certificate, 0)
rest, err := os.ReadFile(name)
if err != nil {
t.Fatalf("os.ReadFile(%v) failed %v", name, err)
}
for len(rest) > 0 {
var block *pem.Block
block, rest = pem.Decode(rest)
c, err := x509.ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("ParseCertificate error %v", err)
}
t.Logf("Parsed Cert sub = %v iss = %v", c.Subject, c.Issuer)
certChain = append(certChain, c)
}
return certChain
}
func loadCRL(t *testing.T, path string) *CRL {
crl, err := ReadCRLFile(path)
if err != nil {
t.Fatalf("ReadCRLFile(%v) failed err = %v", path, err)
}
return crl
}
func checkRevocation(conn tls.ConnectionState, cfg RevocationOptions) error {
return checkChainRevocation(conn.VerifiedChains, cfg)
}
func TestVerifyCrl(t *testing.T) {
tamperedSignature := loadCRL(t, testdata.Path("crl/1.crl"))
// Change the signature so it won't verify
tamperedSignature.certList.Signature[0]++
tamperedContent := loadCRL(t, testdata.Path("crl/provider_crl_empty.pem"))
// Change the content so it won't find a match
tamperedContent.rawIssuer[0]++
verifyTests := []struct {
desc string
crl *CRL
certs []*x509.Certificate
cert *x509.Certificate
errWant string
}{
{
desc: "Pass intermediate",
crl: loadCRL(t, testdata.Path("crl/1.crl")),
certs: makeChain(t, testdata.Path("crl/unrevoked.pem")),
cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1],
errWant: "",
},
{
desc: "Pass leaf",
crl: loadCRL(t, testdata.Path("crl/2.crl")),
certs: makeChain(t, testdata.Path("crl/unrevoked.pem")),
cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[2],
errWant: "",
},
{
desc: "Fail wrong cert chain",
crl: loadCRL(t, testdata.Path("crl/3.crl")),
certs: makeChain(t, testdata.Path("crl/unrevoked.pem")),
cert: makeChain(t, testdata.Path("crl/revokedInt.pem"))[1],
errWant: "No certificates matched",
},
{
desc: "Fail no certs",
crl: loadCRL(t, testdata.Path("crl/1.crl")),
certs: []*x509.Certificate{},
cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1],
errWant: "No certificates matched",
},
{
desc: "Fail Tampered signature",
crl: tamperedSignature,
certs: makeChain(t, testdata.Path("crl/unrevoked.pem")),
cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1],
errWant: "verification failure",
},
{
desc: "Fail Tampered content",
crl: tamperedContent,
certs: makeChain(t, testdata.Path("crl/provider_client_trust_cert.pem")),
cert: makeChain(t, testdata.Path("crl/provider_client_trust_cert.pem"))[0],
errWant: "No certificates",
},
{
desc: "Fail CRL by malicious CA",
crl: loadCRL(t, testdata.Path("crl/provider_malicious_crl_empty.pem")),
certs: makeChain(t, testdata.Path("crl/provider_client_trust_cert.pem")),
cert: makeChain(t, testdata.Path("crl/provider_client_trust_cert.pem"))[0],
errWant: "verification error",
},
{
desc: "Fail KeyUsage without cRLSign bit",
crl: loadCRL(t, testdata.Path("crl/provider_malicious_crl_empty.pem")),
certs: makeChain(t, testdata.Path("crl/provider_malicious_client_trust_cert.pem")),
cert: makeChain(t, testdata.Path("crl/provider_malicious_client_trust_cert.pem"))[0],
errWant: "certificate can't be used",
},
}
for _, tt := range verifyTests {
t.Run(tt.desc, func(t *testing.T) {
err := verifyCRL(tt.crl, tt.certs)
switch {
case tt.errWant == "" && err != nil:
t.Errorf("Valid CRL did not verify err = %v", err)
case tt.errWant != "" && err == nil:
t.Error("Invalid CRL verified")
case tt.errWant != "" && !strings.Contains(err.Error(), tt.errWant):
t.Errorf("fetchIssuerCRL(_, %v, %v, _) = %v; want Contains(%v)", tt.cert.RawIssuer, tt.certs, err, tt.errWant)
}
})
}
}
func TestRevokedCert(t *testing.T) {
revokedIntChain := makeChain(t, testdata.Path("crl/revokedInt.pem"))
revokedLeafChain := makeChain(t, testdata.Path("crl/revokedLeaf.pem"))
validChain := makeChain(t, testdata.Path("crl/unrevoked.pem"))
rawCRLs := make([][]byte, 6)
for i := 1; i <= 6; i++ {
rawCRL, err := os.ReadFile(testdata.Path(fmt.Sprintf("crl/%d.crl", i)))
if err != nil {
t.Fatalf("readFile(%v) failed err = %v", fmt.Sprintf("crl/%d.crl", i), err)
}
rawCRLs = append(rawCRLs, rawCRL)
}
staticCRLProvider := NewStaticCRLProvider(rawCRLs)
directoryCRLProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{CRLDirectory: testdata.Path("crl")})
if err != nil {
t.Fatalf("NewFileWatcherCRLProvider: err = %v", err)
}
defer directoryCRLProvider.Close()
var revocationTests = []struct {
desc string
in tls.ConnectionState
revoked bool
denyUndetermined bool
}{
{
desc: "Single unrevoked",
in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain}},
revoked: false,
},
{
desc: "Single revoked intermediate",
in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedIntChain}},
revoked: true,
},
{
desc: "Single revoked leaf",
in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain}},
revoked: true,
},
{
desc: "Multi one revoked",
in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, revokedLeafChain}},
revoked: false,
},
{
desc: "Multi revoked",
in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain, revokedIntChain}},
revoked: true,
},
{
desc: "Multi unrevoked",
in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, validChain}},
revoked: false,
},
{
desc: "Undetermined revoked",
in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{
{&x509.Certificate{CRLDistributionPoints: []string{"test"}}},
}},
revoked: true,
denyUndetermined: true,
},
{
desc: "Undetermined allowed",
in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{
{&x509.Certificate{CRLDistributionPoints: []string{"test"}}},
}},
revoked: false,
},
}
for _, tt := range revocationTests {
t.Run(fmt.Sprintf("%v with x509 crl dir", tt.desc), func(t *testing.T) {
err := checkRevocation(tt.in, RevocationOptions{
CRLProvider: directoryCRLProvider,
DenyUndetermined: tt.denyUndetermined,
})
t.Logf("checkRevocation err = %v", err)
if tt.revoked && err == nil {
t.Error("Revoked certificate chain was allowed")
} else if !tt.revoked && err != nil {
t.Error("Unrevoked certificate not allowed")
}
})
t.Run(fmt.Sprintf("%v with static provider", tt.desc), func(t *testing.T) {
err := checkRevocation(tt.in, RevocationOptions{
DenyUndetermined: tt.denyUndetermined,
CRLProvider: staticCRLProvider,
})
t.Logf("checkRevocation err = %v", err)
if tt.revoked && err == nil {
t.Error("Revoked certificate chain was allowed")
} else if !tt.revoked && err != nil {
t.Error("Unrevoked certificate not allowed")
}
})
}
}
func setupTLSConn(t *testing.T) (net.Listener, *x509.Certificate, *ecdsa.PrivateKey) {
t.Helper()
templ := x509.Certificate{
SerialNumber: big.NewInt(5),
BasicConstraintsValid: true,
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour),
IsCA: true,
Subject: pkix.Name{CommonName: "test-cert"},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
IPAddresses: []net.IP{netip.MustParseAddr("::1").AsSlice()},
CRLDistributionPoints: []string{"http://static.corp.google.com/crl/campus-sln/borg"},
}
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("ecdsa.GenerateKey failed err = %v", err)
}
rawCert, err := x509.CreateCertificate(rand.Reader, &templ, &templ, key.Public(), key)
if err != nil {
t.Fatalf("x509.CreateCertificate failed err = %v", err)
}
cert, err := x509.ParseCertificate(rawCert)
if err != nil {
t.Fatalf("x509.ParseCertificate failed err = %v", err)
}
srvCfg := tls.Config{
Certificates: []tls.Certificate{
{
Certificate: [][]byte{cert.Raw},
PrivateKey: key,
},
},
}
l, err := tls.Listen("tcp6", "[::1]:0", &srvCfg)
if err != nil {
t.Fatalf("tls.Listen failed err = %v", err)
}
return l, cert, key
}
// TestVerifyConnection will setup a client/server connection and check revocation in the real TLS dialer
func TestVerifyConnection(t *testing.T) {
lis, cert, key := setupTLSConn(t)
defer func() {
lis.Close()
}()
var handshakeTests = []struct {
desc string
revoked []pkix.RevokedCertificate
success bool
}{
{
desc: "Empty CRL",
revoked: []pkix.RevokedCertificate{},
success: true,
},
{
desc: "Revoked Cert",
revoked: []pkix.RevokedCertificate{
{
SerialNumber: cert.SerialNumber,
RevocationTime: time.Now(),
},
},
success: false,
},
}
for _, tt := range handshakeTests {
t.Run(tt.desc, func(t *testing.T) {
// Accept one connection.
go func() {
conn, err := lis.Accept()
if err != nil {
t.Errorf("tls.Accept failed err = %v", err)
} else {
conn.Write([]byte("Hello, World!"))
conn.Close()
}
}()
dir, err := os.MkdirTemp("", "crl_dir")
if err != nil {
t.Fatalf("os.MkdirTemp failed err = %v", err)
}
defer os.RemoveAll(dir)
template := &x509.RevocationList{
RevokedCertificates: tt.revoked,
ThisUpdate: time.Now(),
NextUpdate: time.Now().Add(time.Hour),
Number: big.NewInt(1),
}
crl, err := x509.CreateRevocationList(rand.Reader, template, cert, key)
if err != nil {
t.Fatalf("templ.CreateRevocationList failed err = %v", err)
}
err = os.WriteFile(path.Join(dir, fmt.Sprintf("%s.r0", cert.Subject.ToRDNSequence())), crl, 0777)
if err != nil {
t.Fatalf("os.WriteFile failed err = %v", err)
}
cp := x509.NewCertPool()
cp.AddCert(cert)
provider, err := NewFileWatcherCRLProvider(FileWatcherOptions{CRLDirectory: dir})
if err != nil {
t.Errorf("NewFileWatcherCRLProvider: err = %v", err)
}
defer provider.Close()
cliCfg := tls.Config{
RootCAs: cp,
VerifyConnection: func(cs tls.ConnectionState) error {
return checkRevocation(cs, RevocationOptions{CRLProvider: provider})
},
}
conn, err := tls.Dial(lis.Addr().Network(), lis.Addr().String(), &cliCfg)
t.Logf("tls.Dial err = %v", err)
if tt.success && err != nil {
t.Errorf("Expected success got err = %v", err)
}
if !tt.success && err == nil {
t.Error("Expected error, but got success")
}
if err == nil {
conn.Close()
}
})
}
}

View File

@@ -0,0 +1,25 @@
# Credential Reloading From Files
Credential reloading is a feature supported in the advancedtls library.
A very common way to achieve this is to reload from files.
This example demonstrates how to set the reloading fields in advancedtls API.
Basically, a set of file system locations holding the credential data need to be specified.
Once the credential data needs to be updated, users just change the credential data in the file system, and gRPC will pick up the changes automatically.
A couple of things to note:
1. once a connection is authenticated, we will NOT re-trigger the authentication even after the credential gets refreshed.
2. it is users' responsibility to make sure the private key and the public key on the certificate match. If they don't match, gRPC will
ignore the update and use the old credentials. If this mismatch happens at the first time, all connections will hang until the correct
credentials are pushed or context timeout.
## Try it
In directory `security/advancedtls/examples`:
```
go run server/main.go
```
```
go run client/main.go
```

View File

@@ -0,0 +1,111 @@
/*
*
* Copyright 2020 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.
*
*/
// The client demonstrates how to use the credential reloading feature in
// advancedtls to make a mTLS connection to the server.
package main
import (
"context"
"flag"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"google.golang.org/grpc/security/advancedtls"
"google.golang.org/grpc/security/advancedtls/testdata"
)
var address = "localhost:50051"
const (
// Default timeout for normal connections.
defaultTimeout = 2 * time.Second
// Intervals that set to monitor the credential updates.
credRefreshingInterval = 500 * time.Millisecond
)
func main() {
tmpKeyFile := flag.String("key", "", "temporary key file path")
tmpCertFile := flag.String("cert", "", "temporary cert file path")
flag.Parse()
if tmpKeyFile == nil || *tmpKeyFile == "" {
log.Fatalf("tmpKeyFile is nil or empty.")
}
if tmpCertFile == nil || *tmpCertFile == "" {
log.Fatalf("tmpCertFile is nil or empty.")
}
// Initialize credential struct using reloading API.
identityOptions := pemfile.Options{
CertFile: *tmpCertFile,
KeyFile: *tmpKeyFile,
RefreshDuration: credRefreshingInterval,
}
identityProvider, err := pemfile.NewProvider(identityOptions)
if err != nil {
log.Fatalf("pemfile.NewProvider(%v) failed: %v", identityOptions, err)
}
rootOptions := pemfile.Options{
RootFile: testdata.Path("client_trust_cert_1.pem"),
RefreshDuration: credRefreshingInterval,
}
rootProvider, err := pemfile.NewProvider(rootOptions)
if err != nil {
log.Fatalf("pemfile.NewProvider(%v) failed: %v", rootOptions, err)
}
options := &advancedtls.Options{
IdentityOptions: advancedtls.IdentityCertificateOptions{
IdentityProvider: identityProvider,
},
AdditionalPeerVerification: func(*advancedtls.HandshakeVerificationInfo) (*advancedtls.PostHandshakeVerificationResults, error) {
return &advancedtls.PostHandshakeVerificationResults{}, nil
},
RootOptions: advancedtls.RootCertificateOptions{
RootProvider: rootProvider,
},
VerificationType: advancedtls.CertVerification,
}
clientTLSCreds, err := advancedtls.NewClientCreds(options)
if err != nil {
log.Fatalf("advancedtls.NewClientCreds(%v) failed: %v", options, err)
}
// Make a connection using the credentials.
conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(clientTLSCreds))
if err != nil {
log.Fatalf("grpc.NewClient to %s failed: %v", address, err)
}
client := pb.NewGreeterClient(conn)
// Send the requests every 0.5s. The credential is expected to be changed in
// the bash script. We don't cancel the context nor call conn.Close() here,
// since the bash script is expected to close the client goroutine.
for {
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
_, err = client.SayHello(ctx, &pb.HelloRequest{Name: "gRPC"}, grpc.WaitForReady(true))
if err != nil {
log.Fatalf("client.SayHello failed: %v", err)
}
cancel()
time.Sleep(500 * time.Millisecond)
}
}

View File

@@ -0,0 +1,112 @@
/*
*
* Copyright 2020 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.
*
*/
// The server demonstrates how to use the credential reloading feature in
// advancedtls to serve mTLS connections from the client.
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/security/advancedtls"
"google.golang.org/grpc/security/advancedtls/testdata"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
var port = ":50051"
// Intervals that set to monitor the credential updates.
const credRefreshingInterval = 1 * time.Minute
type greeterServer struct {
pb.UnimplementedGreeterServer
}
// sayHello is a simple implementation of the pb.GreeterServer SayHello method.
func (greeterServer) SayHello(_ context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
flag.Parse()
fmt.Printf("server starting on port %s...\n", port)
identityOptions := pemfile.Options{
CertFile: testdata.Path("server_cert_1.pem"),
KeyFile: testdata.Path("server_key_1.pem"),
RefreshDuration: credRefreshingInterval,
}
identityProvider, err := pemfile.NewProvider(identityOptions)
if err != nil {
log.Fatalf("pemfile.NewProvider(%v) failed: %v", identityOptions, err)
}
defer identityProvider.Close()
rootOptions := pemfile.Options{
RootFile: testdata.Path("server_trust_cert_1.pem"),
RefreshDuration: credRefreshingInterval,
}
rootProvider, err := pemfile.NewProvider(rootOptions)
if err != nil {
log.Fatalf("pemfile.NewProvider(%v) failed: %v", rootOptions, err)
}
defer rootProvider.Close()
// Start a server and create a client using advancedtls API with Provider.
options := &advancedtls.Options{
IdentityOptions: advancedtls.IdentityCertificateOptions{
IdentityProvider: identityProvider,
},
RootOptions: advancedtls.RootCertificateOptions{
RootProvider: rootProvider,
},
RequireClientCert: true,
AdditionalPeerVerification: func(params *advancedtls.HandshakeVerificationInfo) (*advancedtls.PostHandshakeVerificationResults, error) {
// This message is to show the certificate under the hood is actually reloaded.
fmt.Printf("Client common name: %s.\n", params.Leaf.Subject.CommonName)
return &advancedtls.PostHandshakeVerificationResults{}, nil
},
VerificationType: advancedtls.CertVerification,
}
serverTLSCreds, err := advancedtls.NewServerCreds(options)
if err != nil {
log.Fatalf("advancedtls.NewServerCreds(%v) failed: %v", options, err)
}
s := grpc.NewServer(grpc.Creds(serverTLSCreds), grpc.KeepaliveParams(keepalive.ServerParameters{
// Set the max connection time to be 0.5 s to force the client to
// re-establish the connection, and hence re-invoke the verification
// callback.
MaxConnectionAge: 500 * time.Millisecond,
}))
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
pb.RegisterGreeterServer(s, greeterServer{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

View File

@@ -0,0 +1,114 @@
#!/bin/bash
#
# Copyright 2020 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.
#
set +e
export TMPDIR=$(mktemp -d)
trap "rm -rf ${TMPDIR}" EXIT
clean () {
for i in {1..10}; do
jobs -p | xargs -n1 pkill -P
# A simple "wait" just hangs sometimes. Running `jobs` seems to help.
sleep 1
if jobs | read; then
return
fi
done
echo "$(tput setaf 1) clean failed to kill tests $(tput sgr 0)"
jobs
pstree
rm ${CLIENT_LOG}
rm ${SERVER_LOG}
rm ${KEY_FILE_PATH}
rm ${CERT_FILE_PATH}
exit 1
}
fail () {
echo "$(tput setaf 1) $1 $(tput sgr 0)"
clean
exit 1
}
pass () {
echo "$(tput setaf 2) $1 $(tput sgr 0)"
}
EXAMPLES=(
"credential_reloading_from_files"
)
declare -a EXPECTED_SERVER_OUTPUT=("Client common name: foo.bar.hoo.com" "Client common name: foo.bar.another.client.com")
cd ./security/advancedtls/examples
for example in ${EXAMPLES[@]}; do
echo "$(tput setaf 4) testing: ${example} $(tput sgr 0)"
KEY_FILE_PATH=$(mktemp)
cat ../testdata/client_key_1.pem > ${KEY_FILE_PATH}
CERT_FILE_PATH=$(mktemp)
cat ../testdata/client_cert_1.pem > ${CERT_FILE_PATH}
# Build server.
if ! go build -o /dev/null ./${example}/*server/*.go; then
fail "failed to build server"
else
pass "successfully built server"
fi
# Build client.
if ! go build -o /dev/null ./${example}/*client/*.go; then
fail "failed to build client"
else
pass "successfully built client"
fi
# Start server.
SERVER_LOG="$(mktemp)"
go run ./$example/*server/*.go &> $SERVER_LOG &
# Run client binary.
CLIENT_LOG="$(mktemp)"
go run ${example}/*client/*.go -key=${KEY_FILE_PATH} -cert=${CERT_FILE_PATH} &> $CLIENT_LOG &
# Wait for the client to send some requests using old credentials.
sleep 4s
# Switch to the new credentials.
cat ../testdata/another_client_key_1.pem > ${KEY_FILE_PATH}
cat ../testdata/another_client_cert_1.pem > ${CERT_FILE_PATH}
# Wait for the client to send some requests using new credentials.
sleep 4s
# Check server log for expected output.
for output in "${EXPECTED_SERVER_OUTPUT[@]}"; do
if ! grep -q "$output" $SERVER_LOG; then
fail "server log missing output: $output
got server log:
$(cat $SERVER_LOG)
"
else
pass "server log contains expected output: $output"
fi
done
clean
done

View File

@@ -0,0 +1,26 @@
module google.golang.org/grpc/security/advancedtls/examples
go 1.25.0
require (
google.golang.org/grpc v1.83.0
google.golang.org/grpc/examples v0.0.0-20260818050025-b6685c50ff98
google.golang.org/grpc/security/advancedtls v1.0.0
)
require (
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.41.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 // indirect
google.golang.org/protobuf v1.36.12 // indirect
)
replace google.golang.org/grpc => ../../..
replace google.golang.org/grpc/examples => ../../../examples
replace google.golang.org/grpc/security/advancedtls => ../

View File

@@ -0,0 +1,30 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spiffe/go-spiffe/v2 v2.8.1 h1:eXZMLsu+3MLEPJyGJkolqtVrteZfQdUpOWj6LTiDl/E=
github.com/spiffe/go-spiffe/v2 v2.8.1/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 h1:utmqiUzZAEINbLmRyAt1QjVkMyLQ+lvIwCpLaB1rZfw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,24 @@
module google.golang.org/grpc/security/advancedtls
go 1.25.0
require (
github.com/google/go-cmp v0.7.0
golang.org/x/crypto v0.55.0
google.golang.org/grpc v1.83.0
google.golang.org/grpc/examples v0.0.0-20250407062114-b368379ef8f6
)
require (
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.41.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 // indirect
google.golang.org/protobuf v1.36.12 // indirect
)
replace google.golang.org/grpc => ../../
replace google.golang.org/grpc/examples => ../../examples

View File

@@ -0,0 +1,30 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spiffe/go-spiffe/v2 v2.8.1 h1:eXZMLsu+3MLEPJyGJkolqtVrteZfQdUpOWj6LTiDl/E=
github.com/spiffe/go-spiffe/v2 v2.8.1/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 h1:utmqiUzZAEINbLmRyAt1QjVkMyLQ+lvIwCpLaB1rZfw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2020 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 testutils contains helper functions for advancedtls.
package testutils
import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"google.golang.org/grpc/security/advancedtls/testdata"
)
// CertStore contains all the certificates used in the integration tests.
type CertStore struct {
// ClientCert1 is the certificate sent by client to prove its identity.
// It is trusted by ServerTrust1.
ClientCert1 tls.Certificate
// ClientCert2 is the certificate sent by client to prove its identity.
// It is trusted by ServerTrust2.
ClientCert2 tls.Certificate
// ClientCertForCRL is the certificate sent by client to prove its identity.
// It is trusted by ServerTrust3. Used in CRL tests
ClientCertForCRL tls.Certificate
// ServerCert1 is the certificate sent by server to prove its identity.
// It is trusted by ClientTrust1.
ServerCert1 tls.Certificate
// ServerCert2 is the certificate sent by server to prove its identity.
// It is trusted by ClientTrust2.
ServerCert2 tls.Certificate
// ServerCertForCRL is a revoked certificate
// (this info is stored in provider_crl_server_revoked.pem).
ServerCertForCRL tls.Certificate
// ServerPeer3 is the certificate sent by server to prove its identity.
ServerPeer3 tls.Certificate
// ServerPeerLocalhost1 is the certificate sent by server to prove its
// identity. It has "localhost" as its common name, and is trusted by
// ClientTrust1.
ServerPeerLocalhost1 tls.Certificate
// ClientTrust1 is the root certificate used on the client side.
ClientTrust1 *x509.CertPool
// ClientTrust2 is the root certificate used on the client side.
ClientTrust2 *x509.CertPool
// ClientTrust3 is the root certificate used on the client side.
ClientTrust3 *x509.CertPool
// ServerTrust1 is the root certificate used on the server side.
ServerTrust1 *x509.CertPool
// ServerTrust2 is the root certificate used on the server side.
ServerTrust2 *x509.CertPool
// ServerTrust2 is the root certificate used on the server side.
ServerTrust3 *x509.CertPool
}
func readTrustCert(fileName string) (*x509.CertPool, error) {
trustData, err := os.ReadFile(fileName)
if err != nil {
return nil, err
}
trustPool := x509.NewCertPool()
if !trustPool.AppendCertsFromPEM(trustData) {
return nil, fmt.Errorf("error loading trust certificates")
}
return trustPool, nil
}
// LoadCerts function is used to load test certificates at the beginning of
// each integration test.
func (cs *CertStore) LoadCerts() error {
var err error
if cs.ClientCert1, err = tls.LoadX509KeyPair(testdata.Path("client_cert_1.pem"), testdata.Path("client_key_1.pem")); err != nil {
return err
}
if cs.ClientCert2, err = tls.LoadX509KeyPair(testdata.Path("client_cert_2.pem"), testdata.Path("client_key_2.pem")); err != nil {
return err
}
if cs.ClientCertForCRL, err = tls.LoadX509KeyPair(testdata.Path("crl/provider_client_cert.pem"), testdata.Path("crl/provider_client_cert.key")); err != nil {
return err
}
if cs.ServerCert1, err = tls.LoadX509KeyPair(testdata.Path("server_cert_1.pem"), testdata.Path("server_key_1.pem")); err != nil {
return err
}
if cs.ServerCert2, err = tls.LoadX509KeyPair(testdata.Path("server_cert_2.pem"), testdata.Path("server_key_2.pem")); err != nil {
return err
}
if cs.ServerCertForCRL, err = tls.LoadX509KeyPair(testdata.Path("crl/provider_server_cert.pem"), testdata.Path("crl/provider_server_cert.key")); err != nil {
return err
}
if cs.ServerPeer3, err = tls.LoadX509KeyPair(testdata.Path("server_cert_3.pem"), testdata.Path("server_key_3.pem")); err != nil {
return err
}
if cs.ServerPeerLocalhost1, err = tls.LoadX509KeyPair(testdata.Path("server_cert_localhost_1.pem"), testdata.Path("server_key_localhost_1.pem")); err != nil {
return err
}
if cs.ClientTrust1, err = readTrustCert(testdata.Path("client_trust_cert_1.pem")); err != nil {
return err
}
if cs.ClientTrust2, err = readTrustCert(testdata.Path("client_trust_cert_2.pem")); err != nil {
return err
}
if cs.ClientTrust3, err = readTrustCert(testdata.Path("crl/provider_client_trust_cert.pem")); err != nil {
return err
}
if cs.ServerTrust1, err = readTrustCert(testdata.Path("server_trust_cert_1.pem")); err != nil {
return err
}
if cs.ServerTrust2, err = readTrustCert(testdata.Path("server_trust_cert_2.pem")); err != nil {
return err
}
if cs.ServerTrust3, err = readTrustCert(testdata.Path("crl/provider_server_trust_cert.pem")); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,51 @@
/*
*
* Copyright 2020 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 (
"crypto/tls"
"fmt"
)
// buildGetCertificates returns the certificate that matches the SNI field
// for the given ClientHelloInfo, defaulting to the first element of o.GetCertificates.
func buildGetCertificates(clientHello *tls.ClientHelloInfo, o *Options) (*tls.Certificate, error) {
if o.IdentityOptions.GetIdentityCertificatesForServer == nil {
return nil, fmt.Errorf("function GetCertificates must be specified")
}
certificates, err := o.IdentityOptions.GetIdentityCertificatesForServer(clientHello)
if err != nil {
return nil, err
}
if len(certificates) == 0 {
return nil, fmt.Errorf("no certificates configured")
}
// If users pass in only one certificate, return that certificate.
if len(certificates) == 1 {
return certificates[0], nil
}
// Choose the SNI certificate using SupportsCertificate.
for _, cert := range certificates {
if err := clientHello.SupportsCertificate(cert); err == nil {
return cert, nil
}
}
// If nothing matches, return the first certificate.
return certificates[0], nil
}

48
security/advancedtls/testdata/README.md vendored Normal file
View File

@@ -0,0 +1,48 @@
About This Directory
-------------
This testdata directory contains the certificates used in the tests of package advancedtls.
How to Generate Test Certificates Using OpenSSL
-------------
Supposing we are going to create a `subject_cert.pem` that is trusted by `ca_cert.pem`, here are the
commands we run:
1. Generate the private key, `ca_key.pem`, and the cert `ca_cert.pem`, for the CA:
```
$ openssl req -x509 -newkey rsa:4096 -keyout ca_key.pem -out ca_cert.pem -nodes -days $DURATION_DAYS
```
2. Generate a private key `subject_key.pem` for the subject:
```
$ openssl genrsa -out subject_key.pem 4096
```
3. Generate a CSR `csr.pem` using `subject_key.pem`:
```
$ openssl req -new -key subject_key.pem -out csr.pem
```
For some cases, we might want to add some extra SAN fields in `subject_cert.pem`.
In those cases, we can create a configuration file(for example, localhost-openssl.cnf), and do the following:
```
$ openssl req -new -key subject_key.pem -out csr.pem -config $CONFIG_FILE_NAME
```
4. Use `ca_key.pem` and `ca_cert.pem` to sign `csr.pem`, and get a certificate, `subject_cert.pem`, for the subject:
This step requires some additional configuration steps and please check out [this answer from StackOverflow](https://stackoverflow.com/a/21340898) for more.
```
$ openssl ca -config openssl-ca.cnf -policy signing_policy -extensions signing_req -out subject_cert.pem -in csr.pem -keyfile ca_key.pem -cert ca_cert.pem
```
Please see an example configuration template at `openssl-ca.cnf`.
5. Verify the `subject_cert.pem` is trusted by `ca_cert.pem`:
```
$ openssl verify -verbose -CAfile ca_cert.pem subject_cert.pem
```

View File

@@ -0,0 +1,122 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=VA, O=Internet Widgits Pty Ltd, CN=foo.bar.hoo.ca.com
Validity
Not Before: Nov 7 17:11:57 2020 GMT
Not After : Mar 25 17:11:57 2048 GMT
Subject: C=US, ST=CA, O=Internet Widgits Pty Ltd, CN=foo.bar.another.client.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:ec:00:a0:69:4b:11:ae:30:21:15:64:4d:c3:cb:
94:67:58:44:9a:5e:ca:c3:82:75:eb:6f:8d:b7:33:
ca:e0:69:5f:98:1f:17:e5:7d:04:dd:f9:af:63:3b:
84:0e:48:2e:23:fc:b4:de:02:64:ad:0c:8f:6b:70:
15:fe:00:98:47:cb:c3:ff:5c:fb:b4:22:cd:36:18:
6e:4a:6d:de:0e:7a:90:d8:3d:af:76:1d:17:07:67:
79:7f:ab:59:af:1f:37:99:b9:46:ea:db:b3:a4:a2:
8a:e9:b3:41:83:04:71:82:2f:88:51:5d:b3:ba:7d:
72:d3:ef:97:47:89:b4:d0:78:f1:d6:ef:8a:09:94:
19:54:ae:08:8c:77:49:2f:6b:c6:c9:56:0c:fa:ec:
77:76:f0:83:d8:83:d0:4d:b5:f5:d9:e3:12:85:5c:
64:c5:82:60:73:20:fa:8a:36:00:f4:f2:bc:cf:48:
08:94:5f:2b:39:88:4c:56:f5:65:30:67:41:78:99:
7e:26:f5:ab:e7:3d:b0:a3:8c:55:c5:e1:12:39:23:
00:68:88:c2:b1:43:2f:61:7c:d4:08:35:52:28:5d:
93:3e:84:c9:8d:0a:37:df:75:06:f4:ae:1e:2a:1d:
e3:f9:0f:26:80:ad:4e:6a:c3:6a:0c:2e:0f:31:0f:
b2:39:b0:98:aa:1e:96:c7:0b:a3:70:6c:35:52:82:
56:0f:27:e1:07:d5:89:a6:97:58:97:6f:9f:4f:db:
0e:e6:ef:fd:62:f4:c3:d5:bb:af:ee:f7:5b:26:6d:
79:c0:46:71:94:d1:6f:ea:2a:61:78:1c:c5:09:4d:
93:35:bf:6d:1d:26:49:2e:84:9e:8f:48:9c:93:e0:
ef:bb:c7:85:f1:20:2b:8e:1e:da:18:76:db:c6:42:
4f:87:ed:e9:44:90:ab:99:ea:d7:3a:89:f9:be:b5:
3a:b8:5b:50:ec:7c:54:ce:d9:ff:9e:94:30:97:25:
e5:ce:13:b4:ee:06:56:39:0b:d4:51:77:a2:e8:3f:
b4:e7:60:2a:03:44:3a:93:ed:8a:f0:d7:90:f7:92:
fe:e9:19:57:37:24:20:f5:9c:64:d0:fc:13:d1:3e:
a5:1d:ea:14:23:8d:f6:ee:c0:5c:f4:27:82:87:90:
6c:80:6d:ff:6f:1d:ab:2e:83:0f:73:4a:78:3d:40:
7f:e6:f9:55:b4:ea:e8:83:f7:86:e0:c8:c1:d9:96:
a0:7d:38:08:72:88:d2:c5:90:e3:3e:2a:f9:64:26:
52:6e:20:79:20:ea:99:1b:75:65:4b:12:93:22:09:
f9:ec:1b:af:74:a4:3c:a3:df:07:d0:50:95:ad:7e:
55:60:25
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
D5:43:51:8B:A8:4C:84:D0:C8:DE:29:14:1B:15:7A:62:01:ED:FF:EC
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
61:a0:89:19:3e:e8:3d:35:bf:6e:5d:0c:d0:ec:36:85:d4:27:
41:6b:21:0e:5e:ed:e9:e2:17:ef:2b:19:5d:1b:4a:52:7f:e5:
66:db:24:40:4d:cb:f8:9d:92:5e:10:67:68:d0:f8:56:ab:da:
3c:8c:4f:02:07:9e:22:49:45:de:bd:d2:92:e8:ef:9d:f2:68:
97:0d:10:43:c7:b7:73:9e:76:14:7b:1d:b5:8e:df:df:7b:0d:
b3:48:36:21:98:67:3f:41:55:c7:26:09:99:fc:bc:92:8c:9a:
ac:64:76:9d:b2:b1:ca:41:e5:59:79:8d:76:f1:b4:bb:2e:28:
8b:bb:73:c4:83:0e:f9:6e:62:2c:49:d8:00:e5:87:c7:53:1b:
45:ec:29:a4:1c:20:82:bf:d1:f4:a4:23:98:b2:1e:41:a0:ee:
1a:0c:7e:bd:00:84:f5:17:05:1e:a6:7b:fd:75:ee:b9:6d:6b:
31:2a:0d:97:fa:57:86:57:25:44:8c:5e:e9:bc:78:30:13:77:
70:87:78:e1:7a:fb:26:db:b9:95:d0:04:c9:93:26:a2:96:b5:
2b:d5:d3:61:22:b3:ca:31:51:15:6d:51:0a:fb:22:6e:ca:16:
61:a5:91:9c:e0:39:de:cb:ad:fe:23:51:15:34:42:ae:ac:82:
80:9a:12:f6:f4:a6:8b:65:b5:f3:21:7b:78:ab:e2:53:83:1c:
08:83:a5:1a:26:f4:a8:f2:cd:19:d1:85:99:99:45:c8:46:35:
48:c4:fa:44:80:b0:04:67:48:36:c9:5e:44:fa:41:6e:a7:f2:
eb:22:9e:f8:d3:f2:0d:79:1b:33:78:c4:d0:60:e0:93:5f:69:
6f:cf:f0:df:04:3d:5b:b3:ac:09:30:ae:32:ea:0f:9f:7b:2c:
69:bf:f3:fd:6d:7c:a2:25:dc:15:82:df:e0:a5:84:64:39:26:
43:28:0f:cb:2e:7f:fe:9e:c2:7e:20:d3:ca:6d:96:1c:0e:e1:
b1:ac:0d:2e:4d:97:6f:14:39:65:1a:65:9f:13:2d:0a:28:f3:
2e:6d:30:af:c6:82:cb:33:20:86:17:de:52:0c:9b:a1:fb:2a:
59:ca:b1:18:b7:56:06:52:46:9c:8d:36:c6:2e:61:f2:02:2b:
ec:72:15:40:3a:1f:e9:ad:ca:5e:19:aa:1a:73:b5:d4:4d:b9:
d5:b2:24:8c:b6:eb:e6:cc:b2:0e:12:c6:26:52:f7:7a:e7:2c:
0b:1f:bc:24:1c:76:66:bb:2f:1d:ec:26:9e:53:5b:12:0a:e4:
ae:e3:bc:dd:21:a0:aa:cc:e4:9a:1d:8e:f7:16:36:b2:ed:97:
81:5a:48:22:a8:f6:6a:2d
-----BEGIN CERTIFICATE-----
MIIFkTCCA3mgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCVkExITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEb
MBkGA1UEAwwSZm9vLmJhci5ob28uY2EuY29tMB4XDTIwMTEwNzE3MTE1N1oXDTQ4
MDMyNTE3MTE1N1owYjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMSEwHwYDVQQK
DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxIzAhBgNVBAMMGmZvby5iYXIuYW5v
dGhlci5jbGllbnQuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
7ACgaUsRrjAhFWRNw8uUZ1hEml7Kw4J162+NtzPK4GlfmB8X5X0E3fmvYzuEDkgu
I/y03gJkrQyPa3AV/gCYR8vD/1z7tCLNNhhuSm3eDnqQ2D2vdh0XB2d5f6tZrx83
mblG6tuzpKKK6bNBgwRxgi+IUV2zun1y0++XR4m00Hjx1u+KCZQZVK4IjHdJL2vG
yVYM+ux3dvCD2IPQTbX12eMShVxkxYJgcyD6ijYA9PK8z0gIlF8rOYhMVvVlMGdB
eJl+JvWr5z2wo4xVxeESOSMAaIjCsUMvYXzUCDVSKF2TPoTJjQo333UG9K4eKh3j
+Q8mgK1OasNqDC4PMQ+yObCYqh6WxwujcGw1UoJWDyfhB9WJppdYl2+fT9sO5u/9
YvTD1buv7vdbJm15wEZxlNFv6ipheBzFCU2TNb9tHSZJLoSej0ick+Dvu8eF8SAr
jh7aGHbbxkJPh+3pRJCrmerXOon5vrU6uFtQ7HxUztn/npQwlyXlzhO07gZWOQvU
UXei6D+052AqA0Q6k+2K8NeQ95L+6RlXNyQg9Zxk0PwT0T6lHeoUI4327sBc9CeC
h5BsgG3/bx2rLoMPc0p4PUB/5vlVtOrog/eG4MjB2ZagfTgIcojSxZDjPir5ZCZS
biB5IOqZG3VlSxKTIgn57BuvdKQ8o98H0FCVrX5VYCUCAwEAAaNaMFgwHQYDVR0O
BBYEFNVDUYuoTITQyN4pFBsVemIB7f/sMB8GA1UdIwQYMBaAFLQZCBz8ECPFMIYi
vMuxX63qel3xMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUA
A4ICAQBhoIkZPug9Nb9uXQzQ7DaF1CdBayEOXu3p4hfvKxldG0pSf+Vm2yRATcv4
nZJeEGdo0PhWq9o8jE8CB54iSUXevdKS6O+d8miXDRBDx7dznnYUex21jt/few2z
SDYhmGc/QVXHJgmZ/LySjJqsZHadsrHKQeVZeY128bS7LiiLu3PEgw75bmIsSdgA
5YfHUxtF7CmkHCCCv9H0pCOYsh5BoO4aDH69AIT1FwUepnv9de65bWsxKg2X+leG
VyVEjF7pvHgwE3dwh3jhevsm27mV0ATJkyailrUr1dNhIrPKMVEVbVEK+yJuyhZh
pZGc4Dney63+I1EVNEKurIKAmhL29KaLZbXzIXt4q+JTgxwIg6UaJvSo8s0Z0YWZ
mUXIRjVIxPpEgLAEZ0g2yV5E+kFup/LrIp740/INeRszeMTQYOCTX2lvz/DfBD1b
s6wJMK4y6g+feyxpv/P9bXyiJdwVgt/gpYRkOSZDKA/LLn/+nsJ+INPKbZYcDuGx
rA0uTZdvFDllGmWfEy0KKPMubTCvxoLLMyCGF95SDJuh+ypZyrEYt1YGUkacjTbG
LmHyAivschVAOh/prcpeGaoac7XUTbnVsiSMtuvmzLIOEsYmUvd65ywLH7wkHHZm
uy8d7CaeU1sSCuSu47zdIaCqzOSaHY73Fjay7ZeBWkgiqPZqLQ==
-----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,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,122 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 6 (0x6)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=CA, O=Internet Widgits Pty Ltd, CN=foo.bar.server2.trust.com
Validity
Not Before: Jan 9 22:47:15 2020 GMT
Not After : Oct 23 22:47:15 2293 GMT
Subject: C=US, ST=CA, O=Internet Widgits Pty Ltd, CN=foo.bar.client2.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:b9:3e:c6:3b:cb:d6:77:4b:17:d4:8b:91:27:f4:
62:01:60:8d:01:2f:0a:a8:b1:d6:e3:59:d6:25:3a:
a1:7f:2f:5d:ef:02:f9:6f:4f:72:db:75:ce:0b:22:
a2:05:7c:e0:7c:a3:d3:c8:fa:87:c0:6c:a9:47:00:
ed:52:2b:ba:95:36:36:1a:d3:59:1e:a7:30:a7:48:
38:7f:1a:7a:3f:84:cf:83:f0:fe:60:61:9e:c0:46:
ce:44:b5:37:83:ef:14:6c:9a:ea:3b:fe:37:8a:ab:
ea:28:59:43:f0:d7:1a:a0:57:a6:5e:a7:3f:46:95:
92:fb:44:77:68:ee:41:ca:57:1b:de:4c:80:ea:16:
b7:25:c5:b2:e5:d4:47:a7:bb:8d:f5:53:9d:a3:0e:
d0:eb:59:5e:7a:6d:8e:a1:8e:f3:b7:b1:4a:8b:f1:
8a:01:f1:e1:14:85:dc:91:ce:25:7a:fd:db:17:b8:
15:60:34:4b:f5:35:df:bd:22:65:b9:85:4a:7a:39:
74:c0:88:c9:15:61:62:a8:4b:b6:ae:87:0b:2d:5f:
2b:c6:13:c5:9c:1b:63:c0:23:73:6f:24:5e:e1:f9:
f5:ed:82:81:51:90:4a:08:7f:6e:4f:bd:27:00:b2:
b4:be:a8:0b:65:95:22:a4:c7:24:5b:07:5f:3c:66:
55:2d:af:ec:d3:f7:ca:e6:07:44:09:6f:da:a2:f3:
c9:4b:1f:9b:d7:e0:0c:6c:a0:be:4d:4c:6c:c5:3a:
bb:0d:a1:c4:82:75:42:ba:c0:10:d2:93:a4:0e:4e:
41:9a:c2:3c:68:ae:17:92:ec:4b:4f:ca:ef:09:7c:
b2:6d:16:31:15:31:67:78:02:0a:57:6b:60:4e:7f:
cb:0a:27:a5:cd:dd:d9:29:a5:a2:e8:d8:f5:e9:8c:
a3:16:72:9d:b9:94:3e:ef:b1:70:27:2e:16:0f:06:
f9:50:81:99:a2:aa:b2:74:d8:b9:24:0d:08:f4:ff:
16:c1:2b:32:ad:d1:7d:c2:db:ed:e5:8c:52:26:ed:
8c:04:af:86:9e:a1:5f:48:81:20:79:bc:57:58:25:
89:85:02:ba:e1:5f:66:e4:4a:30:2e:6d:3b:89:2c:
4f:e9:02:6a:e9:9e:b3:6c:7e:9d:1b:a9:37:3e:bf:
06:ec:ce:d6:d7:6e:e3:e2:5c:2a:fd:98:dd:4d:59:
e8:43:be:44:fe:ee:0a:64:fe:fc:e3:4d:88:23:27:
46:a7:f0:b5:80:c4:d8:2c:ad:02:a9:68:a7:d5:64:
74:b9:14:21:68:c9:f5:3c:62:73:ed:b2:be:10:89:
1f:d0:1d:1b:8a:ef:5e:6b:4b:08:15:25:4d:9c:b6:
f4:2a:0b
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
E0:27:7D:90:FC:81:7F:F3:EE:97:CE:65:A2:AD:D2:1E:CC:D5:2B:0F
X509v3 Authority Key Identifier:
keyid:63:88:EA:4D:D0:3E:EF:5E:F8:43:91:75:40:E4:16:AB:15:B3:32:B9
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Key Encipherment
Signature Algorithm: sha256WithRSAEncryption
8c:81:8f:65:38:2c:db:69:34:26:47:62:b7:5d:4e:67:41:c2:
67:b2:97:72:51:84:f5:73:8e:cf:9d:0f:a2:91:1e:ec:e4:72:
6f:08:da:26:06:c0:f0:11:fd:b8:ac:23:c7:cf:35:ac:d0:90:
e3:da:f0:8b:7b:55:16:00:5f:82:92:40:07:12:d1:ae:06:13:
c0:5d:7c:9b:64:d7:35:86:59:c3:8d:cd:b9:a8:17:03:2e:b5:
d4:8b:18:11:cf:8d:90:74:8f:12:f6:53:99:66:d8:50:b6:c6:
ef:c8:e3:bc:26:74:67:cb:6d:34:bd:c6:58:38:ef:4b:5e:56:
80:37:2d:25:64:31:96:6e:8d:13:ff:21:63:c9:ec:8f:b6:05:
5a:8b:b5:ae:88:50:af:00:c4:c7:9d:9b:88:a3:05:6c:63:85:
46:1a:b1:6b:32:11:cc:0c:a6:75:44:a2:39:c6:58:c8:2a:f8:
08:8c:9a:12:c2:49:e0:03:da:fa:f7:67:a3:7b:91:71:46:24:
71:83:3f:a9:a0:a9:4f:e5:77:9d:a4:49:2b:0e:69:dd:47:93:
b9:4d:82:3d:f7:12:b1:02:0e:ec:4c:98:76:c2:48:81:30:68:
7c:04:90:e7:a7:e5:0f:44:cf:48:e3:04:1b:9c:4a:0f:20:25:
ce:74:13:83:96:d8:78:69:a0:1c:e4:9e:8d:1b:0c:9f:e8:43:
29:72:82:96:98:6e:8e:8b:0c:0e:18:4e:dd:62:e8:e9:5c:77:
64:40:5b:c3:44:3d:21:0f:3f:ef:04:c8:83:f0:af:cc:be:9c:
b5:6b:32:c3:26:66:a0:06:bc:7b:b0:c8:54:8f:0a:d7:57:bb:
c7:d9:7a:7f:3e:61:ab:64:03:cc:32:44:a1:71:6f:9a:cc:80:
a6:e6:de:2d:8e:8a:2f:ca:bf:63:42:24:de:3f:c2:47:a4:e2:
fb:3d:6f:70:3f:6f:cb:bd:61:40:af:c9:59:75:99:39:9d:65:
e4:89:48:fc:14:1c:ad:03:fc:5f:a2:69:be:4d:a1:a3:ad:6b:
e7:f8:8d:13:64:f8:76:7d:04:af:61:f9:9c:39:68:68:99:bc:
ec:53:b9:d1:e7:f3:c2:c9:87:42:f0:26:8f:47:c3:6d:de:2a:
f5:df:b4:58:f2:1e:f5:6c:29:0b:dd:de:ea:1a:88:21:a4:d1:
bb:7f:54:c5:cd:75:71:4e:ef:d0:50:f8:ff:a2:0f:d5:02:fd:
51:52:86:b8:30:db:4f:e0:3b:f1:91:45:72:49:df:a4:17:97:
25:ca:12:9d:61:9d:29:2c:e4:5f:da:c7:3c:ee:4c:65:5d:2f:
38:a6:7d:8b:52:af:af:18
-----BEGIN CERTIFICATE-----
MIIFkzCCA3ugAwIBAgIBBjANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCQ0ExITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEi
MCAGA1UEAwwZZm9vLmJhci5zZXJ2ZXIyLnRydXN0LmNvbTAgFw0yMDAxMDkyMjQ3
MTVaGA8yMjkzMTAyMzIyNDcxNVowWzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNB
MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxHDAaBgNVBAMME2Zv
by5iYXIuY2xpZW50Mi5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQC5PsY7y9Z3SxfUi5En9GIBYI0BLwqosdbjWdYlOqF/L13vAvlvT3Lbdc4LIqIF
fOB8o9PI+ofAbKlHAO1SK7qVNjYa01kepzCnSDh/Gno/hM+D8P5gYZ7ARs5EtTeD
7xRsmuo7/jeKq+ooWUPw1xqgV6Zepz9GlZL7RHdo7kHKVxveTIDqFrclxbLl1Een
u431U52jDtDrWV56bY6hjvO3sUqL8YoB8eEUhdyRziV6/dsXuBVgNEv1Nd+9ImW5
hUp6OXTAiMkVYWKoS7auhwstXyvGE8WcG2PAI3NvJF7h+fXtgoFRkEoIf25PvScA
srS+qAtllSKkxyRbB188ZlUtr+zT98rmB0QJb9qi88lLH5vX4AxsoL5NTGzFOrsN
ocSCdUK6wBDSk6QOTkGawjxorheS7EtPyu8JfLJtFjEVMWd4AgpXa2BOf8sKJ6XN
3dkppaLo2PXpjKMWcp25lD7vsXAnLhYPBvlQgZmiqrJ02LkkDQj0/xbBKzKt0X3C
2+3ljFIm7YwEr4aeoV9IgSB5vFdYJYmFArrhX2bkSjAubTuJLE/pAmrpnrNsfp0b
qTc+vwbsztbXbuPiXCr9mN1NWehDvkT+7gpk/vzjTYgjJ0an8LWAxNgsrQKpaKfV
ZHS5FCFoyfU8YnPtsr4QiR/QHRuK715rSwgVJU2ctvQqCwIDAQABo1owWDAdBgNV
HQ4EFgQU4Cd9kPyBf/Pul85loq3SHszVKw8wHwYDVR0jBBgwFoAUY4jqTdA+7174
Q5F1QOQWqxWzMrkwCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEL
BQADggIBAIyBj2U4LNtpNCZHYrddTmdBwmeyl3JRhPVzjs+dD6KRHuzkcm8I2iYG
wPAR/bisI8fPNazQkOPa8It7VRYAX4KSQAcS0a4GE8BdfJtk1zWGWcONzbmoFwMu
tdSLGBHPjZB0jxL2U5lm2FC2xu/I47wmdGfLbTS9xlg470teVoA3LSVkMZZujRP/
IWPJ7I+2BVqLta6IUK8AxMedm4ijBWxjhUYasWsyEcwMpnVEojnGWMgq+AiMmhLC
SeAD2vr3Z6N7kXFGJHGDP6mgqU/ld52kSSsOad1Hk7lNgj33ErECDuxMmHbCSIEw
aHwEkOen5Q9Ez0jjBBucSg8gJc50E4OW2HhpoBzkno0bDJ/oQylygpaYbo6LDA4Y
Tt1i6Olcd2RAW8NEPSEPP+8EyIPwr8y+nLVrMsMmZqAGvHuwyFSPCtdXu8fZen8+
YatkA8wyRKFxb5rMgKbm3i2Oii/Kv2NCJN4/wkek4vs9b3A/b8u9YUCvyVl1mTmd
ZeSJSPwUHK0D/F+iab5NoaOta+f4jRNk+HZ9BK9h+Zw5aGiZvOxTudHn88LJh0Lw
Jo9Hw23eKvXftFjyHvVsKQvd3uoaiCGk0bt/VMXNdXFO79BQ+P+iD9UC/VFShrgw
20/gO/GRRXJJ36QXlyXKEp1hnSks5F/axzzuTGVdLzimfYtSr68Y
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAwz612LxzX7TlYKhzDoyc/ygsqbxoEouuPqp79bkts3P4nmTp
jibKNhFf+XM58BlV0br0Tiur7gE76/BfbrMkOcH18L1q0GxWz5YzXQVIxLWzPlWO
iY5recU7mRzjA2kPdKKXe7/EER3a18uHDZAlZCk+9mK8+aVW3lbhJ3dRGjDxiIkB
wsg1QNMuLTDv1947KBVKpKm68EDweToW+a5SMsNSrVOclAfVm2NQkP/xjP1OWbhe
CnObtLdg4XwHAlB080hpanR8spZwhhkvgkyVV6pMLzh1i5uhPn3d2r/SpKM6AhdD
NQpSA/X7GqFgKMPnQetKDPRDboFkukGNYUCXn+JnUXwtLxdyuaAnXPzjtqbe9B40
lSzFfxPEuyV2Pjs5tjbQYBcexwGcPWWalkzYTBCFMnbHblNkgMkzGkQ5p8dp02TD
TAYgVtLr2WVWAmXEunLbicQAP4n0ddVtg86tZvtz+I673AHAT4bBV0VoND9VHw7v
gj+aJhycjYheJ6u2uVinxbA2D5m62MyJQe2rJriKFhchZ7ZNg9HdU95nq3ajr/hg
mSlqCk/yrTJUaTOM8sqb1lnNjGnNP9OPBSjRKQS/st6YD51iE23+3r4txr7W+BDL
tbNPraRgNrMZKSm5tDddE+c2y/n6f55jfvMF7p7mTf/jRqR7HxJyibYQX70CAwEA
AQKCAgEAjT9s5yNOhEqmNssmkbwASEeUKCd5UxFiOUu06gvRmCWqE00F+iTt3Tes
qxZFMAHkKBqMa5EEjOavpvz6zWckKfS8LDGceLQoCX2sIvuTrVuWFN5og/NYpXue
piJTyT/UQpjt5kTRX2Ct1bgUOCe0JUYBmtXLyP9oXOmVcavMLJqD4jbb40Jb5E3i
9iaVHSJUwabFnWJ9LxqL3ee8f10xcjAEPAhlGmKgkg3DV2MSKOGIMThEMGN6nb6c
hAPqPi5erTIRsUYcgEZ9mUXXLPiigg1dmDvMLfelK0R7n6luhlTfvmt9331b4Cmw
Q4/DtTokr3e81qpPrj5F1Mlfsp+8EFd8Ucwtu37DFiwpqYpDeMYxbbVC1toa5QQy
6VRa7NQLTHXfRp6mmaf37KnganLYOqX0vF8LMIn2O11jEnfdAfqSkHY8JzvNG9OJ
71LO5FXa7VEfOGfu1lNXScFN3yqukjr2aPo8bd9hIIw4ZEvJtto+0hBBTF4ttJ+r
R5j+h764A6vqxBo8Oh60sahY7sYBD0BIZT/hmxqaEC1PUpPfveGzWcr6r/xb30ak
DhrbWsH2/St8NjCL/9u86K/KyQB8nDwOQlTC/gB+SxLCp9KcEG3HuNVMFtV/pic/
lzqChT9p+2/F+iv/aIb69FcBuGMfljdrsnnrc9954nco8sXgmDECggEBAOPbmWLb
vnCzZ8VdbqsxlXeF9Ype9tyINE8az9rG0A15tTXtHCwYQRpTi8PWevaTYtJvzj5Z
7DnMH0B56q8p+oMyEP8YkfOQK6OavW9ehSui1y7KSgjFsFXuXPUR6BnLPUZW/BuD
UHrbjspFREWZBrm2y0tOk2sYZirqg9r+Hl1yAZXeXXkAK+UdNygRuoG3by8Nemql
wA22pLu6J0dZ04AQX4ERdxJcTxLx3wf3tpFltSWdsJr6kuevNBcdvpq/Xc9M91bW
n8POxMWIBTZTC5nDTJd9nCip1J8jACFII5evr/L+O3Bwda4k/B277D/DVUtKhhcD
UBucDcLXQro6eHMCggEBANtb9ZBYw3R/JbZrNwShB379I5p2SN30mSbkNB+0PYYx
WNX5YQADFlulG5/spPD15dyHdYWDWI+c40ZXAKfgN12it6id+eUC5hbx4+N9E6yP
4+9mkvPiV2HpIOLSb/fDReJsE/d6l0Fwqh2xGCN6adLSa3DCy4q9IP9pIJHOAkeY
kdBQwtXH0xo9hM25/ZFnWGmhugRvllB2rPCEPFxhsuS1ExgEPLm1T8DnueQTGuEf
lAXUj/s+RVcGgHgQ/ONv9O6uEhmZYST+ZFu3sb2Rq6YwNUjbIiaMuzMgEHAdQ4C4
xYQDC0Bnf3Lt1iszvypwAxjPBcHhVeqzTL4l1sn4Kw8CggEAEK59Fk28LYgU6tAi
UAo7RRrblRvKuu6F1dzCpuOzS6lDaQVI8Ll92q2PJ/FF41N7AqkI0mvG7ZxSFWhX
lCdgncZGlEZ6OPivGTU09ThYS4+KbXSF4wqGFGR1DcQX1/uXKtUnc+QzOitk0s4r
Z2UCpwoI7CR+inKo2C9/I8NC+dhk4VH8SeWHUSjIZviVTPXe//Tep3wnCVn7yXqh
cYnUACYyt8JNk1yKtXpbt7uc9BwcHPrkeRQrOScMizy0PaQQ/CJIYWUpIS68HTIO
H6II0WMI8nZRvnBgjp4DXmxnnq1QFlwigeLZ2rv+cTbW3vwv/GkiVAD8FmlgYIld
60BonQKCAQBAP2fmFklxBoiKLE7Z+TwT0pqp8/kVoT12KaKmoojek/d7/GWPtlfH
Ec3Mgmgw9ySS+c3PBBBdR8s9X+AeS0qMD0uRhGubysSPdduUVp77jM1q4fUqn2GO
mNR7+ry2qaf/UD5s3qgMj64TsjnqskDqcZzsUvGAujI+/JCAhAEg7SvQAsd+C9/l
sJ0EEHSXMNixX5/3CqPQ/2FZtLFlMWxPFkX4Y81RayxnyLcmeP4Hb9NP/dkJ8kwm
2A2qnPckujbX7X35p3XPev7z6hKR/mdy7m284AnZlqCBseN+ouORgQzAxI94Fpg6
ljSDRM255ULS8leyWIhsjIVur/CACUK7AoIBAHRqbtOLnfrDS8VlI+V0GtNJXLVS
XDlgTtPNMaDWMKxVLFNwF8MeY5pf1QHNa399bOumZUlmRfZ+AcJYDTyfF366Eoh6
yatmoQKMJotsQWln9iGWv7wqTP7omrL+Y053R1ypdY4k/4Yf9ptykiCBIUwYqjxk
+NvIcf8r0cZZjsx7SlkjhGGhFHkeFewhbPm7o8bolZ26Nf/luNGuJSOzGac88Sq5
9jSKbkWTI4Rukw3n73AAKkdbLmGkIw81BnMbXH3bBoB+fdmILgIFx61D1QeCipOQ
WJIht2SLm8UXfYAQLGL2kQ2+C531uFvV+hzNA1H5KHj1Lo4BD2ogjjePdFY=
-----END RSA PRIVATE KEY-----

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,33 @@
-----BEGIN CERTIFICATE-----
MIIFpTCCA42gAwIBAgIURc12C7/2O090oCXCOxpatu7h4m8wDQYJKoZIhvcNAQEL
BQAwYTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMSEwHwYDVQQKDBhJbnRlcm5l
dCBXaWRnaXRzIFB0eSBMdGQxIjAgBgNVBAMMGWZvby5iYXIuY2xpZW50Mi50cnVz
dC5jb20wIBcNMjAwMTA5MjI0OTU1WhgPMjIyNTA1MTQyMjQ5NTVaMGExCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJDQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMSIwIAYDVQQDDBlmb28uYmFyLmNsaWVudDIudHJ1c3QuY29tMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxWqK9pdKrJUupCKUCcaVGWLq1wh6
9I8YhLuWv73R15FbImFeh3085o009a4symIMSgfn8iPrN97WAMn4OSxAmJfSs0FA
DBmfpf0JOloFM5GHYVgGpdkFCiEjnJ+eTvIxRwsvbeT9EsLkeOVb8syr9bp/w5ZW
oh5Su9b7pwpAanQx67dxq2lCndVjxZLKgAXO23m70xoFOKwVaynxcUdnYVskFy30
SRhB9h0w7I0L1pb5F1BTrsMgBLtrg81JCQzdmgoTKnn8AHDnA+rwe7ushXE3eCrm
Uxj4n2OYc2siSXBQFyUa/x18kgubS/FPJNHYFPqnyw+g+yk9hraq3OQ8XwHA03eg
1TZkttQwfUV3g2gywDaC6e2PGl2q8+h1g/7kaSu9yiihlMfgQoa7cmC+j1MKAgki
FEkyuQtYGx08rAKL/Gllmgm0VxT9jO33YnuZBqDbfnF3PYGBo4ZW9ERyJDguPTI6
6Ms68uO/B10mNePwOunlKwJxnYZkDnGcqVZpm0RCt5IFWIk+b0ek1OhpzEeGmQp+
xLWzC+O62WVmW5B2aKmJ/jV4MUOA9HFELrbh0kS+Odp1ANgFr0UKQK1O04Hex+7O
3rnHHzeAjHk8SzZRENKFp0Srf5L9GpDb4/FDmNM1XWw2g12R7nD69dNvC6OCiRvi
8TQxRAMYqSU8XKcCAwEAAaNTMFEwHQYDVR0OBBYEFAF0qURhPXq7wjLN0O0g2jrE
xgLoMB8GA1UdIwQYMBaAFAF0qURhPXq7wjLN0O0g2jrExgLoMA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggIBABHATLUgMaHJwT5Rc5P/vPeIu09zZyK5
avol+tSGbMmcWAUK9gYlivyqcPzeJ6m5+GJ2WkfumdhkUY7XclddxEGyw6q/eRE6
nirt84TFlc2QleSFFg84lwTLT6wE6Ym9+qC3C2b0nOgUeGl5J9itoYqDTOp5gF7Q
Ileh2+9aZSnbaR9W3QgRteTIq+9cVnBZExwgrLa6/Iam0x1ERtd/U94prO57D6mE
Wspvj3wfn7oUfTsTGuBjq20xjmQEGxMF+zgMTJGgkOUxwIGrhXWlK80GX6ff9tJJ
3WQ1lBG2BE1eB3NWLuyQjtO0Jl9bfrpz5sUyXMWyGD9bOz/qFLLdi1AxPAu4qIWt
j8avS4DavUtU3LJarW2IVIrVVSs+hg+mrzMpjso0/8QI7kG5hV4vvD6bOxMZzoBW
g6M9+eXYsp03HjNI34Je/w5tcUY90Jfk3mVxz1hTRh1Hj5EhtSlmwxLdBgRe1fdM
Y3gsHP/OFk7MpMFWZQmxZhsfrV1Nfh1XeznKuUCx0EaGPuZcjKeqUroYvlSWKLl9
F2VfCIo0hKE1VZ9G1QxVuB65N+sdgotyj45LCn51HV1unYqY7Lsnmvbyxgevz1Sv
X9kF21BV+lBLQq8aQGyGwk2RfUVlVp2cKvWHqVT+qF9QgW66Dt1gU7+m9qC4jCTO
2OGZ/CvtfsXA
-----END CERTIFICATE-----

View File

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC/eSDUmZrJ9wIB
l9mfW7vuk+5/VXr17g+W7/6HP1qX293dq7ngYs6aNcUVjIjRaufcH/uqPNC5UbCc
KkQpzKOwFBa0fc2q8hsymu5qpBp619KDyHvyj+rMPp1Dbaa+5tzzKYtwpKk7WdTq
NT4aYchLv6UxdppkvwZWns9wShbl/8zC258XnVICg9qJPFajj1Bq/LkOCD6d3mS/
RabUPE7ezex+FP1p4OGDbwgQ0pSz+tDRsnCfMq3S9WiUlgBydbw22JUqNyXjuHIx
3RlKuh08dO1EoIOmV9jkhIhPw10UG7u0JBo3rbLGlEZlI1Ad72xQqplmfRw7ZBN2
GIZNr5LOhz0F5bpzR1EuwIp/iIGLKivXDUS1qnAW3aNiBCmGMAGitMbEKMf5uE7l
mnBzGcOE4GYdmPfOJ+h8qtR+RYFjGKZ4OOVYoU/4imaQ9K9L1AbAyUEkM7EJB4yC
Xut994LXsvJ1l5v3XEVGaLXC/nienrLJ4idGQRc9ptgwfwSif+awN4X9bkAjwHWi
woaNEFVHhIEEocE4j9KIP9FenR4aqA45xJyCPWSLRlZ9DU5evPGvVaczxsgg3DbU
7gdPPSrS7FHBNLy7RJoZTNDSNwqT/moUH8Zheot+1RMwaBM2X8s3mjtjK75h+3Y3
z9x2JsfOTMrrQDKPTUqRljoj2Iv5RQIDAQABAoICAC+ehV66cPenudUBmfr7Cos0
OU1rye/d6/yi5U9nnzVDVjNqIQlAKZfKpaBNWj2S8+UYAzP8egCM43qDPH6UyWTi
Kh9rZjoMil0UkRTuiTNh95YUx1a1GjT/oYcCf0TdD7hd7bLvELOVDNHOugo/pVvJ
ZuEdWRqTM5VZW8fWdUlwS9FuY2uxEZNUjYYx/m4hF2P0RGXMAR6sD6xOO0ZvVUIu
PpHA0KGDbzKL65qbdKYqS8LLOR0usnJT3FWP1L6ir1OIm9hq7L5sweHK1h5ymRDP
F69IqFU3Zda3a1tDACQfHZiYnfiY92xRtgwzMxquz+Zj91C47suKgRiO0uABOWY7
rRCE4aVihSH8hjW2U9tRJBZdpyTlk5wyfoBlVGHOHXhHR0LIBJHcffB3Zwm8BkGd
OR/+4b8yqBBDGC/Bt9dIxM0QdLgmdWO0oywXircEzv6O+l3LGeQg2d7dkWKmMGxi
chnVJVq/txuZVw2+nifI2NueOlc28dIy+GkQqXFVVFgqLCNd7K7wfZ6OVHpb0qx1
fXYtk1Vsx/3YgVKcbHpOKxiJK2xFVtIepooTSHuohZEX+kVtSvh664bmUJ0eZpdN
lkKUhgRfFLtXS6eBPlocZFzWJKUJQ+0b1l4W9G73m68XbByH9dUEe0K1i1ERXcp3
RsSKmouSK04RKbEmyKXhAoIBAQDjGdJvFYRIpHFgsX3cZXC6t6OwSybAM+9g7LOR
jfDZasYs9Tonh2y2MhGKqjKdGQ513Ni1WRzuGrItuyrru+PCLCbattw/GNAlkIZ4
Kqfuex8Ys9TqeW3qfBnIbpc4Sgcrjke5nqTdksoYVMM94alZVLOpsaOvdUwa2keS
MgSwHh6qNVw2/Vz20i8RTzJg3fkhdEQG+atLMl2+J+LhFRlMEACnEJM29UNcuQ3N
TinMRwiSSSCwQpTGMXi61tuJ2m+6cdmHUChX8QoYh9jZLBtjxwEAkGAvzZQeGgxc
bafofHey/ZoZg+DSXgwlKJffmc0huswELB5CiJx43Q5JBj65AoIBAQDX1q0MVxaK
iGsb9g9QE5iO+HD31TBp15rZgyJtUXJWHSFIIs2yCfD2nlxDTKp1KIdLCHj0vnca
/9VGy2h2MNnoU665JCg9wJI9Tgbs7raIM5tQMaJxysGhP/jkY1v0l2fc58+TxgFy
xzqbUeti2t3aQUISWzGukDlwTrQWW7/2DK4JE8pBhDI3n0n6A+eZaeNwWr2ChUlB
1syO5mQpvltM3IWJ/B5CHi5dzRupslnFkIGTzXFhf4kCXzxJb/JLY4XLHSxhiWWg
GvjObbb2FTPgYc+HanpDxM5eRW2oH0hyJUoKR4IrxvvSwGJQD2FejZzRhEI0/L94
D59Ri1nALyjtAoIBAQC8878ircRirG+pBAS0W7JvqFuJUv3q7Us+WbMOaAr82toI
jgDU4tiQvxfZR8LU8wQVDKtCN+LaOVwGsLQFb08RP6sUTxDxbrPAjX9UfCk9QzOc
WgPNEztg3eCV423uZ6mPk9IZnuWNdZSwqdXIpvlAWjkh96s5UV8A+JyUBwnffzAE
bmFLX4L52edPf5VrA0VFkHcJVrIu3rkgfg9HN0bVAnuIhUH3eBmUDGRvbZlZXcDD
9hQ8kyk1vfO1gQ8oo5ZSimdzLj5i7Sp5Po4uI4Smf+1Visp8+49BfGrMfHA3/1eY
lWih0hg88AMq55t1b4I9ji4xSoPi18dYyJQaLhgBAoIBAQDMWsNpJaN/8n2G8ce5
x3PwGaXL4Jt/+tTwEEquOikJA3eZdupOIT92IKW2SoYxevftwM3U2+ilNYhXCQuU
q9gFMgYB4QwAu606QgAooDNObZ4lpXjqSFBgPdOHWdOclyWNcCWHAjgo1hzVJhC5
fgQDOzo1awZ1ArR/cuTrLl9ntMWqboRW17U8GKLQBpZnGGxw2lkHlO6xWZA/1D8N
jt+evEPrSzvS2gSIZ0RDvUtl1NX6fM9WwouUJVtNJKLBYi8xCiQVDSOdHSxpNlO+
VoDRd4on6lZsh4/kjdOvFD9hY5Dgfqfuju2qst/icU19WpMZhCGzTYJzSEdNy6Rk
Y8JZAoIBAGFKZq43NwicIrBTIUKgvLntuNtvkCgBp9awGMptRPqVOkOSkFJLxL6v
pvSjQLLsvoHmgw9DHFYi9D5bWmdNIV/8rPch8XiNyBmjitAMq5siL6cZmswpjwIN
V81q7zt5bRvVJWGXL4JrfUL79bWlzPRBB+jYn2ktsdoz+vQR9tj5ohrOkjwnLSwj
bqhTawwMey4q5LeZPyegkEojx5U/pp/spisT16v9dkGbxgLc7wcmT/7vU2IWY+Es
7WX5FhV0jmj4zESGD5CNtBxkTyBmKJYSXxLZ4ZjS8v3Ua8DkQUdlD73STVK9Lxdp
+xZ1BJ0Xfq/t2SnXDABwi9hvqTNOGqY=
-----END PRIVATE KEY-----

View File

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

View File

@@ -0,0 +1 @@
5.crl

View File

@@ -0,0 +1 @@
1.crl

10
security/advancedtls/testdata/crl/1.crl vendored Normal file
View File

@@ -0,0 +1,10 @@
-----BEGIN X509 CRL-----
MIIBYDCCAQYCAQEwCgYIKoZIzj0EAwIwgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
EwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpH
b29nbGUgTExDMSYwEQYDVQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNs
bjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAyMS0wMi0wMlQwNzozMDozNi0wODowMCkX
DTIxMDIwMjE1MzAzNloXDTIxMDIwOTE1MzAzNlqgLzAtMB8GA1UdIwQYMBaAFPQN
tnCIBcG4ReQgoVi0kPgTROseMAoGA1UdFAQDAgEAMAoGCCqGSM49BAMCA0gAMEUC
IQDB9WEPBPHEo5xjCv8CT9okockJJnkLDOus6FypVLqj5QIgYw9/PYLwb41/Uc+4
LLTAsfdDWh7xBJmqvVQglMoJOEc=
-----END X509 CRL-----

View File

@@ -0,0 +1 @@
2.crl

10
security/advancedtls/testdata/crl/2.crl vendored Normal file
View File

@@ -0,0 +1,10 @@
-----BEGIN X509 CRL-----
MIIBYDCCAQYCAQEwCgYIKoZIzj0EAwIwgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
EwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpH
b29nbGUgTExDMSYwEQYDVQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNs
bjEsMCoGA1UEAxMjbm9kZSBDQSAoMjAyMS0wMi0wMlQwNzozMDozNi0wODowMCkX
DTIxMDIwMjE1MzAzNloXDTIxMDIwOTE1MzAzNlqgLzAtMB8GA1UdIwQYMBaAFBjo
V5Jnk/gp1k7fmWwkvTk/cF/IMAoGA1UdFAQDAgEAMAoGCCqGSM49BAMCA0gAMEUC
IQDgjA1Vj/pNFtNRL0vFEdapmFoArHM2+rn4IiP8jYLsCAIgAj2KEHbbtJ3zl5XP
WVW6ZyW7r3wIX+Bt3vLJWPrQtf8=
-----END X509 CRL-----

View File

@@ -0,0 +1,7 @@
-----BEGIN X509 CRL-----
MIHnMFICAQEwDQYJKoZIhvcNAQEMBQAwDDEKMAgGAioDDAI6KRcNMDkxMTEwMjMw
MDAwWhcNMDkxMTExMDAwMDAwWqASMBAwDgYDVR0jBAcwBYADAQIDMA0GCSqGSIb3
DQEBDAUAA4GBAMl2sjOjtOQ+OCsRyjM0IvqTn7lmdGJMvpYAym367JBamJPCbYrL
MifCjCA1ra7gG0MweZbpm4SG2YLakwi1/B+XhApQ5VVv5SwDn6Yy5zr9ePLEF7Iy
sP86e9s5XfOusLTW+Spre8q1vi7pJrRvUxhJGuUuLoM6Uhvh65ViilDJ
-----END X509 CRL-----

11
security/advancedtls/testdata/crl/3.crl vendored Normal file
View File

@@ -0,0 +1,11 @@
-----BEGIN X509 CRL-----
MIIBiDCCAS8CAQEwCgYIKoZIzj0EAwIwgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
EwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpH
b29nbGUgTExDMSYwEQYDVQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNs
bjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAyMS0wMi0wMlQwNzozMTo1NC0wODowMCkX
DTIxMDIwMjE1MzE1NFoXDTIxMDIwOTE1MzE1NFowJzAlAhQAroEYW855BRqTrlov
5cBCGvkutxcNMjEwMjAyMTUzMTU0WqAvMC0wHwYDVR0jBBgwFoAUeq/TQ959KbWk
/um08jSTXogXpWUwCgYDVR0UBAMCAQEwCgYIKoZIzj0EAwIDRwAwRAIgaSOIhJDg
wOLYlbXkmxW0cqy/AfOUNYbz5D/8/FfvhosCICftg7Vzlu0Nh83jikyjy+wtkiJt
ZYNvGFQ3Sp2L3A9e
-----END X509 CRL-----

10
security/advancedtls/testdata/crl/4.crl vendored Normal file
View File

@@ -0,0 +1,10 @@
-----BEGIN X509 CRL-----
MIIBYDCCAQYCAQEwCgYIKoZIzj0EAwIwgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
EwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpH
b29nbGUgTExDMSYwEQYDVQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNs
bjEsMCoGA1UEAxMjbm9kZSBDQSAoMjAyMS0wMi0wMlQwNzozMTo1NC0wODowMCkX
DTIxMDIwMjE1MzE1NFoXDTIxMDIwOTE1MzE1NFqgLzAtMB8GA1UdIwQYMBaAFIVn
8tIFgZpIdhomgYJ2c5ULLzpSMAoGA1UdFAQDAgEAMAoGCCqGSM49BAMCA0gAMEUC
ICupTvOqgAyRa1nn7+Pe/1vvlJPAQ8gUfTQsQ6XX3v6oAiEA08B2PsK6aTEwzjry
pXqhlUNZFzgaXrVVQuEJbyJ1qoU=
-----END X509 CRL-----

10
security/advancedtls/testdata/crl/5.crl vendored Normal file
View File

@@ -0,0 +1,10 @@
-----BEGIN X509 CRL-----
MIIBXzCCAQYCAQEwCgYIKoZIzj0EAwIwgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
EwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpH
b29nbGUgTExDMSYwEQYDVQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNs
bjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAyMS0wMi0wMlQwNzozMjo1Ny0wODowMCkX
DTIxMDIwMjE1MzI1N1oXDTIxMDIwOTE1MzI1N1qgLzAtMB8GA1UdIwQYMBaAFN+g
xTAtSTlb5Qqvrbp4rZtsaNzqMAoGA1UdFAQDAgEAMAoGCCqGSM49BAMCA0cAMEQC
IHrRKjieY7w7gxvpkJAdszPZBlaSSp/c9wILutBTy7SyAiAwhaHfgas89iRfaBs2
EhGIeK39A+kSzqu6qEQBHpK36g==
-----END X509 CRL-----

11
security/advancedtls/testdata/crl/6.crl vendored Normal file
View File

@@ -0,0 +1,11 @@
-----BEGIN X509 CRL-----
MIIBiDCCAS8CAQEwCgYIKoZIzj0EAwIwgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
EwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpH
b29nbGUgTExDMSYwEQYDVQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNs
bjEsMCoGA1UEAxMjbm9kZSBDQSAoMjAyMS0wMi0wMlQwNzozMjo1Ny0wODowMCkX
DTIxMDIwMjE1MzI1N1oXDTIxMDIwOTE1MzI1N1owJzAlAhQAxSe/pGmyvzN7mxm5
6ZJTYUXYuhcNMjEwMjAyMTUzMjU3WqAvMC0wHwYDVR0jBBgwFoAUpZ30UJXB4lI9
j2SzodCtRFckrRcwCgYDVR0UBAMCAQEwCgYIKoZIzj0EAwIDRwAwRAIgRg3u7t3b
oyV5FhMuGGzWnfIwnKclpT8imnp8tEN253sCIFUY7DjiDohwu4Zup3bWs1OaZ3q3
cm+j0H/oe8zzCAgp
-----END X509 CRL-----

View File

@@ -0,0 +1 @@
4.crl

View File

@@ -0,0 +1 @@
3.crl

View File

@@ -0,0 +1 @@
6.crl

View File

@@ -0,0 +1,92 @@
# CRL Test Data
This directory contains cert chains and CRL files for revocation testing.
To print the chain, use a command like,
```shell
openssl crl2pkcs7 -nocrl -certfile security/crl/x509/client/testdata/revokedLeaf.pem | openssl pkcs7 -print_certs -text -noout
```
The crl file symlinks are generated with `openssl rehash`
## unrevoked.pem
A certificate chain with CRL files and unrevoked certs
* Subject: C=US, ST=California, L=Mountain View, O=Google LLC, OU=Production,
OU=campus-sln, CN=Root CA (2021-02-02T07:30:36-08:00)
* 1.crl
NOTE: 1.crl file is symlinked with 5.crl to simulate two issuers that hash to
the same value to test that loading multiple files works.
* Subject: C=US, ST=California, L=Mountain View, O=Google LLC, OU=Production,
OU=campus-sln, CN=node CA (2021-02-02T07:30:36-08:00)
* 2.crl
## revokedInt.pem
Certificate chain where the intermediate is revoked
* Subject: C=US, ST=California, L=Mountain View, O=Google LLC, OU=Production,
OU=campus-sln, CN=Root CA (2021-02-02T07:31:54-08:00)
* 3.crl
* Subject: C=US, ST=California, L=Mountain View, O=Google LLC, OU=Production,
OU=campus-sln, CN=node CA (2021-02-02T07:31:54-08:00)
* 4.crl
## revokedLeaf.pem
Certificate chain where the leaf is revoked
* Subject: C=US, ST=California, L=Mountain View, O=Google LLC, OU=Production,
OU=campus-sln, CN=Root CA (2021-02-02T07:32:57-08:00)
* 5.crl
* Subject: C=US, ST=California, L=Mountain View, O=Google LLC, OU=Production,
OU=campus-sln, CN=node CA (2021-02-02T07:32:57-08:00)
* 6.crl
## Test Data for testing CRL providers functionality
To generate test data please run provider_create.sh script. All the files have
`provider_` prefix.
We need to generate the following artifacts for testing CRL provider:
* server self signed CA cert
* client self signed CA cert
* server cert signed by client CA
* client cert signed by server CA
* empty crl file
* crl file containing information about revoked server cert
* crl file by 'malicious' CA which contains the same issuer with original CA
All the commands are provided in provider_create.sh script. Please find the
description below.
1. The first two commands generate self signed CAs for client and server:
- provider_server_trust_key.pem
- provider_server_trust_cert.pem
- provider_client_trust_key.pem
- provider_client_trust_cert.pem
2. Generate client and server certs signed by the CAs above:
- provider_server_cert.pem
- provider_client_cert.pem
3. The next 2 commands create 2 files needed for CRL issuing:
- provider_crlnumber.txt
- provider_index.txt
4. The next 3 commands generate an empty CRL file and a CRL file containing
revoked server cert:
- provider_crl_empty.pem
- provider_crl_server_revoked.pem
5. The final section contains commands to generate CRL file by 'malicious' CA.
Note that we use Subject Key Identifier from previously created
provider_client_trust_cert.pem to generate malicious certs / CRL.
- provider_malicious_client_trust_key.pem
- provider_malicious_client_trust_cert.pem
- provider_malicious_crl_empty.pem

View File

@@ -0,0 +1 @@
5.crl

View File

@@ -0,0 +1,14 @@
[req]
distinguished_name = req_distinguished_name
prompt = no
x509_extensions = extensions
[req_distinguished_name]
C = US
ST = CA
L = SVL
O = Internet Widgits Pty Ltd
[extensions]
basicConstraints = CA:true
subjectKeyIdentifier = hash

View File

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

View File

@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFgTCCA2mgAwIBAgIUBpIR/E6Ufmm2cndWnoFZ0Ntsp1wwDQYJKoZIhvcNAQEL
BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5l
dCBXaWRnaXRzIFB0eSBMdGQxGzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTAe
Fw0yNjAyMTIwNzU0MTlaFw0zNjAyMTAwNzU0MTlaMFcxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIDAJDQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRgw
FgYDVQQDDA9mb28uYmFyLmhvby5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQCZnZTnMzrKXLTuxhCDb3zufXGknUBTq2LJS6C4dxq02XDt+pgwGagl
O3CQHTNq2F1s3EEmD2eMMIjhDk1D6DCLcyvMrbNzNFcwaounr8wYdhyzW9DhWBTB
VFU7RqL0jnjLaQGbnGSVGqy3O8cpX+LqPrROrTIyDt63zBwk4LhcIxAACaUpDzmZ
zqk6IcUKv27E3vDS2dUmPUgQtpHiBgZ1VIJkm6GTGrTWNvInbAUbyMP/1sF7Uo59
zPLaPIbtPD6Kau2kvAgpLz70Zx5XTQGvSSyXu/P3C7AtX/sq7f/wyR/1QHX1FZQN
02nCCnBeBBF62sqSmS/pnIHr/ZSW4d3AoaLUl6raKsSSYlXXGEPCK0rWAMh2HsT3
qbU4ya93PclSMhx5BFPVw1kA132ifP4RD29+MjcqiNC7S4bn2aNNWeWTD5o/y2Sd
7ep4eZUpQ0upj/w4g0R/arNgNYDzfq0th8EERn1y9fku2RONkkEnb49xUijwWIKJ
gEVrz4XVTOpkRxo/V9nzwmdh50esv7L3OiE8ogAE2wcKKSWkIwVTOINbdc5ZdXyA
Vzy/J51zZnPztrnmNOyiIlUn6Dcvl1gK5LJaBMzWODCMf0tkeQtSOSwb8Yar+vgu
onRj+mByZ7cBgvosDMe6ST6tff5+wMBBPpUBmeMbshkxeXo5MVN7UwIDAQABo0Iw
QDAdBgNVHQ4EFgQUJWYH52GSJRDUMz8SwIg8BBDEJ5YwHwYDVR0jBBgwFoAUXWT8
wjMCB+STUF9WMHSZ2nmm6lMwDQYJKoZIhvcNAQELBQADggIBABOAJk56dKeYBUW7
81idI/guOqLleBQm1m/qjciQ8tl6eMCfXj1866ud6nzThcRXOcIHnON3R4DPfxt5
AHSWBLPZDkGXvwEY4OAKL2g9Vwg/YAe561AuAKtfQUGyuSGo+S91WgTnQPpq5ZBE
pQwyMa4/QOj740kfNBlrIIhAoekCrS3sO1GXu52SYlKWDk/RP4fZnfzkbjTrkUIP
0wmNbg6mFGgNvgXewbPWWk/oyT22Z8Fi1pRZnF+D1n3MlhBaZcfe9bIWPbSiiqYc
4Hhx4gSyEWSwI8nSGCjfeRQ0/zsoKRyrfBMaJPUGd28blJLazEbvfE66t1BSB7bC
LnEXTWcztuvv4eeQ4zofcDoOQ9RPAdpmomPMsvzpqS6dXZ1KeUEsvrEqxzA15Xy0
XrP0rJlMUKkVNMh+1epAfL/rJWOARpZiFbR+Gt81DSw5f99rXlDuQa/IEvts2Rf7
Si7RKEHDWUYa+Uwd+EVAqhT+6Gc7lG6DPDgrjC9Nbnjris4joUNgkzFBkmkVcnUN
YCz7vBxQY+8WAKJFEeKI7GDdbbdL8O5XTMztG97Mn9psX1ygd+FRr9gkaa6chmYo
CFmaYwUqAXZc5sccNopCrT8xRXvPsfqcObJC+ZTTcy2iG9AHPRONuoMX74euuHv7
fdOHdNs2zxer/DvOI2S0ldauBQcR
-----END CERTIFICATE-----

View File

@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFdzCCA1+gAwIBAgIUBsTfmfdYI6inXskJAgjSkYhI1P8wDQYJKoZIhvcNAQEL
BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNjAyMTIwNzU0MTha
Fw0zNjAyMTAwNzU0MThaMEsxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoG
A1UEBwwDU1ZMMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgWUiV3/qMIn3pCX3/C8JOsWqk
ZoatJMqSUXY50cX4KfRwfB9RxyAc/48pjVUouaHCLPS+/nfycPgWtzU4c4U4QMH8
TKdmi/9g6HeAH/jzWuASqWV8irs+jMA2F3G18n7GgZwRdzIg0X2Bvi/T/mnhhlAK
Or3CwV8XLWBu65z09XA7YICCYZSG/7g6R+jpOb2eVybYtYey+PRd+OEmdlX9r90X
/aF+Xy2gMpqGeSN/OX4AAWNnss0Zl6OFjZYeyrwdGEJh1p++quVNQsiQU+onP5oC
H9m4XL5d9MiGiB36IOAITQd3mbeHm/I0nqCMq5fxqWF4CZna6+E4/kNWC4rKm9TT
DRKqmyJy0VR8lCBdh1lIvGlhJXEVoFvgqpmwwmNMjYW/1OG5CTYMpoSNlIIo+XSn
xWTfjgF6Ex3T4A2igg+fwcG7ApNRUIkdHmMVdYJMlWD11sZrlFCgy0Ia9luFFbZ+
0+DQd0kc2xYN7kFnefrL/McTM4AMnx1jpMky1B45GsyS1Fc5MEQTXxYkMoBP5FVP
6ccsvuvBizAVQWTGNYzL+oDOkg1J1+J2q4aqbiod7vfZdfwSmZrzlLJGIhbxNh29
6e3YdHmoDYbrZZPjPVzRCWS9FlBT3QvTU+uLC6wL/ije6ex++7KvzgIrkIAuEHfe
qEXg09dz9wuXHO/IswIDAQABo1MwUTAdBgNVHQ4EFgQUbcyHqiFVin7tmZVf2ucb
A1bt7UMwHwYDVR0jBBgwFoAUbcyHqiFVin7tmZVf2ucbA1bt7UMwDwYDVR0TAQH/
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAQNrBr1axMAl39YsP/CHbcrTCRjTk
zP2X4Cm8ZbQcJC1Y2hWLD3YUmj5DldG+9D08ub6VTzeQ2GVQmT8ESxEPWXk+5sdN
4MDhoQPLsicvQ6wPsqJ6tIQrJY21W5e5kQY5SBc/yDxyDbTaHJTnU6NhEtuuIXIc
ove4rq0hLs3FnAYcU1wT2/8m2Kohvfmk/T1b7LocBFIgerAE42qVS3ginFDMZOGS
Ejf3JchTfwNYWGIFRokpvBPX3Z34n0yx/Da+zRNLZ0QVrk1MXgUwWc6mRlQ2eLXV
N0hBvi6mVLArxxitmUx3CynAh99iPXyd+wso/vU8mvB0AIIsEXSKqx0j3d/r3Xkz
EhOmnKt84y3YaouUhejigiWqvTmi+MWc2KDFdJSkZ0bRuHsuPORKlUMAdLQl7ca5
ksQ+iGlhX3g8F8A2DkBEQTLIg4dSVq1x1UDIHO5c02BKsftuyv5TP/wdrnE3E2qm
hMrnTl3Q5mZAUHkMOJtVxqSS/+xd4ar5JADk73OOkqKr3JpSdW+Uy2f0tix/I8aV
76lElQvewBsx7dIpL5dKloh8Ev71s9/Z9fnHefacSeRusXofuV3c1yeKELxGKpUh
eVMAx9SkJbTCNc5MnU632JhAKdClRXe+tQw8lThfpmIWsinT1jTc59T5eG3U/Fu5
jGq8soZUfvgGyds=
-----END CERTIFICATE-----

View File

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

View File

@@ -0,0 +1,116 @@
#!/bin/bash
# The script contains a sequence of commands described in README.md
# Generate client/server self signed CAs and certs.
openssl req -x509 -newkey rsa:4096 \
-keyout provider_server_trust_key.pem \
-out provider_server_trust_cert.pem \
-days 3650 \
-config provider_ca.cnf \
-nodes
openssl req -x509 -newkey rsa:4096 \
-keyout provider_client_trust_key.pem \
-out provider_client_trust_cert.pem \
-days 3650 \
-config provider_ca.cnf \
-nodes
openssl req -newkey rsa:4096 \
-keyout provider_server_cert.key \
-out provider_new_cert.csr \
-nodes \
-subj "/C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com" \
-sha256
openssl x509 -req \
-in provider_new_cert.csr \
-out provider_server_cert.pem \
-CA provider_client_trust_cert.pem \
-CAkey provider_client_trust_key.pem \
-CAcreateserial \
-days 3650 \
-sha256 \
-extfile provider_extensions.conf
openssl req -newkey rsa:4096 \
-keyout provider_client_cert.key \
-out provider_new_cert.csr \
-nodes \
-subj "/C=US/ST=CA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.com" \
-sha256
openssl x509 -req \
-in provider_new_cert.csr \
-out provider_client_cert.pem \
-CA provider_server_trust_cert.pem \
-CAkey provider_server_trust_key.pem \
-CAcreateserial \
-days 3650 \
-sha256 \
-extfile provider_extensions.conf
# Generate files need for CRL issuing.
echo "1000" > provider_crlnumber.txt
touch provider_index.txt
# Generate two CRLs.
openssl ca -gencrl \
-keyfile provider_client_trust_key.pem \
-cert provider_client_trust_cert.pem \
-out provider_crl_empty.pem \
-config provider_crl.cnf
openssl ca -revoke provider_server_cert.pem \
-keyfile provider_client_trust_key.pem \
-cert provider_client_trust_cert.pem \
-config provider_crl.cnf
openssl ca -gencrl \
-keyfile provider_client_trust_key.pem \
-cert provider_client_trust_cert.pem \
-out provider_crl_server_revoked.pem \
-config provider_crl.cnf
# Generate malicious CRLs.
openssl genrsa \
-out provider_malicious_client_trust_key.pem 4096
SubjectKeyIdentifier=$(openssl x509 -in provider_client_trust_cert.pem \
-noout \
-text \
| awk '/Subject Key Identifier/ {getline; print $1;}')
sed "s/subjectKeyIdentifier = hash/subjectKeyIdentifier = $SubjectKeyIdentifier/g" \
provider_extensions.conf > provider_extensions.conf.tmp &&
mv provider_extensions.conf.tmp provider_extensions.conf
openssl req -new \
-key provider_malicious_client_trust_key.pem \
-out cert_malicious_request.csr \
-subj "/C=US/ST=CA/L=SVL/O=Internet Widgits Pty Ltd"
openssl x509 -req \
-in cert_malicious_request.csr \
-signkey provider_malicious_client_trust_key.pem \
-out provider_malicious_client_trust_cert.pem \
-days 3650 \
-extfile provider_extensions.conf \
-extensions extensions
openssl ca -gencrl \
-keyfile provider_malicious_client_trust_key.pem \
-cert provider_malicious_client_trust_cert.pem \
-out provider_malicious_crl_empty.pem \
-config provider_crl.cnf
sed "s/subjectKeyIdentifier = .*/subjectKeyIdentifier = hash/g" \
provider_extensions.conf > provider_extensions.conf.tmp &&
mv provider_extensions.conf.tmp provider_extensions.conf
rm -f *.csr provider_{index.txt*,crlnumber.txt*,ca_client.cnf,ca_server.cnf} *.srl

View File

@@ -0,0 +1,14 @@
[ ca ]
default_ca = my_ca
[ my_ca ]
default_md = sha256
database = provider_index.txt
crlnumber = provider_crlnumber.txt
default_crl_days = 30
default_crl_hours = 1
crl_extensions = crl_ext
[crl_ext]
# Authority Key Identifier extension
authorityKeyIdentifier=keyid:always,issuer:always

View File

@@ -0,0 +1,20 @@
-----BEGIN X509 CRL-----
MIIDMTCCARkCAQEwDQYJKoZIhvcNAQELBQAwSzELMAkGA1UEBhMCVVMxCzAJBgNV
BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
UHR5IEx0ZBcNMjYwMjEyMDc1NDE5WhcNMjYwMzE0MDg1NDE5WqCBmTCBljCBhgYD
VR0jBH8wfYAUbcyHqiFVin7tmZVf2ucbA1bt7UOhT6RNMEsxCzAJBgNVBAYTAlVT
MQswCQYDVQQIDAJDQTEMMAoGA1UEBwwDU1ZMMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGSCFAbE35n3WCOop17JCQII0pGISNT/MAsGA1UdFAQEAgIQ
ADANBgkqhkiG9w0BAQsFAAOCAgEADP80NJCab+u4X35GltIMNcedwPFUObfvu06r
DVTm75x6OB4mZCFmNIu1AXWecAPqU7tOnBkfQTDbTNNSHsdVnqXcLO8jiQJ5vePt
OoXqPdFO35OCBWm4+ZnRBn3x1WiRyig4PTgalhPklTKLqY5bIF9tyKH3EdY29CP4
x9TvVoAlAaOM3D8mf2ms9g8I1UPfWjtltuLmL2HJHCiTTFsNm1gnR9UEa8bkeQeP
hxpekE/btZ5XWYKauH03UtVHkUMQuH/pUFhCCPAYwPCDYyE8e4xtjHpak79HvtSd
P/mSlgn9Cce4TOJxryD5KNmsRjcJiUVS3eirr1hhbDD4rDZzFmEWyjYMBQiEcBAl
FMtkomhT1i5+rpVjV2oS/DDwyJe9WhUzc+JhhmyfpJR5IlYtVK6rXgHtfTypv9Pn
bS1SD1KBcOyibixTGoiJWrPjaWbcEM3rQE6cJpkK/8xFvOhmmvpYrxVtyivnLPxO
0pYja34OF1o0AllVHADUjww98tvXjSRNowqBEkEGCtQfIYnbh6WG6m1c6OIMmCPO
dT0Vyk0EOtAIBDNnlQaUOQt3Gmlxi/T3OwAyM7nyAga+qBZNbfJr2zg6GkbIRkbB
8crvyouCUiudUFtxFFL9O1/alDbcIAYCzRk1NL+fPbBHRr5LzSKmyq/DcPKreo19
XaplQm8=
-----END X509 CRL-----

View File

@@ -0,0 +1,20 @@
-----BEGIN X509 CRL-----
MIIDWjCCAUICAQEwDQYJKoZIhvcNAQELBQAwSzELMAkGA1UEBhMCVVMxCzAJBgNV
BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
UHR5IEx0ZBcNMjYwMjEyMDc1NDE5WhcNMjYwMzE0MDg1NDE5WjAnMCUCFBvcyvmo
ckWEbX+0FnMz8yaiiqgFFw0yNjAyMTIwNzU0MTlaoIGZMIGWMIGGBgNVHSMEfzB9
gBRtzIeqIVWKfu2ZlV/a5xsDVu3tQ6FPpE0wSzELMAkGA1UEBhMCVVMxCzAJBgNV
BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
UHR5IEx0ZIIUBsTfmfdYI6inXskJAgjSkYhI1P8wCwYDVR0UBAQCAhABMA0GCSqG
SIb3DQEBCwUAA4ICAQAH7QS5Gbd30cyWj/ATmYuDG2pWOLkTh/kTVHG9aAAv0UsQ
s5M4tTxNX50FcPE3aobVOL0pEAdjq3Cnss1BWQkJqDtewHrV99XmcTBMAvxo0SFi
+fRSgwDR9NaXsDPFJZIn4uHAKKxvA2qBCqKCPJySfIUdzVZ4nsPHAh0LPBGNuf7+
UVakIhdp8jUQavWqlO1Y3VF5IFpiIQEWFQR9KAbZsuH6FsAeC2S+HUzGaVgmALCw
t6OldN4bMSOJakGDwY+3Ih3oNqcYxqAlq+N72X30eonPDloPfvImfqRgJVhD1y5X
vxnln0e47d6I6QUMdNmgbAP1Gs49ntJFk+t99E1XTDsxc7PHNeQ+lHJEh2WsdXrB
m0Ds8YbfN9dsWFiqhILyydm3ei1+JvNGMaQnaVAdUw9wsJDwbVdEDMq13VPDY3H9
jyL7UNOjOSlNJQYpATORtzznzEdq8iZgCyo3cbyBFgXiQ9qdck0FDRXZYp0SPQB7
X+1EjYzFgrPmRMZdItMw147Fe87uj1XGvjo5XID6EOCw/xxVIZCLXijCqDtW44X8
U1W305YxjugQw/wgCUdGFvtThEPUkTKC4O5olBSP6msd10eBY224Lgw9Cn2x0Z5/
QrgyPv7ef9tglfT+mP6BIqkfUWLiGiaxGQvSgY+vH7CoZEqZ5uZ9m7NArbHMag==
-----END X509 CRL-----

View File

@@ -0,0 +1,5 @@
[extensions]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment

View File

@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFXTCCA0WgAwIBAgIUaExYOx/op7TEm3y6Ykmhy8CWn8owDQYJKoZIhvcNAQEL
BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNjAyMTIwNzU0MTla
Fw0zNjAyMTAwNzU0MTlaMEsxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoG
A1UEBwwDU1ZMMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwqLQQN8XrAdohlLIdP5LyeYtL
1S9ZtXFBMdmxtHpLZCuY2orlOyQeaFZrut3DRXmedPFH2n0lk5GbRQAytyo0bSvx
aWJSI+gG6rZsxebFqn9nF7eTFUxpkna9hGCpvhmaMplSjNrKCoVHfkkkBVoHHpW3
vnrcf71mFZAQ+8SbfT7i3UVv2/4Mvhe22gYDa5U0sE9eqd1T4NnNBajTZzX/v1tv
qZMfzJiKmhyr/uI0pxUrefZ6uPwUwPh3/vMTN6ZkeUTQZQdNlYOj3sqtU4hCKKeE
XJ0VlyMfyPltDuentnzawiARvcj5MXKF6XuOKqQ8BDYQqVtWM+05+K7UU4bcBZOw
aXRAgMpbP8lwCTq9QnNGsjDk2w0tCHE9OBcbbEXWn+eRbIkxAd6oniwI9mTWxA95
srGBudB5eXAEREGJXoCpeq6R9vIKGWrKXUjAecEyjdaa4gG68HGv1jhYSBeXuIHg
boOAZlEYPs+tT3QmHiv9lA3uAPa+o1Kt90RBoVZlrYNNt2oWg2Q/9ZKw/biIpAxK
UlaMW1yoR+TBZgeH3mcHGTPM2LlCt7NaTTUMeADb5K3GgpO2MIX4xF7bVhGxZeuJ
tUzp8hZLJkJ9QV2iyEfVLp0v9YgnIy3RqdNNJCIszqRjnsMPISapge8phRfAG2t3
68E3aMv2j46QZ6liNQIDAQABozkwNzAdBgNVHQ4EFgQUbcyHqiFVin7tmZVf2ucb
A1bt7UMwCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIB
AD8jDS+k1jB6ax0vYU27cmximElKbBfZn0Rhrd5JlUuockKaMmSY26s7BSY1bkOA
W5wFpwwmADGm1OOK6cyEJADzA51ZupnD7M0nHSlhIr23Z+8bsUmO8JsniWFBNisd
h9SxAZt3DdLt5RGIrObdSnCE+C7JrzLFELC1W9B7H7anxNFDi+Sj9YR1jV1yLe96
GoIGwXFtUlPm4cEJTsttl/3hDNEJrL+psBx65w7CEg1bsuV7AYTmevrNTjyqOIvo
Z5UBobfhQGErx1hcEbsnXnW0RQEkemclWz7xNOE3/GSOC2smAVeLcHKhJneLsCo7
fDnGD1xuFNCC02EGem2CT2tyBBMk8NJSJsCtcvIm3FYJkkMfJf2pDVDjMl1JgRiO
ixJZ2sdFff/I74VUtCjWD1iKzXitFtTaShNTJUZxsDEb3EMv6NAZFGHoUWjgq/xQ
gFc6vUnzFxG++cjah9EUjDKU3WqnfMlCpcQTh1C3mw6a0pCUHPFandpbfE10RWyN
d9CaFbIhMaHtjRtdmlDe7IGkjv8roaN5IscCKediYHkgkDF74Q92NTzQZQUmU2Wk
xyMRhT3pMDKHu3WL2JPg6UrJK2qXF69KyEiYAPXJXlfBQBq5XZLINeiQsCDhCYas
aOzjFYS00hGVKrYBdNc15PlZhR93d/nRPNcdxAAfRrXy
-----END CERTIFICATE-----

View File

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

View File

@@ -0,0 +1,20 @@
-----BEGIN X509 CRL-----
MIIDWjCCAUICAQEwDQYJKoZIhvcNAQELBQAwSzELMAkGA1UEBhMCVVMxCzAJBgNV
BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
UHR5IEx0ZBcNMjYwMjEyMDc1NDE5WhcNMjYwMzE0MDg1NDE5WjAnMCUCFBvcyvmo
ckWEbX+0FnMz8yaiiqgFFw0yNjAyMTIwNzU0MTlaoIGZMIGWMIGGBgNVHSMEfzB9
gBRtzIeqIVWKfu2ZlV/a5xsDVu3tQ6FPpE0wSzELMAkGA1UEBhMCVVMxCzAJBgNV
BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
UHR5IEx0ZIIUaExYOx/op7TEm3y6Ykmhy8CWn8owCwYDVR0UBAQCAhACMA0GCSqG
SIb3DQEBCwUAA4ICAQAheuHgVWPt4TNAtR6nhdmEBvI+g5VtrJeGAskKKPRjeGVs
eQdEc5w7bJ5D0oRftU/G1k/P7U4H9Moo7jZ7K3XyRgaMAw0RjeTFIs45tJLFuyqq
5MIygJBnfhAazFWZ49vorBnei2A+9Pwlvn2JowVjQ9Eo3VEfo4fIgWwrZISdKADr
I4cVyeEjNDOznJzwPen4QiOVbZu3pMSG10UfCI0OhuSlR86a5nR0Hi8yBCdWyDTl
mrj2o54ywIVYl96j8oC50slm0VGfde5Xy89gypb9/aXQv+bKk3zicw+N3CiXeVGi
Y9i2BWprSMl4qDI6M2gT63z1zDvPwCQHhjRpc2o1Wri6k5jF+zXezF1kwNa/Hru6
slMR/4RhDsRBsfA+pg7Axg6DGD+yan5NTkvShWTYQW2KXt364pH95i34uOT+0L50
RF9lE8rtO6gxFGWSkIOvwqiWsNxXqE4ovhwiI/ewDt37s3UFES0AainBNTcmhxXG
2uF8BQdGGatF7pQ1Qx7Nxq5Xr3WZinxcTu55P4ZETBJaGqT/nPHGh1oc+mRQtZ2p
TjReOOo4BDAaGjni0GNIO89XIgyzpI9NaOAo61gxfjVRI5eBl39vzg3QRgi92TQp
GSPZ+CVZKejvCLtqZrCrBBVmjoZTIkD4XJ3xlaRTAMYpS7CNh0MNcGobprissA==
-----END X509 CRL-----

View File

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
x3/2R1yeRE6qET/XFsGYa/nxAIHY8Ak=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFgjCCA2qgAwIBAgIUG9zK+ahyRYRtf7QWczPzJqKKqAUwDQYJKoZIhvcNAQEL
BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNjAyMTIwNzU0MTha
Fw0zNjAyMTAwNzU0MThaMGcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAG
A1UEBwwJRFVNTVlDSVRZMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBM
dGQxFDASBgNVBAMMC2Zvby5iYXIuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEA4CWvhlqQkYOrgPzVxbPLSwTuKgGT9JNVqa9pLt4JRbO7rTumcFxv
dqFU9KhdlKl3UT1bFYxZB6X7WmWTrEwMIIQHRfnxnW+MfYOoOt5Sgs20lFMYf1ki
RQlwktHIK0P22shaFp9DOeI9y+PshHulG9PP74Rsju5ZLeUqrqn8AoYz2qRxs5cG
S2enJbdOo65ug/5uhmcqY5o0b6Xg2I+nZCB0reGY2ED+0exsNWLJ7cho08MDyLpU
weuWGYIcxWbMOjdojMFuolSq04FvizTYeJalE8w7P5ui4ZLOObpwNu8wfkbEPEES
10m6MgEdu0juNz6JHowKwgU72p2bk8GK8fWgX78bLHaIxhlNY+O42wDgnsIuThKf
P5uNVmuCVDvgzEM3HQUB/U8X1lmn6MFEjKAQq0ja5KcoXGy3uAO4XGJd406aa+HR
g4g0Az+NE/xzOVNQI3YqqkY3LabvCn7IDcQzn4p8zib0RoTwTvMsymI/cvT9+Thr
F+kw09+mAIDu8CJz/icVbPgXdg7NoG279k+JpY5xry1fLDV44BOO0goENNdaHbUq
13vZmqKW6+71P1r1Q0+ZKfP1uSaY63zbltZVWJQXJjO7UZTltJptHnMzQEilNumT
vfRyipkzUTzPGr9rpzeBFS8UivJBpEIMUTBrfL8/F3/R+WQ0yKt7XyMCAwEAAaNC
MEAwHQYDVR0OBBYEFBkCLxjvqldzbym9yZG2rexNVOyhMB8GA1UdIwQYMBaAFG3M
h6ohVYp+7ZmVX9rnGwNW7e1DMA0GCSqGSIb3DQEBCwUAA4ICAQCXpe2iNV0LXSxX
F82i5WJ4Ihbb9raOciij1f06Dd6mFFsfTDYExQ2VxsKwW7LhUwSW7SaO599BhNLl
IsKDtIaz+BPfGjZw2dPUJfZXyCYuvDDg9msPDG4JKc6KqpOjmRO3FhUSQGOm5FZH
79OQc+TaGQkEBSEdC6MsCsQi/Ixuz2hyUWtcnIA5X1W5ZJfFH0G78CXHc4H4eRm+
0E3GbpKTVRFPUJdhALPZ39obO5IpRg4uNcWQvwku0thoAaeJcic1Z0ZMybKVeNTE
BvMzot/2D6/hDEqln9o8RoQmdcBCM67eGZHB/LQ1YOf+Y/kMmoGq89RMtrx9VO4l
TpBnXxwkGCJ9Ex9vfkAliPpmowT+iH1OwYJrJkCfW24SWnwBgAZtb01akHoHRUTZ
FYdihaYt6ejzjVwlUJSWpqS9CnS8e5Nn83HJrKfkihhiEJJv0YZn30ohrWVWe47Y
2rU7onzFNddUpsLKAuwjawQirF7Ooez5YJ+mYaZ57QLOL9QdKjA+diUVP09TJu+x
pTvGpEdWF3f6YlAMreepm3honuugHm8/5+5P55zfCa9LRQP0IneQR2L9Y5Hbm0LB
z/1NpQ6YgvnQccQwq2zxfenntwuG/nE4DFgl3fFh7SaXbJ5VK0hP3a6qfAJO1DR5
oI53aw/lvbD0GineMx5byejYrj7Zvg==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFlTCCA32gAwIBAgIUJJfx0gNOeQy4ksfwESsekJHnYFUwDQYJKoZIhvcNAQEL
BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5l
dCBXaWRnaXRzIFB0eSBMdGQxGzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTAe
Fw0yNjAyMTIwNzU0MThaFw0zNjAyMTAwNzU0MThaMFoxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIDAJWQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRsw
GQYDVQQDDBJmb28uYmFyLmhvby5jYS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDzJH0OV7h7bTnKOJdrUzV+zFefVobL6JL/rOp8ND4MmVyoh+Cl
kg2JyZUUEVrFPFgXzzIqBB2YWZd8KjsSufir02LIsCmc5j/h9QykCJYCWhgu9xbM
zpRP6Wo1aZOg3WbXylzYEj7vFAylzcoCIocHXC9019hQmSnRKs5L+jHPvi16RSq8
SY/we3Z91yB+39EtSY6zkPgtwHnowbeDex0fjNXH3GfzSFoejXwkMIv/yg595AHn
sx35qjKGfe4zaP3ddndnoC9mKIn0Z5b8q4fTzP0p8m0RseqJOl641IeTK15+gEMt
/aqtZBsi9ya2sCIEB8sF6+xLHcSsr7D9TBGSS80jz9i5kCZcsq9aIsJlzTBVRL+R
0gVPnGA35XM8i9f+2OKxrR3rF5yXX2DA7o0tHYWnDlB+c/YedAAJOZ1nn/nTVZV+
oaC69DOknwfMnbBXxNivduCP9MTygWimrwAxXr/58Lk3xApmzc/ZYO2EbhNOx2sb
0iJ73/Q4zJBx4qn3e397LoYU4WKhaUV4lQqUKdbuIpaAb8p/9RJz4IRy9UzAdqyc
oGr1WJkSyt6xcAJ0kCHeZszdbSSFkOOYYwW65x/7vAsvgBsgY+nmiHH+LtKqIK87
Ub8LJiMZii6aeMGwXD7sV3x2PzqwsoxtftaIOVIz8K459p+dY8qpSAFD4wIDAQAB
o1MwUTAdBgNVHQ4EFgQUXWT8wjMCB+STUF9WMHSZ2nmm6lMwHwYDVR0jBBgwFoAU
XWT8wjMCB+STUF9WMHSZ2nmm6lMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
AQsFAAOCAgEAt6pZJZVvBPJjK48BVWJur7PKSdaTK06carAGvuzWY2jn++bNqOxr
BYM9KaPemouw8TxFDp6MuXjHkLSeggYXqR48AzCwg78Kegdfox9Ki29Lg3PbW9II
rqZm3Uz7pDhR+1ydQ6S6Jn3Vddf+l/V1REF0hY8Y5XkJex+Li6Xpjk81TTS0LYd1
76vZo10YNRxCrByEvl9/UqaTFhSYn7ZDFULx0wGySMRr3SVApbqA0fSWg/d2ksY3
Ec82F597Tegu3jUyjw8pAHA+gPDQuALDsoThbRX9qL+6shyLsSYO4G++XreQkVTj
dUcyVQUHxrjYZ6OHD7BXBd2C/tUcCBoJOh1dbA5nQa+xa8NURb/U6/zRvNon4MdH
RMiLHFHLHxTZYZA4yR8yAnwNbtFfYq1r50xhAAdOT7siat4gVvtgCVWJaG/V6iPG
vXXI7eHFwFrNPpACDsROTIqlHjiLQ2FOcHuWqSR7OtBXqvnKMUQ5bEkxIAN0Qdgu
DNfr2NkfZK1CLsJGz1+DA1jcNiGcN5AEwaZSeP8/UUzmUfMkeDm9teHTxgE7GUim
33NNbIYxG2Z1ay55G2Il9JB5rIcefpLcmtiRSumvHAD3zTMT9eNSU0nszeTkwsNI
EYmJVgNDZSkjM2+5Bin11LszKVmZsCwwtWKj+5i12FOhfFLugGkSFmw=
-----END CERTIFICATE-----

View File

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

View File

@@ -0,0 +1,58 @@
-----BEGIN CERTIFICATE-----
MIIDAzCCAqmgAwIBAgITAWjKwm2dNQvkO62Jgyr5rAvVQzAKBggqhkjOPQQDAjCB
pTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v
dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBMTEMxJjARBgNVBAsTClByb2R1
Y3Rpb24wEQYDVQQLEwpjYW1wdXMtc2xuMSwwKgYDVQQDEyNSb290IENBICgyMDIx
LTAyLTAyVDA3OjMxOjU0LTA4OjAwKTAgFw0yMTAyMDIxNTMxNTRaGA85OTk5MTIz
MTIzNTk1OVowgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw
FAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYD
VQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9v
dCBDQSAoMjAyMS0wMi0wMlQwNzozMTo1NC0wODowMCkwWTATBgcqhkjOPQIBBggq
hkjOPQMBBwNCAAQhA0/puhTtSxbVVHseVhL2z7QhpPyJs5Q4beKi7tpaYRDmVn6p
Phh+jbRzg8Qj4gKI/Q1rrdm4rKer63LHpdWdo4GzMIGwMA4GA1UdDwEB/wQEAwIB
BjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUeq/TQ959KbWk/um08jSTXogXpWUwHwYDVR0jBBgwFoAUeq/T
Q959KbWk/um08jSTXogXpWUwLgYDVR0RBCcwJYYjc3BpZmZlOi8vY2FtcHVzLXNs
bi5wcm9kLmdvb2dsZS5jb20wCgYIKoZIzj0EAwIDSAAwRQIgOSQZvyDPQwVOWnpF
zWvI+DS2yXIj/2T2EOvJz2XgcK4CIQCL0mh/+DxLiO4zzbInKr0mxpGSxSeZCUk7
1ZF7AeLlbw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDizCCAzKgAwIBAgIUAK6BGFvOeQUak65aL+XAQhr5LrcwCgYIKoZIzj0EAwIw
gaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N
b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYDVQQLEwpQcm9k
dWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAy
MS0wMi0wMlQwNzozMTo1NC0wODowMCkwIBcNMjEwMjAyMTUzMTU0WhgPOTk5OTEy
MzEyMzU5NTlaMIGlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
MBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UEChMKR29vZ2xlIExMQzEmMBEG
A1UECxMKUHJvZHVjdGlvbjARBgNVBAsTCmNhbXB1cy1zbG4xLDAqBgNVBAMTI25v
ZGUgQ0EgKDIwMjEtMDItMDJUMDc6MzE6NTQtMDg6MDApMFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEye6UOlBos8Q3FFBiLahD9BaLTA18bO4MTPyv35T3lppvxD5X
U/AnEllOnx5OMtMjMBbIQjSkMbiQ9xNXoSqB6aOCATowggE2MA4GA1UdDwEB/wQE
AwIBBjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUhWfy0gWBmkh2GiaBgnZzlQsvOlIwHwYDVR0jBBgwFoAU
eq/TQ959KbWk/um08jSTXogXpWUwMwYDVR0RBCwwKoYoc3BpZmZlOi8vbm9kZS5j
YW1wdXMtc2xuLnByb2QuZ29vZ2xlLmNvbTA7BgNVHR4BAf8EMTAvoC0wK4YpY3Nj
cy10ZWFtLm5vZGUuY2FtcHVzLXNsbi5wcm9kLmdvb2dsZS5jb20wQgYDVR0fBDsw
OTA3oDWgM4YxaHR0cDovL3N0YXRpYy5jb3JwLmdvb2dsZS5jb20vY3JsL2NhbXB1
cy1zbG4vbm9kZTAKBggqhkjOPQQDAgNHADBEAiA79rPu6ZO1/0qB6RxL7jVz1200
UTo8ioB4itbTzMnJqAIgJqp/Rc8OhpsfzQX8XnIIkl+SewT+tOxJT1MHVNMlVhc=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC0DCCAnWgAwIBAgITXQ2c/C27OGqk4Pbu+MNJlOtpYTAKBggqhkjOPQQDAjCB
pTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v
dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBMTEMxJjARBgNVBAsTClByb2R1
Y3Rpb24wEQYDVQQLEwpjYW1wdXMtc2xuMSwwKgYDVQQDEyNub2RlIENBICgyMDIx
LTAyLTAyVDA3OjMxOjU0LTA4OjAwKTAgFw0yMTAyMDIxNTMxNTRaGA85OTk5MTIz
MTIzNTk1OVowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABN2/1le5d3hS/piw
hrNMHjd7gPEjzXwtuXQTzdV+aaeOf3ldnC6OnEF/bggym9MldQSJZLXPYSaoj430
Vu5PRNejggEkMIIBIDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH
AwIGCCsGAQUFBwMBMB0GA1UdDgQWBBTEewP3JgrJPekWWGGjChVqaMhaqTAfBgNV
HSMEGDAWgBSFZ/LSBYGaSHYaJoGCdnOVCy86UjBrBgNVHREBAf8EYTBfghZqemFi
MTIucHJvZC5nb29nbGUuY29thkVzcGlmZmU6Ly9jc2NzLXRlYW0ubm9kZS5jYW1w
dXMtc2xuLnByb2QuZ29vZ2xlLmNvbS9yb2xlL2JvcmctYWRtaW4tY28wQgYDVR0f
BDswOTA3oDWgM4YxaHR0cDovL3N0YXRpYy5jb3JwLmdvb2dsZS5jb20vY3JsL2Nh
bXB1cy1zbG4vbm9kZTAKBggqhkjOPQQDAgNJADBGAiEA9w4qp3nHpXo+6d7mZc69
QoALfP5ynfBCArt8bAlToo8CIQCgc/lTfl2BtBko+7h/w6pKxLeuoQkvCL5gHFyK
LXE6vA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,59 @@
-----BEGIN CERTIFICATE-----
MIIDAzCCAqmgAwIBAgITTwodm6C4ZabFVUVa5yBw0TbzJTAKBggqhkjOPQQDAjCB
pTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v
dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBMTEMxJjARBgNVBAsTClByb2R1
Y3Rpb24wEQYDVQQLEwpjYW1wdXMtc2xuMSwwKgYDVQQDEyNSb290IENBICgyMDIx
LTAyLTAyVDA3OjMyOjU3LTA4OjAwKTAgFw0yMTAyMDIxNTMyNTdaGA85OTk5MTIz
MTIzNTk1OVowgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw
FAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYD
VQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9v
dCBDQSAoMjAyMS0wMi0wMlQwNzozMjo1Ny0wODowMCkwWTATBgcqhkjOPQIBBggq
hkjOPQMBBwNCAARoZnzQWvAoyhvCLA2cFIK17khSaA9aA+flS5X9fLRt4RsfPCx3
kim7wYKQSmBhQdc1UM4h3969r1c1Fvsh2H9qo4GzMIGwMA4GA1UdDwEB/wQEAwIB
BjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQU36DFMC1JOVvlCq+tunitm2xo3OowHwYDVR0jBBgwFoAU36DF
MC1JOVvlCq+tunitm2xo3OowLgYDVR0RBCcwJYYjc3BpZmZlOi8vY2FtcHVzLXNs
bi5wcm9kLmdvb2dsZS5jb20wCgYIKoZIzj0EAwIDSAAwRQIgN7S9dQOQzNih92ag
7c5uQxuz+M6wnxWj/uwGQIIghRUCIQD2UDH6kkRSYQuyP0oN7XYO3XFjmZ2Yer6m
1ZS8fyWYYA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDjTCCAzKgAwIBAgIUAOmArBu9gihLTlqP3W7Et0UoocEwCgYIKoZIzj0EAwIw
gaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N
b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYDVQQLEwpQcm9k
dWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAy
MS0wMi0wMlQwNzozMjo1Ny0wODowMCkwIBcNMjEwMjAyMTUzMjU3WhgPOTk5OTEy
MzEyMzU5NTlaMIGlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
MBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UEChMKR29vZ2xlIExMQzEmMBEG
A1UECxMKUHJvZHVjdGlvbjARBgNVBAsTCmNhbXB1cy1zbG4xLDAqBgNVBAMTI25v
ZGUgQ0EgKDIwMjEtMDItMDJUMDc6MzI6NTctMDg6MDApMFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEfrgVEVQfSEFeCF1/FGeW7oq0yxecenT1BESfj4Z0zJ8p7P9W
bj1o6Rn6dUNlEhGrx7E3/4NFJ0cL1BSNGHkjiqOCATowggE2MA4GA1UdDwEB/wQE
AwIBBjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUpZ30UJXB4lI9j2SzodCtRFckrRcwHwYDVR0jBBgwFoAU
36DFMC1JOVvlCq+tunitm2xo3OowMwYDVR0RBCwwKoYoc3BpZmZlOi8vbm9kZS5j
YW1wdXMtc2xuLnByb2QuZ29vZ2xlLmNvbTA7BgNVHR4BAf8EMTAvoC0wK4YpY3Nj
cy10ZWFtLm5vZGUuY2FtcHVzLXNsbi5wcm9kLmdvb2dsZS5jb20wQgYDVR0fBDsw
OTA3oDWgM4YxaHR0cDovL3N0YXRpYy5jb3JwLmdvb2dsZS5jb20vY3JsL2NhbXB1
cy1zbG4vbm9kZTAKBggqhkjOPQQDAgNJADBGAiEAnuONgMqmbBlj4ibw5BgDtZUM
pboACSFJtEOJu4Yqjt0CIQDI5193J4wUcAY0BK0vO9rRfbNOIc+4ke9ieBDPSuhm
mA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICzzCCAnagAwIBAgIUAMUnv6Rpsr8ze5sZuemSU2FF2LowCgYIKoZIzj0EAwIw
gaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N
b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYDVQQLEwpQcm9k
dWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjbm9kZSBDQSAoMjAy
MS0wMi0wMlQwNzozMjo1Ny0wODowMCkwIBcNMjEwMjAyMTUzMjU3WhgPOTk5OTEy
MzEyMzU5NTlaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASCmYiIHUux5WFz
S0ksJzAPL7YTEh5o5MdXgLPB/WM6x9sVsQDSYU0PF5qc9vPNhkQzGBW79dkBnxhW
AGJkFr1Po4IBJDCCASAwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUF
BwMCBggrBgEFBQcDATAdBgNVHQ4EFgQUCR1CGEdlks0qcxCExO0rP1B/Z7UwHwYD
VR0jBBgwFoAUpZ30UJXB4lI9j2SzodCtRFckrRcwawYDVR0RAQH/BGEwX4IWanph
YjEyLnByb2QuZ29vZ2xlLmNvbYZFc3BpZmZlOi8vY3Njcy10ZWFtLm5vZGUuY2Ft
cHVzLXNsbi5wcm9kLmdvb2dsZS5jb20vcm9sZS9ib3JnLWFkbWluLWNvMEIGA1Ud
HwQ7MDkwN6A1oDOGMWh0dHA6Ly9zdGF0aWMuY29ycC5nb29nbGUuY29tL2NybC9j
YW1wdXMtc2xuL25vZGUwCgYIKoZIzj0EAwIDRwAwRAIgK9vQYNoL8HlEwWv89ioG
aQ1+8swq6Bo/5mJBrdVLvY8CIGxo6M9vJkPdObmetWNC+lmKuZDoqJWI0AAmBT2J
mR2r
-----END CERTIFICATE-----

View File

@@ -0,0 +1,58 @@
-----BEGIN CERTIFICATE-----
MIIDBDCCAqqgAwIBAgIUALy864QhnkTdceLH52k2XVOe8IQwCgYIKoZIzj0EAwIw
gaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N
b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYDVQQLEwpQcm9k
dWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAy
MS0wMi0wMlQwNzozMDozNi0wODowMCkwIBcNMjEwMjAyMTUzMDM2WhgPOTk5OTEy
MzEyMzU5NTlaMIGlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
MBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UEChMKR29vZ2xlIExMQzEmMBEG
A1UECxMKUHJvZHVjdGlvbjARBgNVBAsTCmNhbXB1cy1zbG4xLDAqBgNVBAMTI1Jv
b3QgQ0EgKDIwMjEtMDItMDJUMDc6MzA6MzYtMDg6MDApMFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEYv/JS5hQ5kIgdKqYZWTKCO/6gloHAmIb1G8lmY0oXLXYNHQ4
qHN7/pPtlcHQp0WK/hM8IGvgOUDoynA8mj0H9KOBszCBsDAOBgNVHQ8BAf8EBAMC
AQYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFPQNtnCIBcG4ReQgoVi0kPgTROseMB8GA1UdIwQYMBaAFPQN
tnCIBcG4ReQgoVi0kPgTROseMC4GA1UdEQQnMCWGI3NwaWZmZTovL2NhbXB1cy1z
bG4ucHJvZC5nb29nbGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIQDwBn20DB4X/7Uk
Q5BR8JxQYUPxOfvuedjfeA8bPvQ2FwIgOEWa0cXJs1JxarILJeCXtdXvBgu6LEGQ
3Pk/bgz8Gek=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDizCCAzKgAwIBAgIUAM/6RKQ7Vke0i4xp5LaAqV73cmIwCgYIKoZIzj0EAwIw
gaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N
b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYDVQQLEwpQcm9k
dWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAy
MS0wMi0wMlQwNzozMDozNi0wODowMCkwIBcNMjEwMjAyMTUzMDM2WhgPOTk5OTEy
MzEyMzU5NTlaMIGlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
MBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UEChMKR29vZ2xlIExMQzEmMBEG
A1UECxMKUHJvZHVjdGlvbjARBgNVBAsTCmNhbXB1cy1zbG4xLDAqBgNVBAMTI25v
ZGUgQ0EgKDIwMjEtMDItMDJUMDc6MzA6MzYtMDg6MDApMFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEllnhxmMYiUPUgRGmenbnm10gXpM94zHx3D1/HumPs6arjYuT
Zlhx81XL+g4bu4HII2qcGdP+Hqj/MMFNDI9z4aOCATowggE2MA4GA1UdDwEB/wQE
AwIBBjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUGOhXkmeT+CnWTt+ZbCS9OT9wX8gwHwYDVR0jBBgwFoAU
9A22cIgFwbhF5CChWLSQ+BNE6x4wMwYDVR0RBCwwKoYoc3BpZmZlOi8vbm9kZS5j
YW1wdXMtc2xuLnByb2QuZ29vZ2xlLmNvbTA7BgNVHR4BAf8EMTAvoC0wK4YpY3Nj
cy10ZWFtLm5vZGUuY2FtcHVzLXNsbi5wcm9kLmdvb2dsZS5jb20wQgYDVR0fBDsw
OTA3oDWgM4YxaHR0cDovL3N0YXRpYy5jb3JwLmdvb2dsZS5jb20vY3JsL2NhbXB1
cy1zbG4vbm9kZTAKBggqhkjOPQQDAgNHADBEAiA86egqPw0qyapAeMGbHxrmYZYa
i5ARQsSKRmQixgYizQIgW+2iRWN6Kbqt4WcwpmGv/xDckdRXakF5Ign/WUDO5u4=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICzzCCAnWgAwIBAgITYjjKfYZUKQNUjNyF+hLDGpHJKTAKBggqhkjOPQQDAjCB
pTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v
dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBMTEMxJjARBgNVBAsTClByb2R1
Y3Rpb24wEQYDVQQLEwpjYW1wdXMtc2xuMSwwKgYDVQQDEyNub2RlIENBICgyMDIx
LTAyLTAyVDA3OjMwOjM2LTA4OjAwKTAgFw0yMTAyMDIxNTMwMzZaGA85OTk5MTIz
MTIzNTk1OVowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD4r4+nCgZExYF8v
CLvGn0lY/cmam8mAkJDXRN2Ja2t+JwaTOptPmbbXft+1NTk5gCg5wB+FJCnaV3I/
HaxEhBWjggEkMIIBIDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH
AwIGCCsGAQUFBwMBMB0GA1UdDgQWBBTTCjXX1Txjc00tBg/5cFzpeCSKuDAfBgNV
HSMEGDAWgBQY6FeSZ5P4KdZO35lsJL05P3BfyDBrBgNVHREBAf8EYTBfghZqemFi
MTIucHJvZC5nb29nbGUuY29thkVzcGlmZmU6Ly9jc2NzLXRlYW0ubm9kZS5jYW1w
dXMtc2xuLnByb2QuZ29vZ2xlLmNvbS9yb2xlL2JvcmctYWRtaW4tY28wQgYDVR0f
BDswOTA3oDWgM4YxaHR0cDovL3N0YXRpYy5jb3JwLmdvb2dsZS5jb20vY3JsL2Nh
bXB1cy1zbG4vbm9kZTAKBggqhkjOPQQDAgNIADBFAiBq3URViNyMLpvzZHC1Y+4L
+35guyIJfjHu08P3S8/xswIhAJtWSQ1ZtozdOzGxg7GfUo4hR+5SP6rBTgIqXEfq
48fW
-----END CERTIFICATE-----

View File

@@ -0,0 +1,23 @@
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Illinois
localityName = Locality Name (eg, city)
localityName_default = Chicago
organizationName = Organization Name (eg, company)
organizationName_default = Example, Co.
commonName = Common Name (eg, YOUR name)
commonName_max = 64
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost

View File

@@ -0,0 +1,87 @@
base_dir = .
certificate = $base_dir/cacert.pem # The CA certificate
private_key = $base_dir/cakey.pem # The CA private key
new_certs_dir = $base_dir # Location for new certs after signing
database = $base_dir/index.txt # Database index file
serial = $base_dir/serial.txt # The current serial number
unique_subject = no # Set to 'no' to allow creation of
# several certificates with same subject.
HOME = .
RANDFILE = $ENV::HOME/.rnd
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
[ CA_default ]
default_days = 10000 # How long to certify for
default_crl_days = 30 # How long before next CRL
default_md = sha256 # Use public key default MD
preserve = no # Keep passed DN ordering
x509_extensions = ca_extensions # The extensions to add to the cert
email_in_dn = no # Don't concat the email in the DN
copy_extensions = copy # Required to copy SANs from CSR to cert
####################################################################
[ req ]
default_bits = 4096
default_keyfile = cakey.pem
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions
string_mask = utf8only
####################################################################
[ ca_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Maryland
localityName = Locality Name (eg, city)
localityName_default = Baltimore
organizationName = Organization Name (eg, company)
organizationName_default = Test CA, Limited
organizationalUnitName = Organizational Unit (eg, division)
organizationalUnitName_default = Server Research Department
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = Test CA
emailAddress = Email Address
emailAddress_default = test@example.com
####################################################################
[ ca_extensions ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer
basicConstraints = critical, CA:true
keyUsage = keyCertSign, cRLSign
####################################################################
[ signing_policy ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ signing_req ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment

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,90 @@
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

View File

@@ -0,0 +1,122 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 7 (0x7)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=CA, O=Internet Widgits Pty Ltd, CN=foo.bar.client2.trust.com
Validity
Not Before: Jan 9 22:51:54 2020 GMT
Not After : Oct 23 22:51:54 2293 GMT
Subject: C=US, ST=CA, O=Internet Widgits Pty Ltd, CN=foo.bar.server2.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:b1:0b:d3:7e:5b:61:30:db:b0:5f:3f:6d:d2:e0:
3b:c6:4c:88:95:f5:7e:fd:cd:aa:20:5d:08:b9:6e:
41:db:c4:ed:0d:f8:bc:cb:b4:ee:c5:87:11:05:a0:
ac:12:3b:4e:0b:4c:e4:43:e4:17:89:c1:ae:b4:13:
58:1c:31:58:6a:f2:01:ed:df:66:e9:f9:2e:9c:c5:
85:e6:02:db:36:f4:f3:07:39:75:30:f1:b5:55:5b:
46:2f:87:b0:d4:a0:ab:57:df:30:45:ae:bd:b0:49:
9a:fc:ba:5e:bc:d0:5d:86:f4:24:45:4a:d5:4d:5b:
b6:ba:e8:b7:a1:3b:c3:2f:46:2e:b3:ad:2c:63:03:
df:cb:f4:56:62:91:bd:bc:23:00:af:a2:7a:3d:6f:
f1:33:81:60:0e:bc:20:f5:8a:49:5f:ec:58:bc:64:
d5:47:36:a0:2b:b8:1f:76:25:01:89:3e:ff:52:69:
95:03:8f:bb:14:2f:1a:38:a3:9f:c1:45:20:22:77:
70:97:5e:25:51:b8:3d:5d:89:7a:bb:15:12:cd:1d:
96:d2:9c:72:67:12:85:72:6e:27:7a:ef:25:da:af:
49:26:8d:eb:a0:34:a4:4d:64:c3:63:33:77:5d:ad:
53:c7:ee:51:32:7b:cc:43:bb:86:8d:f9:52:ba:35:
23:0e:30:5d:dc:3b:25:63:c1:e3:5f:4b:b2:02:fc:
fe:5b:18:7f:84:aa:f3:71:e4:16:b5:98:bc:73:c5:
58:13:41:38:eb:f3:a2:fa:8c:98:bd:f1:10:ee:b6:
fe:7e:a5:81:c7:5e:f2:72:54:8e:db:09:f0:35:42:
ca:b7:86:c2:48:b2:c6:18:08:ac:d1:f0:5d:de:b0:
b8:25:8b:3b:bd:61:48:0f:71:3f:ed:97:72:02:c9:
44:5d:0c:00:fc:30:ca:5d:1c:e5:13:1b:3a:d0:ce:
d9:36:a0:db:f5:c2:ad:a6:95:26:4e:7b:29:2d:fc:
c4:04:1d:47:6e:03:59:68:1e:7a:20:6d:e8:a8:e1:
3c:57:59:f8:3d:2f:16:61:7e:24:e5:13:ca:48:0a:
e6:f0:60:a3:2d:93:0b:8f:93:eb:b5:d1:06:26:52:
c0:63:1f:fc:9b:73:fe:91:c3:04:40:32:8d:09:d5:
9e:c4:f6:0b:61:3d:9f:a1:d7:94:a2:e1:3d:b6:bb:
60:26:74:89:33:25:18:0f:c3:88:db:10:5e:a0:5b:
f4:ee:d0:18:ab:36:50:c5:44:9b:6d:ba:ea:e2:6e:
52:3a:55:49:a3:72:ae:04:af:1d:f6:f2:83:27:17:
8b:9a:98:0a:f5:44:b1:c8:f2:a9:c8:ed:b0:75:ca:
52:25:f3
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
74:BD:18:0B:32:AF:D0:51:8E:4C:4C:8D:B2:F6:4E:B8:6D:AB:BD:BA
X509v3 Authority Key Identifier:
keyid:01:74:A9:44:61:3D:7A:BB:C2:32:CD:D0:ED:20:DA:3A:C4:C6:02:E8
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Key Encipherment
Signature Algorithm: sha256WithRSAEncryption
b5:63:0c:d8:ed:af:74:2d:4c:94:36:41:05:2a:f2:ef:45:e5:
6a:0c:76:0c:f3:90:25:e0:54:56:f3:26:23:95:7e:24:74:6b:
fd:02:0a:bc:33:ba:e8:e8:8f:a3:b3:85:2e:59:4c:cf:e3:85:
1a:d6:70:5c:7c:86:e2:7a:11:99:a8:fa:43:9a:bf:50:54:00:
9e:6a:7b:72:7f:c5:20:89:6e:18:6c:46:64:ce:44:44:47:4d:
87:b5:fc:cf:f3:b9:9f:45:a3:cb:b0:91:00:96:2d:29:68:8b:
ff:c7:e0:f1:b7:8d:31:c2:01:be:5b:51:1d:af:42:b1:17:22:
bc:91:e4:d9:b9:96:6d:64:40:79:6c:71:ed:f6:e5:49:16:0a:
e3:bc:18:95:2e:89:ba:c4:a5:ce:ba:ab:3a:32:eb:bc:d8:91:
cd:f2:ee:d1:fc:67:3a:51:00:92:bd:b8:68:0b:54:04:d5:07:
0b:97:11:2c:42:64:7c:47:c1:68:b4:eb:21:c4:e4:ad:17:a7:
16:b9:e0:e6:cd:04:c6:89:36:40:d4:4b:c3:f7:7e:26:6b:3a:
d7:68:b3:b2:da:00:65:13:c8:fa:d0:1c:2e:10:ba:71:3e:0f:
aa:8b:d0:ff:b7:3e:83:9c:bc:b3:d1:52:0c:9f:3f:21:4a:10:
dc:8f:ab:38:45:d4:2c:2a:15:2d:71:45:fe:91:a2:d8:d9:dd:
0c:dc:a7:d9:cd:1b:f5:35:fe:14:ba:c5:1f:ed:ee:fb:87:cc:
87:a1:08:c2:2e:ff:5d:af:b3:3d:6e:11:94:79:0b:28:e6:83:
4e:fc:28:8f:7f:00:85:79:7f:3a:d1:07:ee:6e:fa:94:c4:0b:
4b:2c:05:b1:68:00:e8:37:bc:b8:b2:03:5c:5a:ca:13:f2:68:
57:df:ac:fc:da:be:27:24:7e:6d:c4:a9:53:2d:f2:43:0e:30:
9c:82:d5:fb:f1:a2:0a:83:e0:a5:d8:9f:09:3e:99:c8:39:d6:
69:6d:d6:c2:27:70:59:05:3c:3c:7d:d6:41:6a:b4:9c:1f:70:
7e:3e:ee:6f:67:de:95:1d:eb:31:8b:11:c8:0d:a1:25:4e:08:
ef:3a:11:2d:a7:98:0d:a1:d9:30:2d:da:d2:a0:05:6b:34:38:
a6:87:b2:bd:0f:9c:51:cc:e0:2e:a2:1b:a3:a0:a6:eb:1f:0a:
22:70:59:f0:0b:c9:bd:94:4e:1d:65:3b:99:5d:8e:6c:18:82:
1d:b5:cc:6f:14:21:c4:89:07:9b:81:1d:9a:79:ff:bf:fd:ce:
e4:77:11:0f:47:21:dc:d9:79:f3:40:26:56:5c:b4:86:32:8e:
28:b9:14:e7:b3:fe:86:47
-----BEGIN CERTIFICATE-----
MIIFkzCCA3ugAwIBAgIBBzANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCQ0ExITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEi
MCAGA1UEAwwZZm9vLmJhci5jbGllbnQyLnRydXN0LmNvbTAgFw0yMDAxMDkyMjUx
NTRaGA8yMjkzMTAyMzIyNTE1NFowWzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNB
MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxHDAaBgNVBAMME2Zv
by5iYXIuc2VydmVyMi5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQCxC9N+W2Ew27BfP23S4DvGTIiV9X79zaogXQi5bkHbxO0N+LzLtO7FhxEFoKwS
O04LTORD5BeJwa60E1gcMVhq8gHt32bp+S6cxYXmAts29PMHOXUw8bVVW0Yvh7DU
oKtX3zBFrr2wSZr8ul680F2G9CRFStVNW7a66LehO8MvRi6zrSxjA9/L9FZikb28
IwCvono9b/EzgWAOvCD1iklf7Fi8ZNVHNqAruB92JQGJPv9SaZUDj7sULxo4o5/B
RSAid3CXXiVRuD1diXq7FRLNHZbSnHJnEoVybid67yXar0kmjeugNKRNZMNjM3dd
rVPH7lEye8xDu4aN+VK6NSMOMF3cOyVjweNfS7IC/P5bGH+EqvNx5Ba1mLxzxVgT
QTjr86L6jJi98RDutv5+pYHHXvJyVI7bCfA1Qsq3hsJIssYYCKzR8F3esLglizu9
YUgPcT/tl3ICyURdDAD8MMpdHOUTGzrQztk2oNv1wq2mlSZOeykt/MQEHUduA1lo
Hnogbeio4TxXWfg9LxZhfiTlE8pICubwYKMtkwuPk+u10QYmUsBjH/ybc/6RwwRA
Mo0J1Z7E9gthPZ+h15Si4T22u2AmdIkzJRgPw4jbEF6gW/Tu0BirNlDFRJttuuri
blI6VUmjcq4Erx328oMnF4uamAr1RLHI8qnI7bB1ylIl8wIDAQABo1owWDAdBgNV
HQ4EFgQUdL0YCzKv0FGOTEyNsvZOuG2rvbowHwYDVR0jBBgwFoAUAXSpRGE9ervC
Ms3Q7SDaOsTGAugwCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEL
BQADggIBALVjDNjtr3QtTJQ2QQUq8u9F5WoMdgzzkCXgVFbzJiOVfiR0a/0CCrwz
uujoj6OzhS5ZTM/jhRrWcFx8huJ6EZmo+kOav1BUAJ5qe3J/xSCJbhhsRmTORERH
TYe1/M/zuZ9Fo8uwkQCWLSloi//H4PG3jTHCAb5bUR2vQrEXIryR5Nm5lm1kQHls
ce325UkWCuO8GJUuibrEpc66qzoy67zYkc3y7tH8ZzpRAJK9uGgLVATVBwuXESxC
ZHxHwWi06yHE5K0Xpxa54ObNBMaJNkDUS8P3fiZrOtdos7LaAGUTyPrQHC4QunE+
D6qL0P+3PoOcvLPRUgyfPyFKENyPqzhF1CwqFS1xRf6RotjZ3Qzcp9nNG/U1/hS6
xR/t7vuHzIehCMIu/12vsz1uEZR5Cyjmg078KI9/AIV5fzrRB+5u+pTEC0ssBbFo
AOg3vLiyA1xayhPyaFffrPzavickfm3EqVMt8kMOMJyC1fvxogqD4KXYnwk+mcg5
1mlt1sIncFkFPDx91kFqtJwfcH4+7m9n3pUd6zGLEcgNoSVOCO86ES2nmA2h2TAt
2tKgBWs0OKaHsr0PnFHM4C6iG6OgpusfCiJwWfALyb2UTh1lO5ldjmwYgh21zG8U
IcSJB5uBHZp5/7/9zuR3EQ9HIdzZefNAJlZctIYyjii5FOez/oZH
-----END CERTIFICATE-----

View File

@@ -0,0 +1,90 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 7 (0x7)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, ST = CA, O = Internet Widgits Pty Ltd, CN = foo.bar.client2.trust.com
Validity
Not Before: Jan 9 22:51:54 2020 GMT
Not After : Oct 23 22:51:54 2293 GMT
Subject: C = US, ST = CA, O = Internet Widgits Pty Ltd, CN = foo.bar.server2.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:b1:0b:d3:7e:5b:61:30:db:b0:5f:3f:6d:d2:e0:
3b:c6:4c:88:95:f5:7e:fd:cd:aa:20:5d:08:b9:6e:
41:db:c4:ed:0d:f8:bc:cb:b4:ee:c5:87:11:05:a0:
ac:12:3b:4e:0b:4c:e4:43:e4:17:89:c1:ae:b4:13:
58:1c:31:58:6a:f2:01:ed:df:66:e9:f9:2e:9c:c5:
85:e6:02:db:36:f4:f3:07:39:75:30:f1:b5:55:5b:
46:2f:87:b0:d4:a0:ab:57:df:30:45:ae:bd:b0:49:
9a:fc:ba:5e:bc:d0:5d:86:f4:24:45:4a:d5:4d:5b:
b6:ba:e8:b7:a1:3b:c3:2f:46:2e:b3:ad:2c:63:03:
df:cb:f4:56:62:91:bd:bc:23:00:af:a2:7a:3d:6f:
f1:33:81:60:0e:bc:20:f5:8a:49:5f:ec:58:bc:64:
d5:47:36:a0:2b:b8:1f:76:25:01:89:3e:ff:52:69:
95:03:8f:bb:14:2f:1a:38:a3:9f:c1:45:20:22:77:
70:97:5e:25:51:b8:3d:5d:89:7a:bb:15:12:cd:1d:
96:d2:9c:72:67:12:85:72:6e:27:7a:ef:25:da:af:
49:26:8d:eb:a0:34:a4:4d:64:c3:63:33:77:5d:ad:
53:c7:ee:51:32:7b:cc:43:bb:86:8d:f9:52:ba:35:
23:0e:30:5d:dc:3b:25:63:c1:e3:5f:4b:b2:02:fc:
fe:5b:18:7f:84:aa:f3:71:e4:16:b5:98:bc:73:c5:
58:13:41:38:eb:f3:a2:fa:8c:98:bd:f1:10:ee:b6:
fe:7e:a5:81:c7:5e:f2:72:54:8e:db:09:f0:35:42:
ca:b7:86:c2:48:b2:c6:18:08:ac:d1:f0:5d:de:b0:
b8:25:8b:3b:bd:61:48:0f:71:3f:ed:97:72:02:c9:
44:5d:0c:00:fc:30:ca:5d:1c:e5:13:1b:3a:d0:ce:
d9:36:a0:db:f5:c2:ad:a6:95:26:4e:7b:29:2d:fc:
c4:04:1d:47:6e:03:59:68:1e:7a:20:6d:e8:a8:e1:
3c:57:59:f8:3d:2f:16:61:7e:24:e5:13:ca:48:0a:
e6:f0:60:a3:2d:93:0b:8f:93:eb:b5:d1:06:26:52:
c0:63:1f:fc:9b:73:fe:91:c3:04:40:32:8d:09:d5:
9e:c4:f6:0b:61:3d:9f:a1:d7:94:a2:e1:3d:b6:bb:
60:26:74:89:33:25:18:0f:c3:88:db:10:5e:a0:5b:
f4:ee:d0:18:ab:36:50:c5:44:9b:6d:ba:ea:e2:6e:
52:3a:55:49:a3:72:ae:04:af:1d:f6:f2:83:27:17:
8b:9a:98:0a:f5:44:b1:c8:f2:a9:c8:ed:b0:75:ca:
52:25:f3
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
74:BD:18:0B:32:AF:D0:51:8E:4C:4C:8D:B2:F6:4E:B8:6D:AB:BD:BA
X509v3 Authority Key Identifier:
keyid:01:74:A9:44:61:3D:7A:BB:C2:32:CD:D0:ED:20:DA:3A:C4:C6:02:E8
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Key Encipherment
Signature Algorithm: sha256WithRSAEncryption
b5:63:0c:d8:ed:af:74:2d:4c:94:36:41:05:2a:f2:ef:45:e5:
6a:0c:76:0c:f3:90:25:e0:54:56:f3:26:23:95:7e:24:74:6b:
fd:02:0a:bc:33:ba:e8:e8:8f:a3:b3:85:2e:59:4c:cf:e3:85:
1a:d6:70:5c:7c:86:e2:7a:11:99:a8:fa:43:9a:bf:50:54:00:
9e:6a:7b:72:7f:c5:20:89:6e:18:6c:46:64:ce:44:44:47:4d:
87:b5:fc:cf:f3:b9:9f:45:a3:cb:b0:91:00:96:2d:29:68:8b:
ff:c7:e0:f1:b7:8d:31:c2:01:be:5b:51:1d:af:42:b1:17:22:
bc:91:e4:d9:b9:96:6d:64:40:79:6c:71:ed:f6:e5:49:16:0a:
e3:bc:18:95:2e:89:ba:c4:a5:ce:ba:ab:3a:32:eb:bc:d8:91:
cd:f2:ee:d1:fc:67:3a:51:00:92:bd:b8:68:0b:54:04:d5:07:
0b:97:11:2c:42:64:7c:47:c1:68:b4:eb:21:c4:e4:ad:17:a7:
16:b9:e0:e6:cd:04:c6:89:36:40:d4:4b:c3:f7:7e:26:6b:3a:
d7:68:b3:b2:da:00:65:13:c8:fa:d0:1c:2e:10:ba:71:3e:0f:
aa:8b:d0:ff:b7:3e:83:9c:bc:b3:d1:52:0c:9f:3f:21:4a:10:
dc:8f:ab:38:45:d4:2c:2a:15:2d:71:45:fe:91:a2:d8:d9:dd:
0c:dc:a7:d9:cd:1b:f5:35:fe:14:ba:c5:1f:ed:ee:fb:87:cc:
87:a1:08:c2:2e:ff:5d:af:b3:3d:6e:11:94:79:0b:28:e6:83:
4e:fc:28:8f:7f:00:85:79:7f:3a:d1:07:ee:6e:fa:94:c4:0b:
4b:2c:05:b1:68:00:e8:37:bc:b8:b2:03:5c:5a:ca:13:f2:68:
57:df:ac:fc:da:be:27:24:7e:6d:c4:a9:53:2d:f2:43:0e:30:
9c:82:d5:fb:f1:a2:0a:83:e0:a5:d8:9f:09:3e:99:c8:39:d6:
69:6d:d6:c2:27:70:59:05:3c:3c:7d:d6:41:6a:b4:9c:1f:70:
7e:3e:ee:6f:67:de:95:1d:eb:31:8b:11:c8:0d:a1:25:4e:08:
ef:3a:11:2d:a7:98:0d:a1:d9:30:2d:da:d2:a0:05:6b:34:38:
a6:87:b2:bd:0f:9c:51:cc:e0:2e:a2:1b:a3:a0:a6:eb:1f:0a:
22:70:59:f0:0b:c9:bd:94:4e:1d:65:3b:99:5d:8e:6c:18:82:
1d:b5:cc:6f:14:21:c4:89:07:9b:81:1d:9a:79:ff:bf:fd:ce:
e4:77:11:0f:47:21:dc:d9:79:f3:40:26:56:5c:b4:86:32:8e:
28:b9:14:e7:b3:fe:86:47

View File

@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDyTCCArGgAwIBAgIUeoNdEiqhXVkpcYsmHaKiVS5W/tQwDQYJKoZIhvcNAQEL
BQAwPjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMREwDwYDVQQHDAhTYW4gSm9z
ZTEPMA0GA1UECgwGR29vZ2xlMB4XDTIwMDcxNjE2NTMxOVoXDTQwMDcxMTE2NTMx
OVowgZMxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTERMA8GA1UEBwwIU2FuIEpv
c2UxEjAQBgNVBAoMCUVuZCBQb2ludDEOMAwGA1UECwwFSW5mcmExIjAgBgkqhkiG
9w0BCQEWE2NpbmR5eHVlQGdvb2dsZS5jb20xHDAaBgNVBAMME2Zvby5iYXIuc2Vy
dmVyMy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcIyJEt3ZA
xPn7H5f/IwXS8NcoAXWP8L6rWndcg+EWayx7W+wmUsFKGSFGzrFPPCFmKO8MrQqp
8LSAxHAVtOC6Uw+INWJJw9BRlx2nvV7hfbqu3OnPkPVkN/siUQCqnEKJQHliNT9X
Dl4/Mav75uQSWb3Vfi3KtG7mzPFNNbbe4yfHyGbC4e9RtKkGimDSJ413s3m4+scD
vtpCcCXj9XXZNdCwD1CL3kNdmOdhgfkDBP+AMLBFKZKqpCo6m0s4JJTiej13dc27
wTrnkFm1CP77SV+kQlWg5DAcVXYJkN9FqNqExqIPS/SxMk7H+3qSQACbttQK9UmC
n3qR3pGbwqzNAgMBAAGjaTBnMB8GA1UdIwQYMBaAFG4bi8k0dOd7jSpPQQ6YUDAU
ARaxMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMCwGA1UdEQQlMCOCCmdvb2dsZS5j
b22CCWFwcGxlLmNvbYIKYW1hem9uLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAn5aW
HEHNTDmcgC25oEtCj+IkoAslgFze4ZqkSz0HCzx76vj3AfMmIEvqB0W74wKqeZgm
V0D7I0xHkM3ILH4RjoCotpol3nLooIPFflA6Z1ILTRZl8mE5kfBSHzKdPS0egOf6
kgrNYgJjBEtGNsmq8RKxAHVVAPgH88di0JnQDN5LcV9ZBKTQM2R7EM6a8eWD/Jsi
uujbNtdNERssSBV+Oil93MbsEcOT1RSKKxAiVvkHkR+45GRB889xBnqelcDVqcMK
Vcdp6X7aD5/Bu/4fq9AZlcHSEQDixNtjp/pQR0B5FsCGrb5OAz0B2t9jykDiIyj4
4lxhQz8ykXf7ue0/ag==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,60 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
7a:83:5d:12:2a:a1:5d:59:29:71:8b:26:1d:a2:a2:55:2e:56:fe:d4
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, ST = CA, L = San Jose, O = Google
Validity
Not Before: Jul 16 16:53:19 2020 GMT
Not After : Jul 11 16:53:19 2040 GMT
Subject: C = US, ST = CA, L = San Jose, O = End Point, OU = Infra, emailAddress = cindyxue@google.com, CN = foo.bar.server3.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:dc:23:22:44:b7:76:40:c4:f9:fb:1f:97:ff:23:
05:d2:f0:d7:28:01:75:8f:f0:be:ab:5a:77:5c:83:
e1:16:6b:2c:7b:5b:ec:26:52:c1:4a:19:21:46:ce:
b1:4f:3c:21:66:28:ef:0c:ad:0a:a9:f0:b4:80:c4:
70:15:b4:e0:ba:53:0f:88:35:62:49:c3:d0:51:97:
1d:a7:bd:5e:e1:7d:ba:ae:dc:e9:cf:90:f5:64:37:
fb:22:51:00:aa:9c:42:89:40:79:62:35:3f:57:0e:
5e:3f:31:ab:fb:e6:e4:12:59:bd:d5:7e:2d:ca:b4:
6e:e6:cc:f1:4d:35:b6:de:e3:27:c7:c8:66:c2:e1:
ef:51:b4:a9:06:8a:60:d2:27:8d:77:b3:79:b8:fa:
c7:03:be:da:42:70:25:e3:f5:75:d9:35:d0:b0:0f:
50:8b:de:43:5d:98:e7:61:81:f9:03:04:ff:80:30:
b0:45:29:92:aa:a4:2a:3a:9b:4b:38:24:94:e2:7a:
3d:77:75:cd:bb:c1:3a:e7:90:59:b5:08:fe:fb:49:
5f:a4:42:55:a0:e4:30:1c:55:76:09:90:df:45:a8:
da:84:c6:a2:0f:4b:f4:b1:32:4e:c7:fb:7a:92:40:
00:9b:b6:d4:0a:f5:49:82:9f:7a:91:de:91:9b:c2:
ac:cd
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Authority Key Identifier:
keyid:6E:1B:8B:C9:34:74:E7:7B:8D:2A:4F:41:0E:98:50:30:14:01:16:B1
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
X509v3 Subject Alternative Name:
DNS:google.com, DNS:apple.com, DNS:amazon.com
Signature Algorithm: sha256WithRSAEncryption
9f:96:96:1c:41:cd:4c:39:9c:80:2d:b9:a0:4b:42:8f:e2:24:
a0:0b:25:80:5c:de:e1:9a:a4:4b:3d:07:0b:3c:7b:ea:f8:f7:
01:f3:26:20:4b:ea:07:45:bb:e3:02:aa:79:98:26:57:40:fb:
23:4c:47:90:cd:c8:2c:7e:11:8e:80:a8:b6:9a:25:de:72:e8:
a0:83:c5:7e:50:3a:67:52:0b:4d:16:65:f2:61:39:91:f0:52:
1f:32:9d:3d:2d:1e:80:e7:fa:92:0a:cd:62:02:63:04:4b:46:
36:c9:aa:f1:12:b1:00:75:55:00:f8:07:f3:c7:62:d0:99:d0:
0c:de:4b:71:5f:59:04:a4:d0:33:64:7b:10:ce:9a:f1:e5:83:
fc:9b:22:ba:e8:db:36:d7:4d:11:1b:2c:48:15:7e:3a:29:7d:
dc:c6:ec:11:c3:93:d5:14:8a:2b:10:22:56:f9:07:91:1f:b8:
e4:64:41:f3:cf:71:06:7a:9e:95:c0:d5:a9:c3:0a:55:c7:69:
e9:7e:da:0f:9f:c1:bb:fe:1f:ab:d0:19:95:c1:d2:11:00:e2:
c4:db:63:a7:fa:50:47:40:79:16:c0:86:ad:be:4e:03:3d:01:
da:df:63:ca:40:e2:23:28:f8:e2:5c:61:43:3f:32:91:77:fb:
b9:ed:3f:6a

View File

@@ -0,0 +1,124 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 5 (0x5)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=CA, L=SVL, O=Internet Widgits Pty Ltd
Validity
Not Before: Dec 15 18:05:59 2020 GMT
Not After : May 2 18:05:59 2048 GMT
Subject: C=US, ST=Illinois, L=Chicago, O=Example, Co., CN=localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:d3:4d:01:08:9d:80:de:15:fa:0a:52:ed:2c:f6:
4b:9d:c5:67:2d:b6:41:33:d7:5b:b4:fd:f0:a8:5c:
dd:8a:8d:1f:2d:12:5d:47:88:09:d4:96:ee:63:10:
f9:9d:9a:6c:34:c4:ec:a9:0d:95:6c:48:bc:17:d0:
77:53:93:f8:44:8a:0b:0b:a2:4d:1d:53:f7:55:a7:
ed:6a:35:ad:1d:af:79:bd:d5:c6:5b:96:24:9e:4d:
d8:e9:21:93:e1:93:3b:5d:c4:e3:90:1a:36:d0:f7:
bb:8c:22:e9:d2:f8:29:19:bf:24:c6:52:21:0e:d6:
3a:73:48:ba:e7:81:34:ba:23:21:57:c3:51:0e:59:
51:9f:a6:07:56:17:c4:aa:1b:7b:6c:36:6b:ab:32:
5e:2e:f7:70:dc:eb:f3:da:3b:39:4e:a4:8c:bf:40:
72:ef:00:1e:46:c9:07:6a:93:4c:36:b7:c3:2e:7c:
c5:c1:85:9f:6b:4d:2d:fd:3c:9b:9a:cf:52:b2:fa:
ba:f8:ce:cc:8b:dc:57:7b:ed:37:69:c9:80:dc:a6:
2c:a7:e4:4b:8c:77:cb:2e:28:65:64:61:a4:c8:33:
d9:f8:7d:b7:7c:4f:b7:f4:07:5a:89:ae:2a:59:8c:
ac:00:c7:ce:b0:d0:9b:cd:4d:83:39:55:bb:72:0f:
62:d4:5f:8b:c3:c7:e2:79:95:53:eb:a0:26:0d:52:
7b:4d:40:56:66:2b:55:67:f5:1a:c9:e8:a8:49:bd:
e7:e4:31:9a:e1:8d:80:f2:cc:ab:3d:70:f6:fe:75:
cb:aa:1b:12:d0:6f:d3:c8:df:f1:bd:ce:2b:21:42:
e5:2c:bd:c6:c8:c4:bb:4a:3d:92:48:0d:49:a8:96:
c0:51:ed:30:64:81:dd:ce:05:d4:ff:07:87:59:0b:
13:41:8e:1d:58:e4:47:0c:00:97:12:e4:67:94:24:
67:ef:ed:1e:85:89:df:85:78:10:1f:7f:b2:e8:af:
e7:0f:c7:ec:aa:01:67:b6:0a:9a:23:83:90:1d:0a:
2a:37:80:c3:26:d7:f1:24:29:b3:d8:37:7c:5c:f8:
e3:08:96:1a:34:2d:fa:ff:75:e8:70:25:e2:ab:51:
9d:f7:8a:23:52:89:02:c8:71:ea:cd:a2:89:b6:eb:
6e:c9:fd:fb:dc:63:e8:f9:db:d7:57:d2:0c:fc:56:
9c:16:a8:67:fe:17:88:07:e8:f8:c5:7a:33:72:ad:
b4:5a:43:5b:49:be:79:8b:00:18:83:f7:19:00:07:
94:2c:38:96:61:a7:55:a8:30:db:7e:07:f6:c5:a3:
65:1e:93:2d:6b:5e:d9:a6:0f:fa:c2:19:9b:59:4c:
41:ba:07
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
F3:DC:6A:5B:B7:CE:E9:E1:4D:3E:C4:AE:B7:8E:39:E3:6D:CA:AF:C7
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
X509v3 Subject Alternative Name:
DNS:localhost
Signature Algorithm: sha256WithRSAEncryption
54:13:3d:55:d3:4b:d8:85:f0:54:a8:33:5c:a1:9f:87:79:31:
34:7e:52:b9:46:ea:97:43:fd:0a:9b:ae:2c:8b:74:cd:51:d5:
63:b3:b0:b4:19:a1:da:73:b4:e2:47:0c:d9:33:4a:c6:aa:27:
41:bc:4d:15:74:42:59:eb:41:8e:28:49:f0:55:89:13:f8:f0:
35:1f:e9:b2:ae:48:79:e7:15:a2:aa:59:e0:fd:91:c8:7f:ba:
aa:a4:2e:77:2a:e4:62:fe:c2:83:51:dc:58:83:f8:7f:b2:47:
68:8b:1f:9d:9b:12:25:f2:0f:7d:06:a4:9a:be:a3:af:2d:27:
32:4a:20:fe:5f:98:d0:5d:6a:10:c9:04:49:54:26:60:20:6e:
38:f2:91:a4:24:e7:ed:d2:e4:aa:88:7e:9c:6d:0a:1e:30:97:
a9:9c:45:35:09:fc:45:6c:32:d0:db:79:04:fa:03:7c:36:e5:
27:72:1d:77:a1:d4:12:86:53:bf:93:4e:f7:da:7a:a1:4b:5b:
42:e4:6b:9b:a5:58:f2:ef:30:2e:a6:6f:f7:63:fd:16:b0:35:
81:99:3d:41:dc:da:34:46:ea:1f:02:cc:5e:dc:67:1c:62:68:
76:45:5b:ea:f3:2f:a5:0f:2e:b8:d0:df:ad:8f:3b:03:b4:36:
21:d4:4d:99:09:76:22:a4:61:b4:59:ca:8b:42:5d:64:2a:9a:
4f:7f:67:65:38:4a:c6:e7:8b:ff:36:0d:42:4b:60:03:77:99:
71:1f:ac:f4:83:bb:06:5c:22:fa:ed:4c:c5:37:22:22:11:85:
1a:ab:06:e3:14:c8:71:bf:79:1f:b4:22:64:ea:65:5e:99:c5:
ca:3a:1e:d2:22:96:24:df:65:b1:42:7e:72:21:6b:de:49:cd:
09:45:4d:3e:ff:45:22:f4:01:d5:09:5b:dd:22:cc:25:8f:b2:
5e:b3:35:f8:b3:6d:6e:2b:bd:98:f2:eb:5b:0e:58:31:f3:90:
ec:d9:41:9d:e4:4d:e7:6a:ff:32:63:d1:45:f0:fd:a6:7d:34:
16:e4:c8:0c:79:39:5d:46:e1:28:f1:13:49:a6:02:07:a2:ca:
de:ae:4a:3a:f7:0b:e4:27:49:33:c8:29:2f:cd:6f:0e:5a:be:
e0:5d:c0:98:7e:15:97:ea:a5:ba:84:3b:ab:8d:aa:81:d7:7f:
df:7c:b9:e7:11:6b:7f:82:6e:62:57:d9:ba:25:60:1d:93:49:
eb:48:91:88:2c:22:74:dc:50:cf:6c:44:bd:04:84:fb:97:53:
57:62:56:8b:b1:5c:a2:06:ef:c4:01:21:d6:73:4f:58:79:60:
75:09:53:7d:cb:8c:a2:86
-----BEGIN CERTIFICATE-----
MIIFkzCCA3ugAwIBAgIBBTANBgkqhkiG9w0BAQsFADBLMQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCQ0ExDDAKBgNVBAcMA1NWTDEhMB8GA1UECgwYSW50ZXJuZXQgV2lk
Z2l0cyBQdHkgTHRkMB4XDTIwMTIxNTE4MDU1OVoXDTQ4MDUwMjE4MDU1OVowXTEL
MAkGA1UEBhMCVVMxETAPBgNVBAgMCElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdv
MRUwEwYDVQQKDAxFeGFtcGxlLCBDby4xEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANNNAQidgN4V+gpS7Sz2S53FZy22
QTPXW7T98Khc3YqNHy0SXUeICdSW7mMQ+Z2abDTE7KkNlWxIvBfQd1OT+ESKCwui
TR1T91Wn7Wo1rR2veb3VxluWJJ5N2Okhk+GTO13E45AaNtD3u4wi6dL4KRm/JMZS
IQ7WOnNIuueBNLojIVfDUQ5ZUZ+mB1YXxKobe2w2a6syXi73cNzr89o7OU6kjL9A
cu8AHkbJB2qTTDa3wy58xcGFn2tNLf08m5rPUrL6uvjOzIvcV3vtN2nJgNymLKfk
S4x3yy4oZWRhpMgz2fh9t3xPt/QHWomuKlmMrADHzrDQm81NgzlVu3IPYtRfi8PH
4nmVU+ugJg1Se01AVmYrVWf1GsnoqEm95+QxmuGNgPLMqz1w9v51y6obEtBv08jf
8b3OKyFC5Sy9xsjEu0o9kkgNSaiWwFHtMGSB3c4F1P8Hh1kLE0GOHVjkRwwAlxLk
Z5QkZ+/tHoWJ34V4EB9/suiv5w/H7KoBZ7YKmiODkB0KKjeAwybX8SQps9g3fFz4
4wiWGjQt+v916HAl4qtRnfeKI1KJAshx6s2iibbrbsn9+9xj6Pnb11fSDPxWnBao
Z/4XiAfo+MV6M3KttFpDW0m+eYsAGIP3GQAHlCw4lmGnVagw234H9sWjZR6TLWte
2aYP+sIZm1lMQboHAgMBAAGjcDBuMB0GA1UdDgQWBBTz3Gpbt87p4U0+xK63jjnj
bcqvxzAfBgNVHSMEGDAWgBRapdqxmdTlDuYelOr//GLi7QnxBjAJBgNVHRMEAjAA
MAsGA1UdDwQEAwIFoDAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQEL
BQADggIBAFQTPVXTS9iF8FSoM1yhn4d5MTR+UrlG6pdD/QqbriyLdM1R1WOzsLQZ
odpztOJHDNkzSsaqJ0G8TRV0QlnrQY4oSfBViRP48DUf6bKuSHnnFaKqWeD9kch/
uqqkLncq5GL+woNR3FiD+H+yR2iLH52bEiXyD30GpJq+o68tJzJKIP5fmNBdahDJ
BElUJmAgbjjykaQk5+3S5KqIfpxtCh4wl6mcRTUJ/EVsMtDbeQT6A3w25SdyHXeh
1BKGU7+TTvfaeqFLW0Lka5ulWPLvMC6mb/dj/RawNYGZPUHc2jRG6h8CzF7cZxxi
aHZFW+rzL6UPLrjQ362POwO0NiHUTZkJdiKkYbRZyotCXWQqmk9/Z2U4Ssbni/82
DUJLYAN3mXEfrPSDuwZcIvrtTMU3IiIRhRqrBuMUyHG/eR+0ImTqZV6Zxco6HtIi
liTfZbFCfnIha95JzQlFTT7/RSL0AdUJW90izCWPsl6zNfizbW4rvZjy61sOWDHz
kOzZQZ3kTedq/zJj0UXw/aZ9NBbkyAx5OV1G4SjxE0mmAgeiyt6uSjr3C+QnSTPI
KS/Nbw5avuBdwJh+FZfqpbqEO6uNqoHXf998uecRa3+CbmJX2bolYB2TSetIkYgs
InTcUM9sRL0EhPuXU1diVouxXKIG78QBIdZzT1h5YHUJU33LjKKG
-----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,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDcIyJEt3ZAxPn7
H5f/IwXS8NcoAXWP8L6rWndcg+EWayx7W+wmUsFKGSFGzrFPPCFmKO8MrQqp8LSA
xHAVtOC6Uw+INWJJw9BRlx2nvV7hfbqu3OnPkPVkN/siUQCqnEKJQHliNT9XDl4/
Mav75uQSWb3Vfi3KtG7mzPFNNbbe4yfHyGbC4e9RtKkGimDSJ413s3m4+scDvtpC
cCXj9XXZNdCwD1CL3kNdmOdhgfkDBP+AMLBFKZKqpCo6m0s4JJTiej13dc27wTrn
kFm1CP77SV+kQlWg5DAcVXYJkN9FqNqExqIPS/SxMk7H+3qSQACbttQK9UmCn3qR
3pGbwqzNAgMBAAECggEAU0Te84tKKdnYjUs4HYRL8ay0VienJpl0JjEEMXSZMfe8
TbVJsH1hK/wxgC0zGLuwDoqxUeQqwnmQbZzgoPVYhGJi360BztFI/XPh/c8+EqGS
eg6KSr+UcyJR1ns5e0+8Q1qmD6YAnZeLwu+xFIoT/3T+v8EI5UI3KQqgxAnrcIdc
RWqIwWLkkPm1QVRYsvRaTvmgFd2LcIT9AdIruP/VsqF3GEzvEQSr0lgmwOe0izLb
HKfZr+2JwOppwTLGhKo1wUyUUsglXCBOcFYAA03xdviQWBuCeKXamNrdB7M0T4zZ
THheNRQq2g7bTYnncCYKcrNa5VY5wmHY767mf6ahAQKBgQD+AGk21CsRgiKCGzUI
wTRynutAMkX55U1bud2+OMpzH9omvnccX5ezq5WTw/jZfz+jrUBF8YKSTtb3InUg
yXcpJ/XjRDFMZp1Avy/44rOlYg1QYMD4JK96f8bbd1yej5NVw45V9synaJHuvoDV
bkbZu00S0X8Pgvlh354MUH3CLQKBgQDd3oQdKMZgsX8gtmcQfQoEZ0VvlOYmTM5W
Kw+24iGrBkfBt+NuKx8qm1CpoFGx4G2+TgttMywjF9RG3R2uGqbJZbCOAXzjRMQJ
L9PuTiGAdYD3fA5cTmnrrNEhPydtRhF2M3p8FFeQtwsEBYreXux25rbmVOYTFMgJ
hVUW9RdZIQKBgQDwEYdgMQw70hm3iuuHSMS/iQCkfl+xH08MYRH6FkcSpIpVkDOX
96m0QXpwXQs41pJZqwhSkz9r9WQr1L+Lq58aoRBAK1XE9j+u0IUQ4YQVziTzUV9R
qarJRze2eoxpuR3yM5C2IzuvBqDXW+r8zuvcIrFoFeXXzVzTar1AulsCSQKBgQCa
UD+3QDrp2co/6F26vB0RfvpuZzPEA7undv/RBWrBVvblp46Je3iL28a4lAb+Hsh1
ijasVuEl71b3iqcwBt1mSlIIEsTYFWX7tcZDgxgODqwKdcBPN0K4ZlR2OUSk3g0b
Fybj0gotXwJMY8Z4b7Er6b/gZ8A2GUggRxotg34fwQKBgEh6a88SgTyu1KcR2yzN
Zbs8MEfZ8hqfUj+GL0+6y9KQd4uSIngyHWGNETE9dCObNz5HEdJIpUNvB3vfN0Vk
f1EEPDQLQKJjii7jZ9U9XfPVaUhqIVH3Aupmb30H3AQw7XOF23g63k9Yg1FWtDT6
4CuBsUvjXywL4yHrWK+BuSXF
-----END PRIVATE KEY-----

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,33 @@
-----BEGIN CERTIFICATE-----
MIIFpTCCA42gAwIBAgIUTdt7HKlUedh94k4eA+nlamVgGSkwDQYJKoZIhvcNAQEL
BQAwYTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMSEwHwYDVQQKDBhJbnRlcm5l
dCBXaWRnaXRzIFB0eSBMdGQxIjAgBgNVBAMMGWZvby5iYXIuc2VydmVyMi50cnVz
dC5jb20wIBcNMjAwMTA5MjI0NDA5WhgPMjIyNTA1MTQyMjQ0MDlaMGExCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJDQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMSIwIAYDVQQDDBlmb28uYmFyLnNlcnZlcjIudHJ1c3QuY29tMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2KadB/xdPMRDW/LhFGJcbzVU+yoS
iRudc8w0Wq/0XpQwcDjxrq6v5XuzFIZU46Wb2g+eALNMjW7zv4BLFwEU0+CvMYWt
vgbTA2A07sU7P3WA8uZwjB25nkk0iMVBclL+g1XABnfXNobbKB/dyKArlyzFBV+w
rpV17RdkfXfGjeFWpfxF7KF4Wzh86XKSDYSQQE4kcQqSxDeZfRwm02jaXuPDmvUw
KFIxcfEW/3SadulFvOKgHWjUEirTGsT+8B8fWsfeJjGRmFcc1+utpOoOaC1+sRe6
xTe7JJB9F13mZxEPJuFxuBvjmGiSXkyLWhVWeqzhTipojZ69mYzAxMs8AVWrYeru
EKuf3MlABub8dgDLocvOYD3A0IDm5173pU5RPW9tA2jBNLnyEF+wYFLjtFfYQesl
UlldccG+nZowaeUsiUPhTBzwAYSCdB+imtJxIT0xdOQCo+h9ASvnPpgk6AYaU/2d
gsFY39CvKmTFYlH2EGIJK3MWm6YT3T1fTTUgs/s++CkLzwAXpna4w8SLDl3IdeLX
lMiXhnoNr3uYeusxkJp5rtUHBsYPbH4Ec4erNRgbUuBnHJe4nlC6LCCycLHywhBr
niPPxyNBZzvrmRrVwx5xNEQn8r4ffftpASY/uePJK2wtrZop7mWFo/OnfMO6y/3C
22FK5wIbVLLsDlECAwEAAaNTMFEwHQYDVR0OBBYEFGOI6k3QPu9e+EORdUDkFqsV
szK5MB8GA1UdIwQYMBaAFGOI6k3QPu9e+EORdUDkFqsVszK5MA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggIBAMJtk0AbpT3pu+2G+NK3D4T2brrP66An
lRlQxDXQ0uKunGYMgam+sJWMz3agviekRVQk9Vog9FwiGoYsS3X6ojLrA1FXp/8h
oVXNmW8R87IS2KyPbzTmO+0OvO/KhYmA0USIhAmj645fyy8dGCQQOZCSfXE5/zCM
ODnrgeai3qw+KB4aGJ6fgDKMdPbyl7fyvu5EWDIycuij9S8FQJ7m2gWolxFAN4/c
nnWr/s6n8AQrb+k4Dp50nOrDA7JUEnFfQcBuJpDN2v5MD1/x83R1ZVuqNa+fOgrW
DdSm/XbaPpzZa/R6iJQxG8mNpNEjMnBq7WCa1tLLd7MrdxzrwaFdfRiMj91b/A4W
GZbX7SMrByI/6M01YoTdsPW2i/EDxJjghSGkvwuA2MPe8UqXELn5wpTXTDgCsj8V
j25GUupDB8Dm5aocLEFHiUwzAGcy19zVqepTaM4w//iA1qUuaG7DE8pVzL9XFxm+
L1CGfxSTqdbqWa9PcLUoTI/8n6KQdK+vczgY4y+aUOZdGgLcVoO1BF6McnNPiihk
d+HdWb0xGjw63XsV5kC41y6mHBQJdJTm0CE+yZ1e6gt+YEZCELxpxg530J0CngHs
tCftzNI8o2pQhDhhKzxxGiA1cuzrrLDpdqNZo6VNm5tyYPicVbicZoJSbNDxohEJ
rzhu9hQ7iDV5
-----END CERTIFICATE-----

View File

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

View File

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
m4DEMelbu4jDoUwmms+yra/9chKHzaRB
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2017 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 testdata contains functionality to find data files in tests.
package testdata
import (
"path/filepath"
"runtime"
)
// basepath is the root directory of this package.
var basepath string
func init() {
_, currentFile, _, _ := runtime.Caller(0)
basepath = filepath.Dir(currentFile)
}
// Path returns the absolute path the given relative file or directory path,
// relative to the google.golang.org/grpc/testdata directory in the user's GOPATH.
// If rel is already absolute, it is returned unmodified.
func Path(rel string) string {
if filepath.IsAbs(rel) {
return rel
}
return filepath.Join(basepath, rel)
}