tls: fix handling of x509 subject and issuer

When subject and verifier are represented as strings, escape special
characters (such as '+') to guarantee unambiguity. Previously, different
distinguished names could result in the same string when encoded. In
particular, inserting a '+' in a single-value Relative Distinguished
Name (e.g., L or OU) would produce a string that is indistinguishable
from a multi-value Relative Distinguished Name. Third-party code that
correctly interprets the generated string representation as a
multi-value Relative Distinguished Name could then be vulnerable to an
injection attack, e.g., when an attacker includes a single-value RDN
with type OU and value 'HR + CN=example.com', the string representation
produced by unpatched versions of Node.js would be
'OU=HR + CN=example.com', which represents a multi-value RDN.

Node.js itself is not vulnerable to this attack because the current
implementation that parses such strings into objects does not handle '+'
at all. This oversight leads to incorrect results, but at the same time
appears to prevent injection attacks (as described above).

With this change, the JavaScript objects representing the subject and
issuer Relative Distinguished Names are constructed in C++ directly,
instead of (incorrectly) encoding them as strings and then (incorrectly)
decoding the strings in JavaScript.

This addresses CVE-2021-44533.

CVE-ID: CVE-2021-44533
PR-URL: https://github.com/nodejs-private/node-private/pull/300
Reviewed-By: Michael Dawson <midawson@redhat.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
Tobias Nießen 2021-12-19 18:23:01 +00:00 committed by Richard Lau
parent 50439b446f
commit a336444c7f
No known key found for this signature in database
GPG Key ID: C43CEC45C17AB93C
16 changed files with 656 additions and 13 deletions

View File

