Skip to content

Support partial certificate chain authentication #538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/session_client_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,9 @@ nc_client_tls_session_new(int sock, const char *host, int timeout, struct nc_cli
}

/* set client's verify mode flags */
nc_client_tls_set_verify_wrap(tls_cfg);
if (nc_client_tls_set_verify_wrap(tls_cfg)) {
goto fail;
}

/* init TLS context and store data which may be needed later in it */
if (nc_tls_init_ctx_wrap(cli_cert, cli_pkey, cert_store, crl_store, tls_ctx)) {
Expand All @@ -304,7 +306,7 @@ nc_client_tls_session_new(int sock, const char *host, int timeout, struct nc_cli
cli_cert = cli_pkey = cert_store = crl_store = NULL;

/* setup config from ctx */
if (nc_tls_setup_config_from_ctx_wrap(tls_ctx, NC_CLIENT, tls_cfg)) {
if (nc_tls_setup_config_from_ctx_wrap(tls_ctx, tls_cfg)) {
goto fail;
}

Expand Down
36 changes: 20 additions & 16 deletions src/session_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,29 @@ nc_tls_session_destroy_wrap(void *tls_session)
}

void *
nc_tls_config_new_wrap(int UNUSED(side))
nc_tls_config_new_wrap(int side)
{
int r;
mbedtls_ssl_config *tls_cfg;

tls_cfg = malloc(sizeof *tls_cfg);
NC_CHECK_ERRMEM_RET(!tls_cfg, NULL);

mbedtls_ssl_config_init(tls_cfg);

/* set default config data */
if (side == NC_SERVER) {
r = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
} else {
r = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
}
if (r) {
nc_mbedtls_strerr(NULL, r, "Setting default TLS config failed");
mbedtls_ssl_config_free(tls_cfg);
free(tls_cfg);
return NULL;
}

return tls_cfg;
}

Expand Down Expand Up @@ -659,10 +674,11 @@ nc_server_tls_set_verify_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *cb_da
mbedtls_ssl_conf_verify(tls_cfg, nc_server_tls_verify_cb, cb_data);
}

void
int
nc_client_tls_set_verify_wrap(void *tls_cfg)
{
mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED);
return 0;
}

char *
Expand Down Expand Up @@ -1143,27 +1159,15 @@ nc_tls_init_ctx_wrap(void *cert, void *pkey, void *cert_store, void *crl_store,
}

int
nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg)
nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, void *tls_cfg)
{
int rc;

/* set default config data */
if (side == NC_SERVER) {
rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
} else {
rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
}
if (rc) {
nc_mbedtls_strerr(NULL, rc, "Setting default TLS config failed");
return 1;
}

/* set config's rng */
mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg);
/* set config's cert and key */
mbedtls_ssl_conf_own_cert(tls_cfg, tls_ctx->cert, tls_ctx->pkey);
/* set config's CA and CRL cert store */
mbedtls_ssl_conf_ca_chain(tls_cfg, tls_ctx->cert_store, tls_ctx->crl_store);

return 0;
}

Expand Down
40 changes: 33 additions & 7 deletions src/session_openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,9 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
/* copy the client cert */
data->session->opts.server.client_cert = X509_dup(cert);
NC_CHECK_ERRMEM_RET(!data->session->opts.server.client_cert, 0);

/* verification was successful, override the in-built verification result */
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
}
return 1;
} else {
Expand All @@ -404,10 +407,38 @@ nc_server_tls_set_verify_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *cb_da
SSL_CTX_set_verify(tls_cfg, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_server_tls_verify_cb);
}

void
int
nc_client_tls_set_verify_wrap(void *tls_cfg)
{
int ret = 0;
X509_VERIFY_PARAM *vpm = NULL;

/* set the verify flag */
SSL_CTX_set_verify(tls_cfg, SSL_VERIFY_PEER, NULL);

vpm = X509_VERIFY_PARAM_new();
NC_CHECK_ERRMEM_RET(!vpm, 1);

/* set the partial chain flag to allow verification of a certificate chain
* to succeed even if the chain is not complete.
* See https://github.com/openssl/openssl/issues/7871
* This is not set for the server, because all the CA certs in the chain
* may be needed for CTN, so such partial chain cases are handled manually. */
if (!X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_PARTIAL_CHAIN)) {
ERR(NULL, "Setting X509_V_FLAG_PARTIAL_CHAIN flag failed (%s).", ERR_reason_error_string(ERR_get_error()));
ret = 1;
goto cleanup;
}

