Skip to content

Commit ff6ff9d

Browse files
authored
Merge pull request #9570 from gyuho/tls
*: fix TLS reload when cert includes only IPs (no domain names in SAN field)
2 parents 00b529e + 0b0a943 commit ff6ff9d

28 files changed

+553
-249
lines changed

CHANGELOG-3.2.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.2.18...v3.2.19) and
1111

1212
### Security, Authentication
1313

14-
- Fix [TLS reload](TODO) when [cert SAN field only contains IP addresses](https://github.com/coreos/etcd/issues/9541).
14+
- Fix [TLS reload](https://github.com/coreos/etcd/pull/9570) when [certificate SAN field only includes IP addresses but no domain names](https://github.com/coreos/etcd/issues/9541).
15+
- In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
16+
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/coreos/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
17+
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
1518

1619

1720
## [v3.2.18](https://github.com/coreos/etcd/releases/tag/v3.2.18) (2018-03-29)

CHANGELOG-3.3.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.3.3...v3.3.4) and [
1111

1212
### Security, Authentication
1313

14-
- Fix [TLS reload](TODO) when [cert SAN field only contains IP addresses](https://github.com/coreos/etcd/issues/9541).
14+
- Fix [TLS reload](https://github.com/coreos/etcd/pull/9570) when [certificate SAN field only includes IP addresses but no domain names](https://github.com/coreos/etcd/issues/9541).
15+
- In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
16+
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/coreos/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
17+
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
1518

1619

1720
## [v3.3.3](https://github.com/coreos/etcd/releases/tag/v3.3.3) (2018-03-29)

CHANGELOG-3.4.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,10 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
103103
- Support [`ttl` field for `etcd` Authentication JWT token](https://github.com/coreos/etcd/pull/8302).
104104
- e.g. `etcd --auth-token jwt,pub-key=<pub key path>,priv-key=<priv key path>,sign-method=<sign method>,ttl=5m`.
105105
- Allow empty token provider in [`etcdserver.ServerConfig.AuthToken`](https://github.com/coreos/etcd/pull/9369).
106-
- Fix [TLS reload](TODO) when [cert SAN field only contains IP addresses](https://github.com/coreos/etcd/issues/9541).
106+
- Fix [TLS reload](https://github.com/coreos/etcd/pull/9570) when [certificate SAN field only includes IP addresses but no domain names](https://github.com/coreos/etcd/issues/9541).
107+
- In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
108+
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/coreos/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
109+
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
107110

108111
### Added: `etcd`
109112

Documentation/op-guide/security.md

+16
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,22 @@ I | embed: serving client requests on 127.0.0.1:22379
321321
I | embed: serving client requests on 127.0.0.1:2379
322322
```
323323

324+
[v3.2.19](https://github.com/coreos/etcd/blob/master/CHANGELOG-3.2.md) and [v3.3.4](https://github.com/coreos/etcd/blob/master/CHANGELOG-3.3.md) fixes TLS reload when [certificate SAN field only includes IP addresses but no domain names](https://github.com/coreos/etcd/issues/9541). For example, a member is set up with CSRs (with `cfssl`) as below:
325+
326+
```json
327+
{
328+
"CN": "etcd.local",
329+
"hosts": [
330+
"127.0.0.1"
331+
],
332+
```
333+
334+
In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
335+
336+
However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/coreos/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
337+
338+
Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
339+
324340
## Notes for Host Whitelist
325341

326342
`etcd --host-whitelist` flag specifies acceptable hostnames from HTTP client requests. Client origin policy protects against ["DNS Rebinding"](https://en.wikipedia.org/wiki/DNS_rebinding) attacks to insecure etcd servers. That is, any website can simply create an authorized DNS name, and direct DNS to `"localhost"` (or any other address). Then, all HTTP endpoints of etcd server listening on `"localhost"` becomes accessible, thus vulnerable to DNS rebinding attacks. See [CVE-2018-5702](https://bugs.chromium.org/p/project-zero/issues/detail?id=1447#c2) for more detail.

integration/cluster.go

+61-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646
"github.com/coreos/etcd/etcdserver/api/v3rpc"
4747
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
4848
"github.com/coreos/etcd/pkg/testutil"
49+
"github.com/coreos/etcd/pkg/tlsutil"
4950
"github.com/coreos/etcd/pkg/transport"
5051
"github.com/coreos/etcd/pkg/types"
5152
"github.com/coreos/etcd/rafthttp"
@@ -83,13 +84,27 @@ var (
8384
ClientCertAuth: true,
8485
}
8586

87+
testTLSInfoIP = transport.TLSInfo{
88+
KeyFile: "./fixtures/server-ip.key.insecure",
89+
CertFile: "./fixtures/server-ip.crt",
90+
TrustedCAFile: "./fixtures/ca.crt",
91+
ClientCertAuth: true,
92+
}
93+
8694
testTLSInfoExpired = transport.TLSInfo{
8795
KeyFile: "./fixtures-expired/server.key.insecure",
8896
CertFile: "./fixtures-expired/server.crt",
8997
TrustedCAFile: "./fixtures-expired/ca.crt",
9098
ClientCertAuth: true,
9199
}
92100

101+
testTLSInfoExpiredIP = transport.TLSInfo{
102+
KeyFile: "./fixtures-expired/server-ip.key.insecure",
103+
CertFile: "./fixtures-expired/server-ip.crt",
104+
TrustedCAFile: "./fixtures-expired/ca.crt",
105+
ClientCertAuth: true,
106+
}
107+
93108
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "integration")
94109
)
95110

@@ -110,6 +125,9 @@ type ClusterConfig struct {
110125

111126
ClientMaxCallSendMsgSize int
112127
ClientMaxCallRecvMsgSize int
128+
129+
// UseIP is true to use only IP for gRPC requests.
130+
UseIP bool
113131
}
114132

115133
type cluster struct {
@@ -248,6 +266,7 @@ func (c *cluster) mustNewMember(t *testing.T) *member {
248266
grpcKeepAliveTimeout: c.cfg.GRPCKeepAliveTimeout,
249267
clientMaxCallSendMsgSize: c.cfg.ClientMaxCallSendMsgSize,
250268
clientMaxCallRecvMsgSize: c.cfg.ClientMaxCallRecvMsgSize,
269+
useIP: c.cfg.UseIP,
251270
})
252271
m.DiscoveryURL = c.cfg.DiscoveryURL
253272
if c.cfg.UseGRPC {
@@ -511,6 +530,7 @@ type member struct {
511530
keepDataDirTerminate bool
512531
clientMaxCallSendMsgSize int
513532
clientMaxCallRecvMsgSize int
533+
useIP bool
514534
}
515535

516536
func (m *member) GRPCAddr() string { return m.grpcAddr }
@@ -527,6 +547,7 @@ type memberConfig struct {
527547
grpcKeepAliveTimeout time.Duration
528548
clientMaxCallSendMsgSize int
529549
clientMaxCallRecvMsgSize int
550+
useIP bool
530551
}
531552

532553
// mustNewMember return an inited member with the given name. If peerTLS is
@@ -600,6 +621,7 @@ func mustNewMember(t *testing.T, mcfg memberConfig) *member {
600621
}
601622
m.clientMaxCallSendMsgSize = mcfg.clientMaxCallSendMsgSize
602623
m.clientMaxCallRecvMsgSize = mcfg.clientMaxCallRecvMsgSize
624+
m.useIP = mcfg.useIP
603625

604626
m.InitialCorruptCheck = true
605627

@@ -610,6 +632,9 @@ func mustNewMember(t *testing.T, mcfg memberConfig) *member {
610632
func (m *member) listenGRPC() error {
611633
// prefix with localhost so cert has right domain
612634
m.grpcAddr = "localhost:" + m.Name
635+
if m.useIP { // for IP-only sTLS certs
636+
m.grpcAddr = "127.0.0.1:" + m.Name
637+
}
613638
l, err := transport.NewUnixListener(m.grpcAddr)
614639
if err != nil {
615640
return fmt.Errorf("listen failed on grpc socket %s (%v)", m.grpcAddr, err)
@@ -785,10 +810,45 @@ func (m *member) Launch() error {
785810
if m.ClientTLSInfo == nil {
786811
hs.Start()
787812
} else {
788-
hs.TLS, err = m.ClientTLSInfo.ServerConfig()
813+
info := m.ClientTLSInfo
814+
hs.TLS, err = info.ServerConfig()
789815
if err != nil {
790816
return err
791817
}
818+
819+
// baseConfig is called on initial TLS handshake start.
820+
//
821+
// Previously,
822+
// 1. Server has non-empty (*tls.Config).Certificates on client hello
823+
// 2. Server calls (*tls.Config).GetCertificate iff:
824+
// - Server's (*tls.Config).Certificates is not empty, or
825+
// - Client supplies SNI; non-empty (*tls.ClientHelloInfo).ServerName
826+
//
827+
// When (*tls.Config).Certificates is always populated on initial handshake,
828+
// client is expected to provide a valid matching SNI to pass the TLS
829+
// verification, thus trigger server (*tls.Config).GetCertificate to reload
830+
// TLS assets. However, a cert whose SAN field does not include domain names
831+
// but only IP addresses, has empty (*tls.ClientHelloInfo).ServerName, thus
832+
// it was never able to trigger TLS reload on initial handshake; first
833+
// ceritifcate object was being used, never being updated.
834+
//
835+
// Now, (*tls.Config).Certificates is created empty on initial TLS client
836+
// handshake, in order to trigger (*tls.Config).GetCertificate and populate
837+
// rest of the certificates on every new TLS connection, even when client
838+
// SNI is empty (e.g. cert only includes IPs).
839+
//
840+
// This introduces another problem with "httptest.Server":
841+
// when server initial certificates are empty, certificates
842+
// are overwritten by Go's internal test certs, which have
843+
// different SAN fields (e.g. example.com). To work around,
844+
// re-overwrite (*tls.Config).Certificates before starting
845+
// test server.
846+
tlsCert, err := tlsutil.NewCert(info.CertFile, info.KeyFile, nil)
847+
if err != nil {
848+
return err
849+
}
850+
hs.TLS.Certificates = []tls.Certificate{*tlsCert}
851+
792852
hs.StartTLS()
793853
}
794854
closer := func() {

integration/fixtures-expired/ca.crt

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
-----BEGIN CERTIFICATE-----
2-
MIID0jCCArqgAwIBAgIUbY6SSy/rF2TQzWsH4GxG+h+Pvw8wDQYJKoZIhvcNAQEL
2+
MIID0jCCArqgAwIBAgIUEKEIOO1O97Bz4car+7SHDxT5tB4wDQYJKoZIhvcNAQEL
33
BQAwbzEMMAoGA1UEBhMDVVNBMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
44
Ew1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKEwRldGNkMRYwFAYDVQQLEw1ldGNkIFNl
5-
Y3VyaXR5MQswCQYDVQQDEwJjYTAeFw0xODA0MDgxNzUzMDBaFw0yODA0MDUxNzUz
5+
Y3VyaXR5MQswCQYDVQQDEwJjYTAeFw0wMDA0MTMxODU1MDBaFw0xMDA0MTExODU1
66
MDBaMG8xDDAKBgNVBAYTA1VTQTETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE
77
BxMNU2FuIEZyYW5jaXNjbzENMAsGA1UEChMEZXRjZDEWMBQGA1UECxMNZXRjZCBT
88
ZWN1cml0eTELMAkGA1UEAxMCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
9-
AoIBAQCqhEOeNSLK5CcfvZgHFHPJzRWeDc/fAQ3U2GSF1+KEslOA0mmHiL1paloS
10-
CbuwzoY/EGPCudFxIwFwjl2BAxbMdaCAKCxPwMHfn/38I45GgJFODjcOP0AX9i3O
11-
z2jsAGm02HNicmF24TuQgij8lvhhKjNsy2Lrb8/i6NmX8AKZl9smkRRd5HpUz9DD
12-
HelH2CXYCjbGXdpCyjN2PwfGSoCsAV8NDwbe0CAg6+dZCQrbqt2PJE2uRBoLgp3p
13-
AsVdPiFL1igOimgQRShGvMEVLkA7cmB3fALZy1WTGGj4h76HtEz8nywN7PmoWQJv
14-
AZFM168XPQ35S9+1CROtWUoM7dlhAgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjAS
15-
BgNVHRMBAf8ECDAGAQH/AgECMB0GA1UdDgQWBBSLaEU8nqrYzNEcmi0oZKd1AAFK
16-
gTAfBgNVHSMEGDAWgBSLaEU8nqrYzNEcmi0oZKd1AAFKgTANBgkqhkiG9w0BAQsF
17-
AAOCAQEApPHGwdcMRWMk+RS1NVb3yCPdf2Tx8pPYAJpLY46OPenGnFt6+wJs6Nhq
18-
bj9zmEEqyn1WLXtuel+X4E4BEofkTEAM+06UT7SGgEF7zMY+zQjfPqD52jLhS11I
19-
hp3u/hDR5c8r6RmvuH1TiPK5twxmV1w6LRGQcGJtw1PdTVfgHM+1s7kQ+Ineo4kK
20-
8m1JR44B3GHyw+o0jsf5NqnmQnW6aMACQXiX93fnelkPOsKez/oxiy/WK5dDMrzH
21-
JgNonK+bZRpef15XK3EOhmHp8YrY0CEq4MFsxxmkMZT0OnvIMEi9SkPV1cFq2N7r
22-
uTB9aMzzD/1u+3+IpHCrkb0QICj3YQ==
9+
AoIBAQDFeNJ9r2TFcJp9UHS42QN2NN1A96LQXxn/BirHzXdeTk6YEe0eloA91SJT
10+
BAae7aGdPMkpMyAAXheGPGHAbSde5dONYx2QE4nqWRl79v6kDbX6EmqwpzTOGD/T
11+
UfLXe65g6w6kaXcNZWiMdqfkUImke/WWM1qunsCKoGOXF8Jg1DLy7NSjqT2Kg1UP
12+
evJ5GOrWmIj5rEnEvW0ohR7mKV23xl5okVjrlzCi+arWDdl5RzE0I9x7vKNE0TKX
13+
NNHG9hMSJQ/ipXXXyMcahqGZXtkGvOpwpO3lpsGjo3WIUZMQW2FA3xR0nBC6Lt+0
14+
d+7IXOy/LbzXpkcL8Ws5BZuLDSKLAgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjAS
15+
BgNVHRMBAf8ECDAGAQH/AgECMB0GA1UdDgQWBBT5Z7kwwTNntO1UsuMdUv/Yq3Ad
16+
cDAfBgNVHSMEGDAWgBT5Z7kwwTNntO1UsuMdUv/Yq3AdcDANBgkqhkiG9w0BAQsF
17+
AAOCAQEAkFF/fIVlcgj1uRL36wP4DgkOMCpU5+vwlDdHihDzRJHZqik+3+oNz7DD
18+
pRIURHMeeF+Wk5/GRQ/oGzKYotNLLzqCOggnLCxET6Hkb07vfve91HmYVOYix5pU
19+
GPW8+M3XyFTL3+2BnPpqPpJWpJ28g+N3eQjAG8rIbjXESdxrpJFKY22nMbtyS1rH
20+
dyzf3OO4S7LZiRQx0nuD9SZtX2vj5DyN8Am/zieSYm+GCtJsvIiDoB+Uhndnxxt0
21+
FA0/89vGJ1gCo+Z6clzqBIbesRUBnLvPbUdpxhFAtjUKZhQv05IrE81/GP7F7kEr
22+
oODS2+D5WC6mKDO4v2k736OTw6HwOQ==
2323
-----END CERTIFICATE-----

integration/fixtures-expired/gencerts.sh

+11
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,19 @@ cfssl gencert \
2424
./server-ca-csr.json | cfssljson --bare ./server
2525
mv server.pem server.crt
2626
mv server-key.pem server.key.insecure
27+
28+
# generate IP: 127.0.0.1, CN: example.com certificates
29+
cfssl gencert \
30+
--ca ./ca.crt \
31+
--ca-key ./ca-key.pem \
32+
--config ./gencert.json \
33+
./server-ca-csr-ip.json | cfssljson --bare ./server-ip
34+
mv server-ip.pem server-ip.crt
35+
mv server-ip-key.pem server-ip.key.insecure
36+
2737
if which openssl >/dev/null; then
2838
openssl x509 -in ./server.crt -text -noout
39+
openssl x509 -in ./server-ip.crt -text -noout
2940
fi
3041

3142
rm -f *.csr *.pem *.stderr *.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"key": {
3+
"algo": "rsa",
4+
"size": 2048
5+
},
6+
"names": [
7+
{
8+
"O": "etcd",
9+
"OU": "etcd Security",
10+
"L": "San Francisco",
11+
"ST": "California",
12+
"C": "USA"
13+
}
14+
],
15+
"CN": "example.com",
16+
"hosts": [
17+
"127.0.0.1"
18+
]
19+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEBzCCAu+gAwIBAgIUOc4vrxQ6OeHoGslhL7daP1Ye8ZYwDQYJKoZIhvcNAQEL
3+
BQAwbzEMMAoGA1UEBhMDVVNBMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
4+
Ew1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKEwRldGNkMRYwFAYDVQQLEw1ldGNkIFNl
5+
Y3VyaXR5MQswCQYDVQQDEwJjYTAeFw0wMDA0MTMxODU1MDBaFw0wMDA0MTMxOTU1
6+
MDBaMHgxDDAKBgNVBAYTA1VTQTETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE
7+
BxMNU2FuIEZyYW5jaXNjbzENMAsGA1UEChMEZXRjZDEWMBQGA1UECxMNZXRjZCBT
8+
ZWN1cml0eTEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
9+
A4IBDwAwggEKAoIBAQDLUZuwXwiky2VvujM0EOP9LL85sx1dKLc/16hOl6qYRPOg
10+
PH7zsmXMVndsD2Fi9NDbhV9rVHfVNkCyZO/D81u52UEyr2uSFHOfIqLkFGKvcxhO
11+
FtOLA7wTjzHiYO1pFgqYBWzSfIyreYYo13tCYxHUlhn3ibqvCz9fimGsQmswhUiP
12+
yaC4C8iBICWNd4vrXHhtKb5pHHzUDFHkOxKF6VS9f7InKBy2yTr8ekgoEYyE3gtp
13+
ncoVbVlwxehChbZThFi0xsQc/kG/eoyGznKo9RUlUW+h3SEJR3bYizYP76ZwWXus
14+
nP5vgLmZ0wIi/689uTQbEAK438rK3xTSziPv6B51AgMBAAGjgZEwgY4wDgYDVR0P
15+
AQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMB
16+
Af8EAjAAMB0GA1UdDgQWBBR8bgC5SVrIrOAkjED8FB6OThk0IjAfBgNVHSMEGDAW
17+
gBT5Z7kwwTNntO1UsuMdUv/Yq3AdcDAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3
18+
DQEBCwUAA4IBAQDEV4Ec8TpDXvFTJYXrpME5KnKvtq1fEv100jc88cmlGb/rygge
19+
MtisA1rYSaSEPMF0j7HoMtTwP90yrJCBTr7/vziAXCZU2H6bg24exRzqtMDpDhXg
20+
mvqkqvMVFem8ANIF3a+qXPY/pzjh4xrPuOw10TfG0bE576lAY/KbnY3UvXo6QL54
21+
AMyimFhq8e9dJ7JnO3eaYmJv6oSjKjqNYSU+01UfxEJGNbx1IELMDlnVKX0Zmn9p
22+
YbUS3nrowKoVXpuca9KzS1pINgqVsztF5XJxzqlcDwERR/QcTKwUgQ0y0BBRqiGg
23+
WdtbyamFufvF8GPsNJ0KRHXSIRRXF7hbgiXd
24+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEogIBAAKCAQEAy1GbsF8IpMtlb7ozNBDj/Sy/ObMdXSi3P9eoTpeqmETzoDx+
3+
87JlzFZ3bA9hYvTQ24Vfa1R31TZAsmTvw/NbudlBMq9rkhRznyKi5BRir3MYThbT
4+
iwO8E48x4mDtaRYKmAVs0nyMq3mGKNd7QmMR1JYZ94m6rws/X4phrEJrMIVIj8mg
5+
uAvIgSAljXeL61x4bSm+aRx81AxR5DsShelUvX+yJygctsk6/HpIKBGMhN4LaZ3K
6+
FW1ZcMXoQoW2U4RYtMbEHP5Bv3qMhs5yqPUVJVFvod0hCUd22Is2D++mcFl7rJz+
7+
b4C5mdMCIv+vPbk0GxACuN/Kyt8U0s4j7+gedQIDAQABAoIBAAjHl1+AWxEyr0ip
8+
07g12oJ+QiutrmDtdyxMlbn/FqDIqXSL6DeBxp+SREnoSB5L0BEKq1opJZuRYi3R
9+
6gCeK6HU3dngdVazh2KhzkLnFnPZFn2Ywr3IBYEat968rMPS7dYutcpJEpH9B2wQ
10+
EgSF3qk9ahWkXulcJPptMVaM77ACnZk6yYsPDPqjX/zsCXVga59QL0x1n2ai50er
11+
W7kthCj69zZP6crbnjyCUDjNdpDio7xurvvxs0k1KWmcN9QjdOyFXDFgTUnphIFX
12+
pEyVhu+LmLzFKc1WVQ4sIAt6ot9kpWt+cdaBVIWl2yCmqF4nbJ38DDG6wLXaZQd1
13+
DgEL0YECgYEAzt7QukPfjgw5CKZguQVVn0LGYdHw47qHiusjABzYH4mokMHqR/r5
14+
LIIRQ4JjB/vpxavj6B0e73tcfwbSzLSwsRI9/6Z27UVpXnpU5LY7+46d+ZXsQorE
15+
8jeUX6ZQi65ujpFFKkftKlmq67XJtmSh2T+3dMqRXmFWVZThllBJcGUCgYEA+5rd
16+
gvZhaj9Rng1CwK3FoI/mp0BtSL+TE8/JbV0yA5X6NhXlts/ysafFZsj9RkR1NhXL
17+
ql8Bl9RxrV6mTIz6/76NC39ZUQUe5FZGv64rqjoFwOnv6ap1/8ntDFy29DgZ5Dqn
18+
flAtbbEyVG+VCwwhDgUT+FTNNS1eg18GStr6LNECgYASgo1anUgbhax0wa5V38xR
19+
e8AUcJyFQ+Ns4q03DV2pNMAIc9Fqr2IsQVcaG0iRJlE8hqzV0AU8mGUmWI30Exbc
20+
QS2a+mIZyOQst/VwoX2sfI5WDrwdGB2XLrHv/Qmn9euehhESP21RJMTOYm2yDD8P
21+
GUxo/tcTAtKexbuJn5VyoQKBgB7OH0DhmZvAlOWdCgc9P20hMURZBwhZLFDIqAjT
22+
2EPIIRJuK+nuG/DUcb7b7OalixRMJtt9Nly4jhKD/ChzOmgFlI9L0EuzLM0YIyFk
23+
2cPFxt6Pxef+DuR6fKN+1oegNstSwx8cAfPkNh1QbBcmLQXiaUeGWnmgTGoZQFP5
24+
65eBAoGAfV98Mwka+VJ3hYNPL2ZHUXHnXw9Hnf5NnaGfgz7/Ucw3H88HsrIDIZgO
25+
NKSM3NVRIrweAx8/gDIrGqjXkvrwuCqXXYeS23gRteigUpoQrtGjBxIwtalT8K2O
26+
jI4vqz8SsNALtR8nehmBPzTj+t+rF5b1cMfyreHccoAa+0TbPac=
27+
-----END RSA PRIVATE KEY-----

0 commit comments

Comments
 (0)