Skip to content

Commit 1f49bfb

Browse files
committed
[FAB-2714] Enable peer to start with TLS enabled
https://jira.hyperledger.org/browse/FAB-2714 Prior to this change, the peer did not honor the TLS setting in core.yaml. With this change, the peer will now read the peer.tls.* settings to determine whether or not to start the main peer server with TLS enabled. This following changes were made: - core/comm/testdata/certs/generate.go - needed to add usage extension to be able to use server certs as client certs - core/peer/peer.go - added package scoped peerServer variable in order to support dynamic updates from config transactions - peer/node/start.go - loads TLS config and calls function in core/peer package in order to create the peer server The tests do include a test for mutual (client) TLS authentication, but this is not yet enabled for the peer on start up Change-Id: I9a6d19b928c36b7500d004a57b7d76e40bb18fd9 Signed-off-by: Gari Singh <[email protected]>
1 parent 99c8a68 commit 1f49bfb

17 files changed

+412
-8
lines changed

core/comm/testdata/certs/generate.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ func genServerCertificateECDSA(name string, signKey *ecdsa.PrivateKey, signCert
128128
return err
129129
}
130130

131-
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
131+
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth,
132+
x509.ExtKeyUsageClientAuth}
132133

133134
//set the organization for the subject
134135
subject := subjectTemplate()
@@ -262,7 +263,7 @@ func main() {
262263
}
263264
}
264265
//generate client certificates for the org
265-
for k := 1; k <= *numServerCerts; k++ {
266+
for k := 1; k <= *numClientCerts; k++ {
266267
err := genClientCertificateECDSA(fmt.Sprintf(baseOrgName+"%d-client%d", i, k), signKey, signCert)
267268
if err != nil {
268269
fmt.Printf("error generating client certificate for %s%d-client%d : %s\n",

core/peer/peer.go

+21
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import (
4646

4747
var peerLogger = logging.MustGetLogger("peer")
4848

49+
var peerServer comm.GRPCServer
50+
4951
type chainSupport struct {
5052
configtxapi.Manager
5153
config.Application
@@ -404,3 +406,22 @@ func (c *channelPolicyManagerGetter) Manager(channelID string) (policies.Manager
404406
policyManager := GetPolicyManager(channelID)
405407
return policyManager, policyManager != nil
406408
}
409+
410+
// CreatePeerServer creates an instance of comm.GRPCServer
411+
// This server is used for peer communications
412+
func CreatePeerServer(listenAddress string,
413+
secureConfig comm.SecureServerConfig) (comm.GRPCServer, error) {
414+
415+
var err error
416+
peerServer, err = comm.NewGRPCServer(listenAddress, secureConfig)
417+
if err != nil {
418+
peerLogger.Errorf("Failed to create peer server (%s)", err)
419+
return nil, err
420+
}
421+
return peerServer, nil
422+
}
423+
424+
// GetPeerServer returns the peer server instance
425+
func GetPeerServer() comm.GRPCServer {
426+
return peerServer
427+
}

core/peer/pkg_test.go

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package peer_test
18+
19+
import (
20+
"crypto/tls"
21+
"crypto/x509"
22+
"errors"
23+
"fmt"
24+
"io/ioutil"
25+
"path/filepath"
26+
"testing"
27+
"time"
28+
29+
"google.golang.org/grpc/credentials"
30+
31+
"golang.org/x/net/context"
32+
"google.golang.org/grpc"
33+
34+
"github.com/hyperledger/fabric/core/comm"
35+
testpb "github.com/hyperledger/fabric/core/comm/testdata/grpc"
36+
"github.com/hyperledger/fabric/core/peer"
37+
"github.com/stretchr/testify/assert"
38+
)
39+
40+
// default timeout for grpc connections
41+
var timeout = time.Second * 1
42+
43+
// test server to be registered with the GRPCServer
44+
type testServiceServer struct{}
45+
46+
func (tss *testServiceServer) EmptyCall(context.Context, *testpb.Empty) (*testpb.Empty, error) {
47+
return new(testpb.Empty), nil
48+
}
49+
50+
// createCertPool creates an x509.CertPool from an array of PEM-encoded certificates
51+
func createCertPool(rootCAs [][]byte) (*x509.CertPool, error) {
52+
53+
certPool := x509.NewCertPool()
54+
for _, rootCA := range rootCAs {
55+
if !certPool.AppendCertsFromPEM(rootCA) {
56+
return nil, errors.New("Failed to load root certificates")
57+
}
58+
}
59+
return certPool, nil
60+
}
61+
62+
// helper function to invoke the EmptyCall againt the test service
63+
func invokeEmptyCall(address string, dialOptions []grpc.DialOption) (*testpb.Empty, error) {
64+
65+
//add DialOptions
66+
dialOptions = append(dialOptions, grpc.WithBlock())
67+
dialOptions = append(dialOptions, grpc.WithTimeout(timeout))
68+
//create GRPC client conn
69+
clientConn, err := grpc.Dial(address, dialOptions...)
70+
if err != nil {
71+
return nil, err
72+
}
73+
defer clientConn.Close()
74+
75+
//create GRPC client
76+
client := testpb.NewTestServiceClient(clientConn)
77+
78+
ctx := context.Background()
79+
ctx, cancel := context.WithTimeout(ctx, timeout)
80+
defer cancel()
81+
82+
//invoke service
83+
empty, err := client.EmptyCall(ctx, new(testpb.Empty))
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
return empty, nil
89+
}
90+
91+
func TestCreatePeerServer(t *testing.T) {
92+
93+
t.Parallel()
94+
95+
// load test certs from testdata
96+
org1CA, err := ioutil.ReadFile(filepath.Join("testdata", "Org1-cert.pem"))
97+
org1Server1Key, err := ioutil.ReadFile(filepath.Join("testdata", "Org1-server1-key.pem"))
98+
org1Server1Cert, err := ioutil.ReadFile(filepath.Join("testdata", "Org1-server1-cert.pem"))
99+
org2CA, err := ioutil.ReadFile(filepath.Join("testdata", "Org2-cert.pem"))
100+
org2Server1Key, err := ioutil.ReadFile(filepath.Join("testdata", "Org2-server1-key.pem"))
101+
org2Server1Cert, err := ioutil.ReadFile(filepath.Join("testdata", "Org2-server1-cert.pem"))
102+
103+
if err != nil {
104+
t.Fatalf("Failed to load test certificates: %v", err)
105+
}
106+
107+
org1CertPool, err := createCertPool([][]byte{org1CA})
108+
org2CertPool, err := createCertPool([][]byte{org2CA})
109+
110+
if err != nil {
111+
t.Fatalf("Failed to load root certificates into pool: %v", err)
112+
}
113+
114+
org1Creds := credentials.NewClientTLSFromCert(org1CertPool, "")
115+
org2Creds := credentials.NewClientTLSFromCert(org2CertPool, "")
116+
117+
// use server cert as client cert
118+
org2ClientCert, err := tls.X509KeyPair(org2Server1Cert, org2Server1Key)
119+
if err != nil {
120+
t.Fatalf("Failed to load client certificate: %v", err)
121+
}
122+
org1Org2Creds := credentials.NewTLS(&tls.Config{
123+
Certificates: []tls.Certificate{org2ClientCert},
124+
RootCAs: org1CertPool,
125+
})
126+
127+
// basic function tests
128+
var tests = []struct {
129+
name string
130+
listenAddress string
131+
secureConfig comm.SecureServerConfig
132+
expectError bool
133+
goodOptions []grpc.DialOption
134+
badOptions []grpc.DialOption
135+
}{
136+
{
137+
name: "NoTLS",
138+
listenAddress: fmt.Sprintf("localhost:%d", 4050),
139+
secureConfig: comm.SecureServerConfig{
140+
UseTLS: false,
141+
},
142+
expectError: false,
143+
goodOptions: []grpc.DialOption{grpc.WithInsecure()},
144+
badOptions: []grpc.DialOption{grpc.WithTransportCredentials(org1Creds)},
145+
},
146+
{
147+
name: "BadAddress",
148+
listenAddress: "badaddress",
149+
secureConfig: comm.SecureServerConfig{
150+
UseTLS: false,
151+
},
152+
expectError: true,
153+
},
154+
{
155+
name: "ServerTLSOrg1",
156+
listenAddress: fmt.Sprintf("localhost:%d", 4051),
157+
secureConfig: comm.SecureServerConfig{
158+
UseTLS: true,
159+
ServerCertificate: org1Server1Cert,
160+
ServerKey: org1Server1Key,
161+
ServerRootCAs: [][]byte{org1CA},
162+
},
163+
expectError: false,
164+
goodOptions: []grpc.DialOption{grpc.WithTransportCredentials(org1Creds)},
165+
badOptions: []grpc.DialOption{grpc.WithTransportCredentials(org2Creds)},
166+
},
167+
{
168+
name: "MutualTLSOrg1Org2",
169+
listenAddress: fmt.Sprintf("localhost:%d", 4052),
170+
secureConfig: comm.SecureServerConfig{
171+
UseTLS: true,
172+
ServerCertificate: org1Server1Cert,
173+
ServerKey: org1Server1Key,
174+
ServerRootCAs: [][]byte{org1CA},
175+
ClientRootCAs: [][]byte{org1CA, org2CA},
176+
RequireClientCert: true,
177+
},
178+
expectError: false,
179+
goodOptions: []grpc.DialOption{grpc.WithTransportCredentials(org1Org2Creds)},
180+
badOptions: []grpc.DialOption{grpc.WithTransportCredentials(org1Creds)},
181+
},
182+
}
183+
184+
for _, test := range tests {
185+
test := test
186+
t.Run(test.name, func(t *testing.T) {
187+
t.Parallel()
188+
t.Logf("Running test %s ...", test.name)
189+
190+
_, err := peer.CreatePeerServer(test.listenAddress, test.secureConfig)
191+
// check to see whether to not we expect an error
192+
// we don't check the exact error because the comm package covers these cases
193+
if test.expectError {
194+
assert.Error(t, err, "CreatePeerServer should have returned an error")
195+
} else {
196+
assert.NoError(t, err, "CreatePeerServer should not have returned an error")
197+
// get the server from peer
198+
peerServer := peer.GetPeerServer()
199+
assert.NotNil(t, peerServer, "GetPeerServer should not return a nil value")
200+
// register a GRPC test service
201+
testpb.RegisterTestServiceServer(peerServer.Server(), &testServiceServer{})
202+
go peerServer.Start()
203+
defer peerServer.Stop()
204+
205+
//invoke the EmptyCall service with good options
206+
_, err = invokeEmptyCall(test.listenAddress, test.goodOptions)
207+
assert.NoError(t, err, "Failed to invoke the EmptyCall service")
208+
//invoke the EmptyCall service with bad options
209+
_, err = invokeEmptyCall(test.listenAddress, test.badOptions)
210+
assert.Error(t, err, "Expected error using bad dial options")
211+
212+
}
213+
})
214+
}
215+
}

core/peer/testdata/Org1-cert.pem

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIB4jCCAYigAwIBAgIQGm/MiEzhl9NQB7VQsWTwpzAKBggqhkjOPQQDAjBYMQsw
3+
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
4+
YW5jaXNjbzENMAsGA1UEChMET3JnMTENMAsGA1UEAxMET3JnMTAeFw0xNzAzMDkx
5+
MjE4NDBaFw0yNzAzMDcxMjE4NDBaMFgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD
6+
YWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKEwRPcmcx
7+
MQ0wCwYDVQQDEwRPcmcxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEU70ukwCU
8+
MIU7v7GTm2iQDPansRjHctQXiz3wLwTjnkxmCnvWG6DzkkOUTFrGQgC/BuUXnT+e
9+
pVVYPHv3pyxXV6M0MDIwDgYDVR0PAQH/BAQDAgGmMA8GA1UdJQQIMAYGBFUdJQAw
10+
DwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiBAQXkEp2iDIrgjOg2U
11+
Uc/NMTxHOapzr4c7a2//HrUN/QIhAP4C4dOzqw2WZSL5yaKGsDwVYXTzIX8VEzgH
12+
S/iulKlP
13+
-----END CERTIFICATE-----

core/peer/testdata/Org1-key.pem

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIPV6aiHMGDfHF6Ub+iKVcnDwyacwtZp5SMUnnMPWsYJtoAoGCCqGSM49
3+
AwEHoUQDQgAEU70ukwCUMIU7v7GTm2iQDPansRjHctQXiz3wLwTjnkxmCnvWG6Dz
4+
kkOUTFrGQgC/BuUXnT+epVVYPHv3pyxXVw==
5+
-----END EC PRIVATE KEY-----
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIB/DCCAaGgAwIBAgIRANHBGVHQ24Z7DyTeCJy0hkAwCgYIKoZIzj0EAwIwWDEL
3+
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
4+
cmFuY2lzY28xDTALBgNVBAoTBE9yZzExDTALBgNVBAMTBE9yZzEwHhcNMTcwMzA5
5+
MTIxODQwWhcNMjcwMzA3MTIxODQwWjBlMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
6+
Q2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEVMBMGA1UEChMMT3Jn
7+
MS1zZXJ2ZXIxMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjO
8+
PQMBBwNCAAQK2y+RWueR/DA1azaTAOCWg2V5OQvaV/Z5w5eM0pnxFNigvL2M2587
9+
K9TyIko/q/FSugFcRlpwqluOfRNrS/pgoz8wPTAOBgNVHQ8BAf8EBAMCBaAwHQYD
10+
VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwCgYIKoZI
11+
zj0EAwIDSQAwRgIhAOCcZX387r7wcIhGjugCa30FLfNt+JuzVmI1u6mQlyAhAiEA
12+
hHaqckAlaGrf2RZ22JfuruIeBFspvynLo/R8wnWUgTU=
13+
-----END CERTIFICATE-----
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIFs7jdTFvAvefiEmo/l12AxECeajntSHWIEBWITL4TbloAoGCCqGSM49
3+
AwEHoUQDQgAECtsvkVrnkfwwNWs2kwDgloNleTkL2lf2ecOXjNKZ8RTYoLy9jNuf
4+
OyvU8iJKP6vxUroBXEZacKpbjn0Ta0v6YA==
5+
-----END EC PRIVATE KEY-----
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIB+zCCAaCgAwIBAgIQUXz+3XMkFuny6scdi93EOTAKBggqhkjOPQQDAjBYMQsw
3+
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
4+
YW5jaXNjbzENMAsGA1UEChMET3JnMTENMAsGA1UEAxMET3JnMTAeFw0xNzAzMDkx
5+
MjE4NDBaFw0yNzAzMDcxMjE4NDBaMGUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD
6+
YWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRUwEwYDVQQKEwxPcmcx
7+
LXNlcnZlcjIxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49
8+
AwEHA0IABOn36rJJ1NWZ6ghuzsx/KCtmY+yBHP6J/nDloqvUAGsPxtL/D0Wdn9c1
9+
pHeYBTkpqkpEuQiq2fxKCjH0rClh9YqjPzA9MA4GA1UdDwEB/wQEAwIFoDAdBgNV
10+
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAKBggqhkjO
11+
PQQDAgNJADBGAiEAvtuTYx/9wVuuhDWl0P0PMmgSvpcWV1jIj2LT7xFdq/cCIQCp
12+
s2LlnqyCJ1t6lBNpNbn/HYPYn46FQmvjhHGCzwW9kw==
13+
-----END CERTIFICATE-----
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIKWbEerXeYWnExsy0baJPL8RChzY/7JVz0QQbs0efZjnoAoGCCqGSM49
3+
AwEHoUQDQgAE6ffqsknU1ZnqCG7OzH8oK2Zj7IEc/on+cOWiq9QAaw/G0v8PRZ2f
4+
1zWkd5gFOSmqSkS5CKrZ/EoKMfSsKWH1ig==
5+
-----END EC PRIVATE KEY-----

core/peer/testdata/Org2-cert.pem

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIB4zCCAYigAwIBAgIQctpUUW4DlMMhPEDnOcZBsDAKBggqhkjOPQQDAjBYMQsw
3+
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
4+
YW5jaXNjbzENMAsGA1UEChMET3JnMjENMAsGA1UEAxMET3JnMjAeFw0xNzAzMDkx
5+
MjE4NDBaFw0yNzAzMDcxMjE4NDBaMFgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD
6+
YWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKEwRPcmcy
7+
MQ0wCwYDVQQDEwRPcmcyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4t3xokqU
8+
oq6M+cneFK5r/MLT/vAYFfu/67AGYWaFJKN7xPzlREO1VbGqz6AvNSBJsq1+k8Mq
9+
uw8YtJyQnfghD6M0MDIwDgYDVR0PAQH/BAQDAgGmMA8GA1UdJQQIMAYGBFUdJQAw
10+
DwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNJADBGAiEAoTYAwlEu2g/fvwmb
11+
v6wgVs6lAN0nDfttySDZqfJdOJ8CIQCcYOqoXVxPvHS5re4UhcBU+pu+7rRYuH6t
12+
37f6tMOgKQ==
13+
-----END CERTIFICATE-----

core/peer/testdata/Org2-key.pem

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIDNhajAyPe7+ofYFs/9ZYtOsHSoYC6FWtNiF3VaDWkILoAoGCCqGSM49
3+
AwEHoUQDQgAE4t3xokqUoq6M+cneFK5r/MLT/vAYFfu/67AGYWaFJKN7xPzlREO1
4+
VbGqz6AvNSBJsq1+k8Mquw8YtJyQnfghDw==
5+
-----END EC PRIVATE KEY-----
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIB+zCCAaCgAwIBAgIQUZ/QyyHUYl4zIuKxdxcHCTAKBggqhkjOPQQDAjBYMQsw
3+
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
4+
YW5jaXNjbzENMAsGA1UEChMET3JnMjENMAsGA1UEAxMET3JnMjAeFw0xNzAzMDkx
5+
MjE4NDBaFw0yNzAzMDcxMjE4NDBaMGUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD
6+
YWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRUwEwYDVQQKEwxPcmcy
7+
LXNlcnZlcjExEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49
8+
AwEHA0IABPGTbJUzh8uE81pbJfd3cO0MU94I87IPLQwe1weEC3aCcZ+awF4kIT5T
9+
Z/SmTiDGHf1BH3CONUaTGYXKtioL2mqjPzA9MA4GA1UdDwEB/wQEAwIFoDAdBgNV
10+
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAKBggqhkjO
11+
PQQDAgNJADBGAiEAjJ9iEz1dix1j+t+TMJtDLsLwFpnmcRUsrTlUfh1Fzg0CIQCx
12+
K5rXgKTR48yMQ1mTizTNljd3I+DsNGWPDrbKHgIg+g==
13+
-----END CERTIFICATE-----
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIJZHiy1JhS/mosbf1VGOuus63/XsG2rBug79RmUOlcU5oAoGCCqGSM49
3+
AwEHoUQDQgAE8ZNslTOHy4TzWlsl93dw7QxT3gjzsg8tDB7XB4QLdoJxn5rAXiQh
4+
PlNn9KZOIMYd/UEfcI41RpMZhcq2Kgvaag==
5+
-----END EC PRIVATE KEY-----
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIB+zCCAaGgAwIBAgIRALSYDDlVt7w7Fw7cdP8F9LMwCgYIKoZIzj0EAwIwWDEL
3+
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
4+
cmFuY2lzY28xDTALBgNVBAoTBE9yZzIxDTALBgNVBAMTBE9yZzIwHhcNMTcwMzA5
5+
MTIxODQwWhcNMjcwMzA3MTIxODQwWjBlMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
6+
Q2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEVMBMGA1UEChMMT3Jn
7+
Mi1zZXJ2ZXIyMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjO
8+
PQMBBwNCAAQttx8Y8K31yCxoHX+iQLF7fu0ZU2EHtkAaD9T69emDiWLA5qCpksjr
9+
0IwoLvJymwa2OR+2rrMzqI65+CvZNT4koz8wPTAOBgNVHQ8BAf8EBAMCBaAwHQYD
10+
VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwCgYIKoZI
11+
zj0EAwIDSAAwRQIgeciUm1lmT+nOawKmgEBeiP53VczMtT7S5MHZOCBgroUCIQCN
12+
8RSB44VgUwjfZfdW9Kr5xB5R6ufzAkGC6xlPbqiYPQ==
13+
-----END CERTIFICATE-----
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIBn9Ftx1gfZXp8bCP2yOHv2y7fX1vlIluXavEl4RQqYIoAoGCCqGSM49
3+
AwEHoUQDQgAELbcfGPCt9cgsaB1/okCxe37tGVNhB7ZAGg/U+vXpg4liwOagqZLI
4+
69CMKC7ycpsGtjkftq6zM6iOufgr2TU+JA==
5+
-----END EC PRIVATE KEY-----

0 commit comments

Comments
 (0)