@ -126,11 +126,13 @@ function translatePeerCertificate(c) {
if (!c)
return null;
if (c.issuer != null) c.issuer = parseCertString(c.issuer);
// TODO(tniessen): can we remove parseCertString without breaking anything?
if (typeof c.issuer === 'string') c.issuer = parseCertString(c.issuer);
if (c.issuerCertificate != null && c.issuerCertificate !== c) {
c.issuerCertificate = translatePeerCertificate(c.issuerCertificate);
}
if (c.subject != null) c.subject = parseCertString(c.subject);
// TODO(tniessen): can we remove parseCertString without breaking anything?
if (typeof c.subject === 'string') c.subject = parseCertString(c.subject);
if (c.infoAccess != null) {
const info = c.infoAccess;
c.infoAccess = ObjectCreate(null);

View File

@ -42,6 +42,7 @@ using v8::Value;
namespace crypto {
static constexpr int X509_NAME_FLAGS =
ASN1_STRFLGS_ESC_2253 |
ASN1_STRFLGS_ESC_CTRL |
ASN1_STRFLGS_UTF8_CONVERT |
XN_FLAG_SEP_MULTILINE |
@ -964,6 +965,93 @@ MaybeLocal<Value> GetSubject(
return ToV8Value(env, bio);
}
template <X509_NAME* get_name(const X509*)>
static MaybeLocal<Value> GetX509NameObject(Environment* env, X509* cert) {
X509_NAME* name = get_name(cert);
CHECK_NOT_NULL(name);
int cnt = X509_NAME_entry_count(name);
CHECK_GE(cnt, 0);
Local<Object> result =
Object::New(env->isolate(), Null(env->isolate()), nullptr, nullptr, 0);
if (result.IsEmpty()) {
return MaybeLocal<Value>();
}
for (int i = 0; i < cnt; i++) {
X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, i);
CHECK_NOT_NULL(entry);
// We intentionally ignore the value of X509_NAME_ENTRY_set because the
// representation as an object does not allow grouping entries into sets
// anyway, and multi-value RDNs are rare, i.e., the vast majority of
// Relative Distinguished Names contains a single type-value pair only.
const ASN1_OBJECT* type = X509_NAME_ENTRY_get_object(entry);
const ASN1_STRING* value = X509_NAME_ENTRY_get_data(entry);
// If OpenSSL knows the type, use the short name of the type as the key, and
// the numeric representation of the type's OID otherwise.
int type_nid = OBJ_obj2nid(type);
char type_buf[80];
const char* type_str;
if (type_nid != NID_undef) {
type_str = OBJ_nid2sn(type_nid);
CHECK_NOT_NULL(type_str);
} else {
OBJ_obj2txt(type_buf, sizeof(type_buf), type, true);
type_str = type_buf;
}
Local<String> v8_name;
if (!String::NewFromUtf8(env->isolate(), type_str).ToLocal(&v8_name)) {
return MaybeLocal<Value>();
}
// The previous implementation used X509_NAME_print_ex, which escapes some
// characters in the value. The old implementation did not decode/unescape
// values correctly though, leading to ambiguous and incorrect
// representations. The new implementation only converts to Unicode and does
// not escape anything.
unsigned char* value_str;
int value_str_size = ASN1_STRING_to_UTF8(&value_str, value);
if (value_str_size < 0) {
return Undefined(env->isolate());
}
Local<String> v8_value;
if (!String::NewFromUtf8(env->isolate(),
reinterpret_cast<const char*>(value_str),
NewStringType::kNormal,
value_str_size).ToLocal(&v8_value)) {
OPENSSL_free(value_str);
return MaybeLocal<Value>();
}
OPENSSL_free(value_str);
// For backward compatibility, we only create arrays if multiple values
// exist for the same key. That is not great but there is not much we can
// change here without breaking things. Note that this creates nested data
// structures, yet still does not allow representing Distinguished Names
// accurately.
if (result->HasOwnProperty(env->context(), v8_name).ToChecked()) {
Local<Value> accum =
result->Get(env->context(), v8_name).ToLocalChecked();
if (!accum->IsArray()) {
accum = Array::New(env->isolate(), &accum, 1);
result->Set(env->context(), v8_name, accum).Check();
}
Local<Array> array = accum.As<Array>();
array->Set(env->context(), array->Length(), v8_value).Check();
} else {
result->Set(env->context(), v8_name, v8_value).Check();
}
}
return result;
}
MaybeLocal<Value> GetCipherName(Environment* env, const SSLPointer& ssl) {
return GetCipherName(env, SSL_get_current_cipher(ssl.get()));
}
@ -1194,22 +1282,44 @@ MaybeLocal<Value> GetPeerCert(
return result;
}
MaybeLocal<Object> X509ToObject(Environment* env, X509* cert) {
MaybeLocal<Object> X509ToObject(
Environment* env,
X509* cert,
bool names_as_string) {
EscapableHandleScope scope(env->isolate());
Local<Context> context = env->context();
Local<Object> info = Object::New(env->isolate());
BIOPointer bio(BIO_new(BIO_s_mem()));
if (names_as_string) {
// TODO(tniessen): this branch should not have to exist. It is only here
// because toLegacyObject() does not actually return a legacy object, and
// instead represents subject and issuer as strings.
if (!Set<Value>(context,
info,
env->subject_string(),
GetSubject(env, bio, cert)) ||
!Set<Value>(context,
info,
env->issuer_string(),
GetIssuerString(env, bio, cert))) {
return MaybeLocal<Object>();
}
} else {
if (!Set<Value>(context,
info,
env->subject_string(),
GetX509NameObject<X509_get_subject_name>(env, cert)) ||
!Set<Value>(context,
info,
env->issuer_string(),
GetX509NameObject<X509_get_issuer_name>(env, cert))) {
return MaybeLocal<Object>();
}
}
if (!Set<Value>(context,
info,
env->subject_string(),
GetSubject(env, bio, cert)) ||
!Set<Value>(context,
info,
env->issuer_string(),
GetIssuerString(env, bio, cert)) ||
!Set<Value>(context,
info,
env->subjectaltname_string(),
GetSubjectAltNameString(env, bio, cert)) ||

View File

@ -116,7 +116,8 @@ v8::MaybeLocal<v8::Object> ECPointToBuffer(
v8::MaybeLocal<v8::Object> X509ToObject(
Environment* env,
X509* cert);
X509* cert,
bool names_as_string = false);
v8::MaybeLocal<v8::Value> GetValidTo(
Environment* env,

View File

@ -470,7 +470,7 @@ void X509Certificate::ToLegacy(const FunctionCallbackInfo<Value>& args) {
X509Certificate* cert;
ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder());
Local<Value> ret;
if (X509ToObject(env, cert->get()).ToLocal(&ret))
if (X509ToObject(env, cert->get(), true).ToLocal(&ret))
args.GetReturnValue().Set(ret);
}

View File

@ -500,3 +500,144 @@ for (let i = 0; i < infoAccessExtensions.length; i++) {
});
writeFileSync(`./info-${i}-cert.pem`, `${pem}\n`);
}
const subjects = [
[
[
{ type: oid.localityName, value: UTF8String.encode('Somewhere') }
],
[
{ type: oid.commonName, value: UTF8String.encode('evil.example.com') }
]
],
[
[
{
type: oid.localityName,
value: UTF8String.encode('Somewhere\0evil.example.com'),
}
]
],
[
[
{
type: oid.localityName,
value: UTF8String.encode('Somewhere\nCN=evil.example.com')
}
]
],
[
[
{
type: oid.localityName,
value: UTF8String.encode('Somewhere, CN = evil.example.com')
}
]
],
[
[
{
type: oid.localityName,
value: UTF8String.encode('Somewhere/CN=evil.example.com')
}
]
],
[
[
{
type: oid.localityName,
value: UTF8String.encode('M\u00fcnchen\\\nCN=evil.example.com')
}
]
],
[
[
{ type: oid.localityName, value: UTF8String.encode('Somewhere') },
{ type: oid.commonName, value: UTF8String.encode('evil.example.com') },
]
],
[
[
{
type: oid.localityName,
value: UTF8String.encode('Somewhere + CN=evil.example.com'),
}
]
],
[
[
{ type: oid.localityName, value: UTF8String.encode('L1') },
{ type: oid.localityName, value: UTF8String.encode('L2') },
],
[
{ type: oid.localityName, value: UTF8String.encode('L3') },
]
],
[
[
{ type: oid.localityName, value: UTF8String.encode('L1') },
],
[
{ type: oid.localityName, value: UTF8String.encode('L2') },
],
[
{ type: oid.localityName, value: UTF8String.encode('L3') },
],
],
];
for (let i = 0; i < subjects.length; i++) {
const tbs = {
version: 'v3',
serialNumber: new BN('01', 16),
signature: {
algorithm: oid.sha256WithRSAEncryption,
parameters: null_
},
issuer: {
type: 'rdnSequence',
value: subjects[i]
},
validity: {
notBefore: { type: 'utcTime', value: now },
notAfter: { type: 'utcTime', value: now + days * 86400000 }
},
subject: {
type: 'rdnSequence',
value: subjects[i]
},
subjectPublicKeyInfo: {
algorithm: {
algorithm: oid.rsaEncryption,
parameters: null_
},
subjectPublicKey: {
unused: 0,
data: publicKey
}
}
};
// Self-sign the certificate.
const tbsDer = rfc5280.TBSCertificate.encode(tbs, 'der');
const signature = crypto.createSign(digest).update(tbsDer).sign(privateKey);
// Construct the signed certificate.
const cert = {
tbsCertificate: tbs,
signatureAlgorithm: {
algorithm: oid.sha256WithRSAEncryption,
parameters: null_
},
signature: {
unused: 0,
data: signature
}
};
// Store the signed certificate.
const pem = rfc5280.Certificate.encode(cert, 'pem', {
label: 'CERTIFICATE'
});
writeFileSync(`./subj-${i}-cert.pem`, `${pem}\n`);
}

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIE1zCCAr+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAvMRIwEAYDVQQHDAlTb21l
d2hlcmUxGTAXBgNVBAMMEGV2aWwuZXhhbXBsZS5jb20wHhcNMjExMjIwMTQ1NzM1
WhcNMzExMjE4MTQ1NzM1WjAvMRIwEAYDVQQHDAlTb21ld2hlcmUxGTAXBgNVBAMM
EGV2aWwuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQCxEWd00u9E9T/ko6WcCKjhZ7tjnfVylnA7M0EHOwvdivgD46eAb1omsonLagiV
rZrG7EpYuMhtz+g3Yv1d0nvFvv8ge9UIdnN8EDTDzLpJ3KbNqHURraiXuBDqa3rd
Y4JBakCcuYHl1bj1OTew7xl1FWc1je04rBTQGTFIRdmJZYyc9bIw9WkY6msod0w1
PDcLhZS3emh/eYaL4zAQWrVhQfWzf4rZzFaI/a5n0o75aUkTuvxDDQ51V2d6WkSU
3KbOnf2JW+QJXfzsNOeiYA9AnfY59evr4GEeG8VZdGuUG39uDCIWmAUT8elhXXqV
q+NdBqc6VUNLDJbqCmMx/Ecp48EHO6X5uXm0xViZIVPNIzqiiRhVt4nFfwPQZrTg
aq2+tD7/zD1yED4O1FhlDl5twH2N7+oG06HsEluQdLPrj7IedpneGVKMs078Ddov
7j6icYv/RZHVetDlrzDDHjLJWwxyAWzdGdkhtMGPd6B9i4TtF/PU3J3nbpLn5XfE
BFu4jJ+w+5Wvk5a60gF1ERy/OLBM/e8sro2sEBIpp1tN1wJVBZOtTIi4VVDhwDRQ
Uiwb2d1Re7GQ7+mcz5D/01qxW6S+w0IKrpwJUjR3mpa0OU98KfKVJkeyeEBLkEhD
dnGTDqZ9E/ickGosrW2gAAYKgzXk725dpxTdpLEosfDbpwIDAQABMA0GCSqGSIb3
DQEBCwUAA4ICAQAnszSuVqfEmpjf2VMvk9TUuiop0tejHP+hB30IURJqA9K51edx
IRszXXU4Sj8uHT88RpKxgDm/GcfEA0l2rWZ6Mal6pmUyjteJJPMVA6fgeNM8XvtJ
eoxi2wm8FzxXJrPK7fOMG5/fLb7ENUZYFRHVFJ+Gk290DP7x81Gzb5tcsolrVqW+
TZdV2aBZya28NjgXncjinIlD61I6LzoQbDInab5nEPKMRuRTXMLfbAypXrPAbsfz
+Z6ZKhfNEo0/5cI4iG8MQXM1HgbFCkWOTPPeR53lo+1f9dN3IZ+1PYUjkOJzuxUZ
HIA+Dy+S1ocfK582LqohexhjeC5AL74rJJcgns9ORxz2GN1buIRTzi9XL2egp7cd
+XgZ3phpY4mIM0bH+DJ7eIqkM17WkEwJ3vazu7tEmIldc06Pmt2vFEcQB3T0bsw7
lBZdwSEkqTb+IexaQerSyztuxKc2DhOLTqZfVPCd2LWhasNSHzGmanI3vmBy98MN
LZzo7+G1BDMyMsl3DwEiwOGYARXJklU1LxCj6nVCTymNToLXtF2xHcZuK94Pqol9
n8zMCUYNOr7USWA25GwfpN65UHN7YXsOl9XIMWl+iVA5QepAI9sL0n3CyFW0ZXgn
DsZkfikYa+xhQSUANV4zDx1X8FxZmT0Op/+mhkvwL1+YKUHJy3WdXrIFgw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQHDBpTb21l
d2hlcmUAZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0zMTEyMTgx
NDU3MzVaMCUxIzAhBgNVBAcMGlNvbWV3aGVyZQBldmlsLmV4YW1wbGUuY29tMIIC
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOlnAio4We7
Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9XdJ7xb7/
IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3sO8ZdRVn
NY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+MwEFq1YUH1
s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTnomAPQJ32
OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpjMfxHKePB
Bzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRYZQ5ebcB9
je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8wwx4yyVsM
cgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIBdREcvziw
TP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q/9NasVuk
vsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1toAAGCoM1
5O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAmjKOoKxLwPY4
e65pYTUSBctPZ2juW5uNs8UvH5O32OC9RhENJBIIKn3B9Z/wkexR2zcvaQmJObLW
6mkR7O0tNgsXVYJFzLRBfjM/nyP6nafiCUekmoh9Kojq6x5IQQgEsK+Uw123kkoI
w/h3hBYBq8+CFPnYtBLZBVVFMNGaATXrYJPCcjVrtAHYxIWaDN2R+1DWLRIV72sF
hu4xGz0kmUbzforl/FA3gdgM7mwfZMF4+EoQZi5mShdWnyfzAHIbtahnA4lPNtx9
vBqYIZ/a2ITsXmWc2KGs/rRG+SDLzg+H1Xudvu/y2d1ULpZQfT6bg6Ro855FiU9h
TyHHQGGqlC9/DjHy//wERsFEJZh5/j21LGyalEjgfOYtzPkjZlIweYr8LlHTrauo
/gWihriaaWAkD+2fwQ09CUHdvOG6yoT+j/E50FsekfqV3tKMwoZoph6dF1TWQg32
JXV0akpd5ff1cca8sZgJfUksDfSkrwG7fl3tje30vQTlvNrhu2MCKFGQwyXed3qg
86lx+sTZjxMYvqWWysKTx8aIJ95XAK2jJ2OEVI2X6cdgoAp6aMkycbttik4hDoPJ
eAWaZo2UFs2MGoUbX9m4RzPqPuBHNFqoV6yRyS1K/3KWyxVVvamZY0Qgzmoi4coB
hRlTO6GDkF7u1YQ7eZi7pP7U8OcklfE=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEyTCCArGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQHDB1Tb21l
d2hlcmUKQ049ZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0zMTEy
MTgxNDU3MzVaMCgxJjAkBgNVBAcMHVNvbWV3aGVyZQpDTj1ldmlsLmV4YW1wbGUu
Y29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOl
nAio4We7Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9
XdJ7xb7/IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3
sO8ZdRVnNY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+Mw
EFq1YUH1s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTn
omAPQJ32OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpj
MfxHKePBBzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRY
ZQ5ebcB9je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8w
wx4yyVsMcgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIB
dREcvziwTP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q
/9NasVukvsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1t
oAAGCoM15O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAEMEW
EElTS/lgeoWvTruGEqmpwS86NE+j+Ws+VnUXnjo2RSqs4tSICkBzJsi4g/WHNa5V
TzD42MOmyQTUGaJ96Cpq8VmL8pE0mYKo1wXsi8WonDgaw0Eup6v9ga5kHPfKJBvV
dqEP+upiAbYXxlISj+xgOVW5WBJ3tBic1Iyg/oOKlHwXYA0IKc1MOLlvh0EdVqj7
2cYodO7nuAmeFLpf5RDtGTNMWt/whoqv+vUb5iy2pDdDNMJdoa0hT/L4E+ibl0ZA
7W/RKkcXJ0RlZMA7rYGjQ2/lasHvMniHlfLZd2UtChVgs8hY/b1PCLubyiz1peCj
Q8Y4VoveePnxfovTPvcvMxPbNiCLPJtsPhWq1KPbOyBpKBc/mJ6I5DmszQB16Jb2
fq6RfrrXjC1C+vYN4KCUGPbS+J4eZ0a04C4OdSGED02YSOpLIBnfNRMDyXZQ6Hhd
sZSvyOAD3UhugEloCV9cnFKVglbXaW3k97xeYg/86udVPrgiAEn7u3Lsr9U1wZ2x
wFgE4js1IzeIvIZOk9wDQHPolUiPaZUvMZXfM7+i9X9qX9AgtUAxnO0y0U9zXrUB
Xjdtfddb4XAHdrPnuBkCb/75JeQ4JroP3t59iY0SFuQ0TH9YkOJULrw7oTqqmLo+
PAFMiK1/kbmpVsT92k2WLjPgrAXe+lslQPwXBNM=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEzzCCAregAwIBAgIBATANBgkqhkiG9w0BAQsFADArMSkwJwYDVQQHDCBTb21l
d2hlcmUsIENOID0gZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0z
MTEyMTgxNDU3MzVaMCsxKTAnBgNVBAcMIFNvbWV3aGVyZSwgQ04gPSBldmlsLmV4
YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLv
RPU/5KOlnAio4We7Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjI
bc/oN2L9XdJ7xb7/IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB
5dW49Tk3sO8ZdRVnNY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3po
f3mGi+MwEFq1YUH1s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvk
CV387DTnomAPQJ32OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVD
SwyW6gpjMfxHKePBBzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9
chA+DtRYZQ5ebcB9je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR
1XrQ5a8wwx4yyVsMcgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuV
r5OWutIBdREcvziwTP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXux
kO/pnM+Q/9NasVukvsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4
nJBqLK1toAAGCoM15O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOC
AgEAFvcwnV5K6KH4jvYFUccZDEVZ2WFuZsqJVD5N4nX5KgHmnSzyDYgHRRZ4oGiN
eTgi+3B6S5TPRTMLUaO7hnFxilnfr3HlhsQhGVh+Qb+ovyL1evsrCu8CzmmFMJs1
bHm/ct/HzDfNgrx7HEZbrpesNjka05UWhIewA/64IkSMFoGbrjb35WINpcHQNgvQ
X5YnUTk3U+DyDHGeRvZ9dsYBXnK7Q+s6lbS1Bvl3G65SZq9fxqtxLnwloP5ms62j
r7OLdQ/IDYFu0v/HKkA9Ms/NJyKtoPUXYyiP0qQPq2A9lDRW07goCaR7WApmU4Sr
uYQVAPCFbEJGQtjUVUrmEdlEuNaiaMM7+iB5WEXaQ8M8gRX+4U7lbk7HsRSsHlDn
9/1sAOxrWAnCffoYSrUwruD8SKVCTBlkYs5pPSIkfz/yzwNq5u6ebe5ATJBjIv+H
N4nflcrY18oMAz694f+94RUFat/5wX+WsnNT4Av+bVz6Gv5nbGJGXurUArrne5F9
G+ESYu2KuGIRhxrOrBIvZapv9lITlBm9t8kChBbR9YZC4dD0+lu72h4xH3iXeeBl
MFmP1mk8zxuIwH6H/bM70B5NAHEw4U5guthnRU5YSK5EpvXhNl/JqdSp8xskfYCM
62dhRqgQNL0HZxKJO61bn3XBvVKLPNpCqBD5KQsI0R4wevM=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEyTCCArGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQHDB1Tb21l
d2hlcmUvQ049ZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0zMTEy
MTgxNDU3MzVaMCgxJjAkBgNVBAcMHVNvbWV3aGVyZS9DTj1ldmlsLmV4YW1wbGUu
Y29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOl
nAio4We7Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9
XdJ7xb7/IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3
sO8ZdRVnNY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+Mw
EFq1YUH1s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTn
omAPQJ32OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpj
MfxHKePBBzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRY
ZQ5ebcB9je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8w
wx4yyVsMcgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIB
dREcvziwTP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q
/9NasVukvsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1t
oAAGCoM15O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAQD16
wSsZodV3hk98VYDyXBuQdzrlF1zXm5n7Dx+ONGw62d3FRRaegbkwBfvUf7P+ZfR/
qUFZQwWKYZ+hYos/gIvYuBRJSSg8nrGrHkp+AXIxQ6ZmgVAat3OnLdzG+k0Cras6
vzRrEohL3JnXCBVZ+4MMnNrZFhGzQ9rHGJtrarkZ5NQMhH8VbfdtuKDpwS8O9mtI
MqoNTIViocqtBem8ZD5z+m9A5UT8DMKwL+gjDQQ3j/flfmAq5bcqZkkIrJol3mrp
4Ol1Hc4/tVMa1wsnEtYGWEOfBJqANY3m5IiEBHIyeP67NR68fdlZ+XFpdHNl5/LV
XwjGquv0jSE3CbKR1ez5sefn1fmCWVZi5mZV6O8jpT7Ztu1XL8jOxTxtCMKE6cCC
xgEL2HFG4JWeA/z5ZXT8U+4Bfiu1GXBMxF5LJc89DORTBRIWMR1IHca+nOb2zHNF
v4QOfqLKF+ko5D/ie9Xg1s49l6lI8NReg9NRRp2sc90Zxc0Pqz7wdNH2SMUC/+gR
kWhz77OhACeXpcRQVy0Bi64l5Or+05ZB2piK6OemcFUKIybKjxUbzuwZdrqj0vK6
Tw1nemA1BCH8X+b1rz6kDKPycBAEdtMoRSFzbtZbdjBR1g0PLGeYn8rL2gsLMpaN
1XTCTb7BAAy0Ky4cpMduD+uYGbma9V4ER3RLdL8=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEyTCCArGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQHDB1Nw7xu
Y2hlblwKQ049ZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0zMTEy
MTgxNDU3MzVaMCgxJjAkBgNVBAcMHU3DvG5jaGVuXApDTj1ldmlsLmV4YW1wbGUu
Y29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOl
nAio4We7Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9
XdJ7xb7/IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3
sO8ZdRVnNY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+Mw
EFq1YUH1s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTn
omAPQJ32OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpj
MfxHKePBBzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRY
ZQ5ebcB9je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8w
wx4yyVsMcgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIB
dREcvziwTP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q
/9NasVukvsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1t
oAAGCoM15O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAipRw
3Q8C0CUYTQJlYTAdmATrboUFATpex+ZFhQgQPPWs/tUvf8zWU+DdDjFjrLNCY+ew
FaURBnNQ92AE3LVDayu3Jh6TMoHKMAnPOERaiMuHDoKr/T4JVk2vWSBck6aYbokl
7W7/ucMTVyPS9tLiuIwyJ+0dta+ucQSjIZj2RtCzsOtxdbUqt/7iTJrl8EjZGGbH
FTKSbFBY2mR9oFKhoyCaVV0Alw1//napqdzu93gNqZx3cXskA0T63GxyhjhVpFq8
d1ILGB3yKAiIzc5epNKx8ZPSUddx7zK0FAXRtBGHcOTES3+kTljkxmXAFDGTrMk0
fsWgKfDDkDEGaUHL43524HLnPUoQASdQ9Uk5r7TDkl/kATv5w+HpWKdd3sxcSH8m
UeUFCFdJbcOyqKfF7jz8kCe08Xt2sEW5tKZb4xWjI+mm01PCNeyCsaAw4OlSDUEm
63fCsXY/b+i0hOxdd/eusoq3B76ngOEGaEJ8jOvpxeyHuet9kDet5M48aQRE9S9x
HJWLL+80mFt4yiRHPUob/WP+4L7EnBjmiVBevEO0sptYLqymdRuCy4Ub4/QIQnNW
kFasltzL/WEe1TzpTNziqOk1jEHA06D5Euwy/mI+S0Y0uvFOYC+tVkspsCNikrTu
Fj0Lqyg5tqQJM3msSEfJvaJhUydaeIZp1Cr535Y=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIE0zCCArugAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMSswEAYDVQQHDAlTb21l
d2hlcmUwFwYDVQQDDBBldmlsLmV4YW1wbGUuY29tMB4XDTIxMTIyMDE0NTczNVoX
DTMxMTIxODE0NTczNVowLTErMBAGA1UEBwwJU29tZXdoZXJlMBcGA1UEAwwQZXZp
bC5leGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALER
Z3TS70T1P+SjpZwIqOFnu2Od9XKWcDszQQc7C92K+APjp4BvWiayictqCJWtmsbs
Sli4yG3P6Ddi/V3Se8W+/yB71Qh2c3wQNMPMukncps2odRGtqJe4EOpret1jgkFq
QJy5geXVuPU5N7DvGXUVZzWN7TisFNAZMUhF2YlljJz1sjD1aRjqayh3TDU8NwuF
lLd6aH95hovjMBBatWFB9bN/itnMVoj9rmfSjvlpSRO6/EMNDnVXZ3paRJTcps6d
/Ylb5Ald/Ow056JgD0Cd9jn16+vgYR4bxVl0a5Qbf24MIhaYBRPx6WFdepWr410G
pzpVQ0sMluoKYzH8RynjwQc7pfm5ebTFWJkhU80jOqKJGFW3icV/A9BmtOBqrb60
Pv/MPXIQPg7UWGUOXm3AfY3v6gbToewSW5B0s+uPsh52md4ZUoyzTvwN2i/uPqJx
i/9FkdV60OWvMMMeMslbDHIBbN0Z2SG0wY93oH2LhO0X89Tcnedukufld8QEW7iM
n7D7la+TlrrSAXURHL84sEz97yyujawQEimnW03XAlUFk61MiLhVUOHANFBSLBvZ
3VF7sZDv6ZzPkP/TWrFbpL7DQgqunAlSNHealrQ5T3wp8pUmR7J4QEuQSEN2cZMO
pn0T+JyQaiytbaAABgqDNeTvbl2nFN2ksSix8NunAgMBAAEwDQYJKoZIhvcNAQEL
BQADggIBAAdRC4tmZb5tukc4pIdnzRyrzNq3uefQNLcrZpZaCKAWvey+AFOZw88N
nnjUT0A3bXA2YJPKQtRaSJG+UBH3xgRNOM0ttvKYqmzZDt/ygzxRlTMt80AVVyMG
P06D5UUZHEX6aUchS/noDI5jewZy23jINEAzQv8B72r8WjV/LwjbJ1IoBg08gJhO
QQCfeDaJ0sAQCL1tdlwiS6Q3N6rkC3jLzBHCzXP0FN5OF5rxr6nlfHiTOuhTdodR
p/UrLVADdvpXq6SegbTvZ7/KwNWzzAmOEx2MAHFQKh46S1+RHQE3L7SV9dqV2XCe
OxfBPPXTy+AiceKhVL0+jhdI/VWIdhTHSCeFuzrGbrLQwWLCDZ5AZjS/JaBXuVGl
WILzz3ZG6ekdqMY/qG8weDEFv49f03MGWoX27uhkz4qtumLzrXEspzL7GwUfnDZo
zyF9Jo9vJVNmiz/N2DnUd0X5hdHUsjnN8vPN+3u5kkvfXTgT9wUrMgzECu/tyC92
GAX0MqY6lKJwTT+pxkZPUNGMbP8c3BuO9NVGPUeOA+/4sgsws+V0TDF7umNk2nq3
vCuS+QFZXAR4Ns2xgIOMH8XQjRZ4qSp3HsFNehOqSQrFvcgjMLo0RcgiwgReUMl+
Pnhjk+V4ttEIUe3UswaRHD9moG4sgCfFk/bafwCvdKonD6mBETMa
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEzTCCArWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAqMSgwJgYDVQQHDB9Tb21l
d2hlcmUgKyBDTj1ldmlsLmV4YW1wbGUuY29tMB4XDTIxMTIyMDE0NTczNVoXDTMx
MTIxODE0NTczNVowKjEoMCYGA1UEBwwfU29tZXdoZXJlICsgQ049ZXZpbC5leGFt
cGxlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALERZ3TS70T1
P+SjpZwIqOFnu2Od9XKWcDszQQc7C92K+APjp4BvWiayictqCJWtmsbsSli4yG3P
6Ddi/V3Se8W+/yB71Qh2c3wQNMPMukncps2odRGtqJe4EOpret1jgkFqQJy5geXV
uPU5N7DvGXUVZzWN7TisFNAZMUhF2YlljJz1sjD1aRjqayh3TDU8NwuFlLd6aH95
hovjMBBatWFB9bN/itnMVoj9rmfSjvlpSRO6/EMNDnVXZ3paRJTcps6d/Ylb5Ald
/Ow056JgD0Cd9jn16+vgYR4bxVl0a5Qbf24MIhaYBRPx6WFdepWr410GpzpVQ0sM
luoKYzH8RynjwQc7pfm5ebTFWJkhU80jOqKJGFW3icV/A9BmtOBqrb60Pv/MPXIQ
Pg7UWGUOXm3AfY3v6gbToewSW5B0s+uPsh52md4ZUoyzTvwN2i/uPqJxi/9FkdV6
0OWvMMMeMslbDHIBbN0Z2SG0wY93oH2LhO0X89Tcnedukufld8QEW7iMn7D7la+T
lrrSAXURHL84sEz97yyujawQEimnW03XAlUFk61MiLhVUOHANFBSLBvZ3VF7sZDv
6ZzPkP/TWrFbpL7DQgqunAlSNHealrQ5T3wp8pUmR7J4QEuQSEN2cZMOpn0T+JyQ
aiytbaAABgqDNeTvbl2nFN2ksSix8NunAgMBAAEwDQYJKoZIhvcNAQELBQADggIB
AAG8vjV7c4B4yKO2BDhufVjkmzot97SPf4qR0qJATAV+Iifm5D2YL/dr36kyvTiK
JoPU/0vztcnh5X75YzvEtD4xh5zg3FQdAEpGx4zZkNXkJt2syz3V3DFG9Te4GH3n
/a39z4yn2J2MG2uXj+TTSJR23ICAgqNkj4EtrwvOouAqLCR/yZuYaUM6ZPmEYrHM
5wwiMCheDgMUYvFhTIKAwalnQitCGQCFr5WvTHU/0oVn498miZEU5LPAIiuhIQoA
UI/tro47evU/Nli8WY9UImLbcWkbIS7MogtWhjDQXd80G3sX+9DpVO43S2Cf4shB
yXl49bvqITMXdurSQrNKbfQ5aLDmKno4Qjs9wZMmi2xhIKczuB4bdtQDsC0/LiSr
oydiSP9uxYatT6SedzgkypTOL/5qtuh14Z7aRio5s4WrIDDJ1RVlWJGffq4hF+j/
cu5OHo4cyvN42+bnyYzAWpOE7h8Nmi0D14zvm1FE3FKVSlBZzScBBungVdJkchAP
4JleXVqfH5skLgMiYCa3qocfUEfeKTCVXJUxaPIvBILtcOYzx75B0izsVlsd/dr+
DqoIKN9aMGyuKR0QZtmW97eCxaH6Dm7lVuym56hiQrT3J0PL2iU+LU1R9UfLE/pL
RjUWW/gbxxNq8dbFybiUM7Sj+6tWuVvLygA04lMeDIDq
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADAlMRYwCQYDVQQHDAJMMTAJ
BgNVBAcMAkwyMQswCQYDVQQHDAJMMzAeFw0yMTEyMjAxNDU3MzVaFw0zMTEyMTgx
NDU3MzVaMCUxFjAJBgNVBAcMAkwxMAkGA1UEBwwCTDIxCzAJBgNVBAcMAkwzMIIC
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOlnAio4We7
Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9XdJ7xb7/
IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3sO8ZdRVn
NY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+MwEFq1YUH1
s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTnomAPQJ32
OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpjMfxHKePB
Bzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRYZQ5ebcB9
je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8wwx4yyVsM
cgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIBdREcvziw
TP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q/9NasVuk
vsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1toAAGCoM1
5O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAGNhY0vKd8Os9
75+HHQH03BugatuIykpSu+tj8OYr2/7VLT76qUaKdkAZV0m9TiS8MitHZieEbig3
EozQtYrTZQbiFjiV8FudPsmAXZxcz1TdE25mZykWe24FmZNdeMQmoVRZYbg3gb/M
sTEDbnV3DoW6X8LWMlitaBpisxg/LqHakATvj6Otvts8RFhI1c/JFx8THuY14Fj1
sJ8eFdwebPK35V4ZNtH8bevVo9MvnUS290fF1WDC1dnjZ1zYqHT7sPoGbCFF4kne
TF2Ef12BgUNtgJKnXeEV5Gull4iOQS8qTkWCIm8jbz1+9ap8nqVcGn60bkwiMmgz
hNyBW7c31MvEfedfCwFma/uV1yMB2nGwX47TMnTTjwc5b2I/lOrFOfeh2JD9QVZF
XFKRsVXqCwa3aLc1fc93M9kEHzKWzGgMjYvJzZEGsoqTil22NmQXIG7jKjLth7zF
4Sc/qBDXsLaqUaWQveZ9U6suFYr9u2X7h3KkciFtsZPFK+AZGO07z/4nWEeo4frV
RyltN38BmJxwBSxNEZFBiMJ9AEmg2EhgBXJbEhN9XCwpW2EEp+M09AfcebzKjJ+h
3Q7AWlTPawz/PQzzunZzNMkq7/6Y/dIFg/Ak8RIPkMVb3xE9oD0wMWigyiK05UUI
832NnZXih3qq15MfVS4eTSeKrNcFt3c=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIExzCCAq+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAnMQswCQYDVQQHDAJMMTEL
MAkGA1UEBwwCTDIxCzAJBgNVBAcMAkwzMB4XDTIxMTIyMDE0NTczNVoXDTMxMTIx
ODE0NTczNVowJzELMAkGA1UEBwwCTDExCzAJBgNVBAcMAkwyMQswCQYDVQQHDAJM
MzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALERZ3TS70T1P+SjpZwI
qOFnu2Od9XKWcDszQQc7C92K+APjp4BvWiayictqCJWtmsbsSli4yG3P6Ddi/V3S
e8W+/yB71Qh2c3wQNMPMukncps2odRGtqJe4EOpret1jgkFqQJy5geXVuPU5N7Dv
GXUVZzWN7TisFNAZMUhF2YlljJz1sjD1aRjqayh3TDU8NwuFlLd6aH95hovjMBBa
tWFB9bN/itnMVoj9rmfSjvlpSRO6/EMNDnVXZ3paRJTcps6d/Ylb5Ald/Ow056Jg
D0Cd9jn16+vgYR4bxVl0a5Qbf24MIhaYBRPx6WFdepWr410GpzpVQ0sMluoKYzH8
RynjwQc7pfm5ebTFWJkhU80jOqKJGFW3icV/A9BmtOBqrb60Pv/MPXIQPg7UWGUO
Xm3AfY3v6gbToewSW5B0s+uPsh52md4ZUoyzTvwN2i/uPqJxi/9FkdV60OWvMMMe
MslbDHIBbN0Z2SG0wY93oH2LhO0X89Tcnedukufld8QEW7iMn7D7la+TlrrSAXUR
HL84sEz97yyujawQEimnW03XAlUFk61MiLhVUOHANFBSLBvZ3VF7sZDv6ZzPkP/T
WrFbpL7DQgqunAlSNHealrQ5T3wp8pUmR7J4QEuQSEN2cZMOpn0T+JyQaiytbaAA
BgqDNeTvbl2nFN2ksSix8NunAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAEeFRIyV
5PdD7Xipg3byNhcCH6I8gADM+Ipnxic93COfQrWCKd/lnsJzxml7VhyANScUTx44
wkYs+kW9Xi/tEViVwrsFzlTB3YwaAYPiGNtr98B4JBUfLneHSh8IUeeMUnBeLt4O
eqo3ts38hCfY3B3E2FtV9nRBKu91ZwE+pInWftdTJ6pIkltr+t9kPbVFW72hYfQJ
rdtyzIiSkTnJElcvNcWtsqEmTMLewgZz/bjbZkQh/LXQDT7oepZBZ5Qb4F8kwytb
wGC/OFoByWyXYfuPWKb2obdnbb5xa1vg8rLVdVgY25q+VeNItBB/FSzf0Pnxd9od
jVVtzvby57A0IT7XpTu8RFAkuWmZp4FO5kDyXLNgsd6md/qeqcO5V7dY6MSKeIXw
nMYTBWuxOZPMw2RnxjcfkEdN/5sDuYHnzuizkH+OiwPPfs2qa4EETaxo5xxmTcy+
pDh0GEOIgyazpJnncgG1k1ABOcHevRaCpm8NuXexkfpAHEORNfOflRkJDICXSUxv
5o2VjOhqj8gRqLvpGBW3hCxVM/Of2Fzdye0ldoDhzcW0WxjzmcjcC5EEEVSapwok
K5+ZvVFjqW2j619UICFf95tCtB025AzWWwVVQ9rlnCWL0MOrOwe66vYERG2MUYAD
jcB7FUOjXh2+3Gkh1PzXiXCQatDLhIVt9Vus
-----END CERTIFICATE-----

View File

@ -247,6 +247,115 @@ const { hasOpenSSL3 } = common;
}
}
// Test escaping rules for the subject field.
{
const expectedSubjects = [
{
text: 'L=Somewhere\nCN=evil.example.com',
legacy: {
L: 'Somewhere',
CN: 'evil.example.com',
},
},
{
text: 'L=Somewhere\\00evil.example.com',
legacy: {
L: 'Somewhere\0evil.example.com',
},
},
{
text: 'L=Somewhere\\0ACN=evil.example.com',
legacy: {
L: 'Somewhere\nCN=evil.example.com'
},
},
{
text: 'L=Somewhere\\, CN = evil.example.com',
legacy: {
L: 'Somewhere, CN = evil.example.com'
},
},
{
text: 'L=Somewhere/CN=evil.example.com',
legacy: {
L: 'Somewhere/CN=evil.example.com'
},
},
{
text: 'L=München\\\\\\0ACN=evil.example.com',
legacy: {
L: 'München\\\nCN=evil.example.com'
}
},
{
text: 'L=Somewhere + CN=evil.example.com',
legacy: {
L: 'Somewhere',
CN: 'evil.example.com',
}
},
{
text: 'L=Somewhere \\+ CN=evil.example.com',
legacy: {
L: 'Somewhere + CN=evil.example.com'
}
},
// Observe that the legacy representation cannot properly distinguish
// between multi-value RDNs and multiple single-value RDNs.
{
text: 'L=L1 + L=L2\nL=L3',
legacy: {
L: ['L1', 'L2', 'L3']
},
},
{
text: 'L=L1\nL=L2\nL=L3',
legacy: {
L: ['L1', 'L2', 'L3']
},
},
];
const serverKey = fixtures.readSync('x509-escaping/server-key.pem', 'utf8');
for (let i = 0; i < expectedSubjects.length; i++) {
const pem = fixtures.readSync(`x509-escaping/subj-${i}-cert.pem`, 'utf8');
const expected = expectedSubjects[i];
// Test the subject property of the X509Certificate API.
const cert = new X509Certificate(pem);
assert.strictEqual(cert.subject, expected.text);
// The issuer MUST be the same as the subject since the cert is self-signed.
assert.strictEqual(cert.issuer, expected.text);
// Test that the certificate obtained by checkServerIdentity has the correct
// subject property.
const server = tls.createServer({
key: serverKey,
cert: pem,
}, common.mustCall((conn) => {
conn.destroy();
server.close();
})).listen(common.mustCall(() => {
const { port } = server.address();
tls.connect(port, {
ca: pem,
servername: 'example.com',
checkServerIdentity: (hostname, peerCert) => {
assert.strictEqual(hostname, 'example.com');
const expectedObject = Object.assign(Object.create(null),
expected.legacy);
assert.deepStrictEqual(peerCert.subject, expectedObject);
// The issuer MUST be the same as the subject since the cert is
// self-signed. Otherwise, OpenSSL would have already rejected the
// certificate while connecting to the TLS server.
assert.deepStrictEqual(peerCert.issuer, expectedObject);
},
}, common.mustCall());
}));
}
}
// The internal parsing logic must match the JSON specification exactly.
{
// This list is partially based on V8's own JSON tests.