if (!SSL_CTX_set1_param(tls_cfg, vpm)) {
ERR(NULL, "Failed to set verify param (%s).", ERR_reason_error_string(ERR_get_error()));
ret = 1;
goto cleanup;
}

cleanup:
X509_VERIFY_PARAM_free(vpm);
return ret;
}

char *
Expand Down Expand Up @@ -774,7 +805,7 @@ nc_tls_move_crls_to_store(const X509_STORE *src, X509_STORE *dst)
}

int
nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg)
nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, void *tls_cfg)
{
if (SSL_CTX_use_certificate(tls_cfg, tls_ctx->cert) != 1) {
ERR(NULL, "Setting up TLS certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
Expand All @@ -786,11 +817,6 @@ nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tl
return 1;
}

/* disable server-side automatic chain building */
if (side == NC_SERVER) {
SSL_CTX_set_mode(tls_cfg, SSL_MODE_NO_AUTO_CHAIN);
}

if (tls_ctx->crl_store) {
/* move CRLs from crl_store to cert_store, because SSL_CTX can only have one store */
if (nc_tls_move_crls_to_store(tls_ctx->crl_store, tls_ctx->cert_store)) {
Expand Down
2 changes: 1 addition & 1 deletion src/session_server_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt
srv_cert = srv_pkey = cert_store = crl_store = NULL;

/* setup config from ctx */
if (nc_tls_setup_config_from_ctx_wrap(&session->ti.tls.ctx, NC_SERVER, tls_cfg)) {
if (nc_tls_setup_config_from_ctx_wrap(&session->ti.tls.ctx, tls_cfg)) {
goto fail;
}
session->ti.tls.config = tls_cfg;
Expand Down
6 changes: 3 additions & 3 deletions src/session_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,9 @@ void nc_server_tls_set_verify_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *
* @brief Set TLS client's verify flags.
*
* @param[in] tls_cfg TLS configuration.
* @return 0 on success, 1 on error.
*/
void nc_client_tls_set_verify_wrap(void *tls_cfg);
int nc_client_tls_set_verify_wrap(void *tls_cfg);

/**
* @brief Verify the certificate.
Expand Down Expand Up @@ -451,11 +452,10 @@ int nc_tls_init_ctx_wrap(void *cert, void *pkey, void *cert_store, void *crl_sto
* @brief Setup a TLS configuration from a TLS context.
*
* @param[in] tls_ctx TLS context.
* @param[in] side Side of the TLS connection.
* @param[in,out] tls_cfg TLS configuration.
* @return 0 on success, non-zero on fail.
*/
int nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg);
int nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, void *tls_cfg);

/**
* @brief Get the error code from a TLS session's verification.
Expand Down
1 change: 1 addition & 0 deletions tests/data/certs/0b527f1f.0
1 change: 1 addition & 0 deletions tests/data/certs/a96df0b1.0
1 change: 1 addition & 0 deletions tests/data/certs/acf0d71a.0
28 changes: 28 additions & 0 deletions tests/data/certs/client.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDSs0VI4Hb5bjVU
auqaT92xKRn1efE7xeA5qfWv6uYMA8APj9aPC4zLUoEixeoJjJfnwpz9Kslt5lOm
ul+30OSGQkkIvjQ3xZJlNJPlDqp/ro2M8Mv5zVleVsGX6eEvVOtgjzbfBaXnscxa
uxIDHwvsBf39W6op742Mjwmp1c5bhMAywQQgNXv9+rvmOiAmXfazVzZ01L1XLQmt
tRPLhUcFkEWrF4nvuXzd9M5d6wZ/PUJM49OE8gp/OvbVTKSwCGxiPYcyNFr1+7kD
Ni83a7pTkOGj9eWc6Pz+kqU/bdrpKp++Ju75iVKO8tbOoJleomY9XEmUc8FKp0IZ
Kw2iUU4xAgMBAAECggEABBNnMvRyoLsWyYIC9Ua3u2JX5/V6quUWRA9LWyKQXimh
aLun0x8OU22OZXGOKC6hzqAnYMdX26ohsp821Eu0a/tyxbpGMND/1rjYZo0s06j5
AIG09GlaQfasKofh7QQ3xGeo0FIf5jY8hYHKZKiQVrm78s934HVLl+05auFhq0gC
Yn3kN754q7pM8gRA+OJ1rl7PXtSYUCERG/ShKlo6YZH155VRKf9s2L/HaBMSgZ5K
XlJjw7FhbxgqVLX0NhlawfpR8g0nP21vUXq3y/kUCm2z16j7Kn0765YOsa/MjlSB
fksQTJqW36WYMo0GHzs2LDU/+kaoeOQmXtvz6QXbMQKBgQDpkngR/73Uw5aS88wr
nzw1KBMLzmugA8xougsTjZCONVliXcUgC/GSOc7nxVHEcdf8HMHPhpuslFwKa0vX
19fv+UV/irHE6xgXYv+TzGWSzHH3UBHhBJwcp9BmpvrjN1Z0znA8DvV/WcXLiTsk
WRM6wmjcrXv4NySMGz4Btm1K6QKBgQDm7pQ5bqCpqVvlb/3Irjg1L0mrztrGC6pJ
NSSZnQmGODDvnG9KYA1prAxtAwV6QIaLlGQHtmuLTo4QiNdehzN+T9ADIwW7O3ez
AmVMeW/dA2lfraKGpT6BxRGl/HUjynO654YdjakVPhFCXoruYdNXarYAGxbvFH7f
Ly7FjYjMCQKBgCRpwjBEJ2sJButn+JeAXpmG4tk/WAX9MZvODTYCJtwUsxejuLyT
QBjkzD3TxDiU6vAR56Ebwh84jdTN/2fp7W8q9Eu66pXhdMMImwGGpvsoxkehAviK
iG/rNZEbTRepI+BX4qVqS5mL/EsL3N2AyX+qmUB2B1Kd/iOdh6AquYUpAoGBANI+
j3AiSd9tMx8V8MWN59pz7OygbQk1gZkY6vN3xz4NVnAvyhq6jDuSydVNlOUSbewR
82gaKpsvr6NaEkobaUQsQcM5VXR61aWCHtubC7755iuXl4piUyAuXLzztWoaXXbi
ZnXtjOysD2aPUIlrQtApdP1vrFeKH1/uQvTUwKCRAoGAPHffvNXu6AlpN103jj+k
UUuq3Dg7Kg2vwWd+EIICLDbJ9wvQxamzIfHlklvjU3fLorkxMiTKSKT0YayztqUb
SwT4ClaSe7Mi39IOnoo0uZ6VQEKLdM62WWHGt+odyBSy5wf28iw9Bgb2v2pswsxW
j9CGEUZ5K2VCxU7kSm9Xtss=
-----END PRIVATE KEY-----
76 changes: 76 additions & 0 deletions tests/data/certs/client.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=CZ, ST=Some-State, O=CESNET, OU=TMC, CN=Intermediate Client CA
Validity
Not Before: Jun 3 11:57:09 2025 GMT
Not After : Jun 1 11:57:09 2035 GMT
Subject: C=CZ, ST=Some-State, O=CESNET, OU=TMC, CN=client
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:d2:b3:45:48:e0:76:f9:6e:35:54:6a:ea:9a:4f:
dd:b1:29:19:f5:79:f1:3b:c5:e0:39:a9:f5:af:ea:
e6:0c:03:c0:0f:8f:d6:8f:0b:8c:cb:52:81:22:c5:
ea:09:8c:97:e7:c2:9c:fd:2a:c9:6d:e6:53:a6:ba:
5f:b7:d0:e4:86:42:49:08:be:34:37:c5:92:65:34:
93:e5:0e:aa:7f:ae:8d:8c:f0:cb:f9:cd:59:5e:56:
c1:97:e9:e1:2f:54:eb:60:8f:36:df:05:a5:e7:b1:
cc:5a:bb:12:03:1f:0b:ec:05:fd:fd:5b:aa:29:ef:
8d:8c:8f:09:a9:d5:ce:5b:84:c0:32:c1:04:20:35:
7b:fd:fa:bb:e6:3a:20:26:5d:f6:b3:57:36:74:d4:
bd:57:2d:09:ad:b5:13:cb:85:47:05:90:45:ab:17:
89:ef:b9:7c:dd:f4:ce:5d:eb:06:7f:3d:42:4c:e3:
d3:84:f2:0a:7f:3a:f6:d5:4c:a4:b0:08:6c:62:3d:
87:32:34:5a:f5:fb:b9:03:36:2f:37:6b:ba:53:90:
e1:a3:f5:e5:9c:e8:fc:fe:92:a5:3f:6d:da:e9:2a:
9f:be:26:ee:f9:89:52:8e:f2:d6:ce:a0:99:5e:a2:
66:3d:5c:49:94:73:c1:4a:a7:42:19:2b:0d:a2:51:
4e:31
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
C4:30:21:DE:F9:75:98:59:AE:63:B7:5B:70:0F:E1:C0:72:B1:EF:7F
X509v3 Authority Key Identifier:
F2:CF:C5:9A:C6:63:7A:AB:A0:59:60:41:FE:7D:3F:5D:BE:9A:D5:3F
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
78:e4:53:ad:ce:4c:38:ed:3f:74:89:78:66:79:1a:45:bb:98:
03:95:27:9b:c9:72:77:10:2a:ac:df:99:7d:b0:35:91:52:9e:
45:ff:f7:ff:40:30:8b:84:8b:78:5f:b0:2a:8e:f6:7c:af:4d:
6c:46:de:2b:c1:35:db:5e:1f:5e:b9:df:d1:65:34:03:99:fd:
89:3b:b9:e1:52:1d:ac:03:ce:3e:7b:78:43:6b:c7:4d:65:f2:
13:da:d6:88:4b:a4:f9:00:c0:29:c7:70:e7:37:a6:58:bd:41:
b2:c6:22:00:93:23:2d:78:df:88:15:00:f3:38:0d:dc:6d:67:
5a:5c:56:2d:9f:04:74:25:29:9a:fc:25:56:a8:3d:43:4d:53:
6f:04:3c:34:25:e1:d5:16:4c:a4:39:39:82:3e:df:c9:98:ef:
67:55:c4:8b:99:4b:5c:9f:93:b0:51:17:9c:0b:bc:48:16:64:
79:0c:47:75:69:61:02:0d:34:dd:d8:c4:74:fc:2b:71:3b:3e:
f2:e8:9b:2f:eb:c4:55:22:d7:4e:fd:ff:29:92:f1:05:bc:77:
57:96:6f:80:73:67:84:82:dc:4a:8d:8f:91:97:85:66:a0:dc:
2e:12:53:dc:e0:b2:5d:e0:a3:31:9a:97:4e:c6:b6:5d:48:30:
9f:81:9e:14
-----BEGIN CERTIFICATE-----
MIIDcTCCAlmgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJDWjET
MBEGA1UECAwKU29tZS1TdGF0ZTEPMA0GA1UECgwGQ0VTTkVUMQwwCgYDVQQLDANU
TUMxHzAdBgNVBAMMFkludGVybWVkaWF0ZSBDbGllbnQgQ0EwHhcNMjUwNjAzMTE1
NzA5WhcNMzUwNjAxMTE1NzA5WjBSMQswCQYDVQQGEwJDWjETMBEGA1UECAwKU29t
ZS1TdGF0ZTEPMA0GA1UECgwGQ0VTTkVUMQwwCgYDVQQLDANUTUMxDzANBgNVBAMM
BmNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANKzRUjgdvlu
NVRq6ppP3bEpGfV58TvF4Dmp9a/q5gwDwA+P1o8LjMtSgSLF6gmMl+fCnP0qyW3m
U6a6X7fQ5IZCSQi+NDfFkmU0k+UOqn+ujYzwy/nNWV5WwZfp4S9U62CPNt8Fpeex
zFq7EgMfC+wF/f1bqinvjYyPCanVzluEwDLBBCA1e/36u+Y6ICZd9rNXNnTUvVct
Ca21E8uFRwWQRasXie+5fN30zl3rBn89Qkzj04TyCn869tVMpLAIbGI9hzI0WvX7
uQM2LzdrulOQ4aP15Zzo/P6SpT9t2ukqn74m7vmJUo7y1s6gmV6iZj1cSZRzwUqn
QhkrDaJRTjECAwEAAaNCMEAwHQYDVR0OBBYEFMQwId75dZhZrmO3W3AP4cByse9/
MB8GA1UdIwQYMBaAFPLPxZrGY3qroFlgQf59P12+mtU/MA0GCSqGSIb3DQEBCwUA
A4IBAQB45FOtzkw47T90iXhmeRpFu5gDlSebyXJ3ECqs35l9sDWRUp5F//f/QDCL
hIt4X7AqjvZ8r01sRt4rwTXbXh9eud/RZTQDmf2JO7nhUh2sA84+e3hDa8dNZfIT
2taIS6T5AMApx3DnN6ZYvUGyxiIAkyMteN+IFQDzOA3cbWdaXFYtnwR0JSma/CVW
qD1DTVNvBDw0JeHVFkykOTmCPt/JmO9nVcSLmUtcn5OwURecC7xIFmR5DEd1aWEC
DTTd2MR0/CtxOz7y6Jsv68RVItdO/f8pkvEFvHdXlm+Ac2eEgtxKjY+Rl4VmoNwu
ElPc4LJd4KMxmpdOxrZdSDCfgZ4U
-----END CERTIFICATE-----
1 change: 1 addition & 0 deletions tests/data/certs/eab22c76.0
28 changes: 28 additions & 0 deletions tests/data/certs/intermediate_ca.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDBfCEtO655d2Ey
bmcaoZ6dKfAYPG//80d9kLxgpu5z4rrydnsUbTqymuCBk6J7UNAGQqsIYMz/TOiq
emDBxYhsQS484TY2KSCdKsECR89V6zU4zhuer8FOtrJTOi2I3eAO3kiT6fpSmVby
+M5io1GpZRIOoG8sv5heOx8Ngb5WNweTuSeruVbiAQF5ze6w6RytCZq4m/6DmyTn
xowJeAJzCpN804JIZead7ili23ZGDqX/hL0WR0ZZ3bxfbw0lWT87okAMkyjJ3ZWi
mDf44+cnOEex5jelh6rdMglkKYEaBnLrP+MTIh0jtNCBN8vkOszbbRlZPZKszf5H
mj/iuKg9AgMBAAECggEAEf2moMFFEsKmH92Cf8hEao0M6AFX/Al/0xijXiyszSC+
zQR1owDEbaQXY7pkaClWVnwE0ZVyqlCD7TTON2ZUg5W3T2402hS5QB6gGMQFmZHH
0/Lc6u9LvwkfjBFDok+DNyQLGpnYhAKsbOZJW9mfd8FP9wU1oTMQNL7e6c9vxoNT
f/i0OIS2BACIWSirYMu4NMNsCLO3eu33N93efS79MSd0wZHwxRQRSjg8ZvlI2O5o
2we4/ROPUZfTr/J1MXLsACI5Pggu2HZelFfw8p/yheNBUloz0aY4JrbniOVzsPtJ
mF/5YZhUC6I+x0VBwPFyWkiLjBjaUshirb0AnO4+3wKBgQD1BD4MqjNlLNIkrfJO
yb1laZC17bP7InwCyLrjpV+nYeHtHRxXpQqGeDkV5kdO6c9p5rNRru+T3RumeWEb
mZNXHmmnOQIwMVADlWAook1cC9vNlrdPKuf/SbrugTYXhWLmMHN4BJDCRvpRu9Gm
xdpYmiOg47e+Z0nnMuW7pKk5vwKBgQDKKIVFUHntj5NK/JahERXXvqkGOiMtGlSg
XEOUJ3SAj8ij+m3L3tesexdGj8TxBpeskP9ZbpZS9TjHdH8HGAgUwQynLtl2xBDl
5N4pNNg9hZzwZNighcvzJjlG/bYom8KpYzD/5rvbF2PKeMzEQcaIP7ynedcHveEC
nCaX1fPFAwKBgQDk5zi9BkmLzUrqP6QvnWQXGX+7p75M8hOfWj7XrMlnjBfROTAg
Za0moazbW87k16USEBkZR/DFWH2bkqx5Z4nyixQzyRvVQEy7fFNgwvi49M8+C/9n
s/MYWp6MciTB5rjVyxAlYUaIal0z/N9KCnAJ9L8ycWxZMxAlMjTUkGoU6wKBgQC3
uKulmsW0iE8sTvH13zkioTurVRqy2sMeGbuw4UqXrBSEMgumxBbtwdAdaHtrMLqX
Mg9ej4o+Sm4Xll/7anOtqOR+66zmqrlQtPbSLBx5jPxb3Mqi1/XKPGrBgcISlbwL
hQH7Dw6axl8Cm1Y85iovmP0ylyA9AOuFG7RXOTIZUQKBgQC+NhR6B7eIy4RxwgWu
QvPeqmpREtBZr/SIt38Q43Y001YjJT1UHxyCgkKEU7h2RHa+bzgIXD3LN4FNORYA
O1e3JaZ3uESGAWcjHtWuF5mbQH77T6sXqwKC3lL0/VcdT+JGFyYOApOWyI92U2FL
Sq1PGhXgcEt6b/BJtBUgo3gdEQ==
-----END PRIVATE KEY-----
Loading