Skip to content

Commit 83d8f88

Browse files
tniessenkumarak
authored andcommittedJan 7, 2022
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. Co-authored-by: Akshay K <iit.akshay@gmail.com> CVE-ID: CVE-2021-44533 Backport-PR-URL: nodejs-private/node-private#305 PR-URL: nodejs-private/node-private#300 Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 461a0c6 commit 83d8f88

15 files changed

+660
-12
lines changed
 

‎lib/_tls_common.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,13 @@ exports.translatePeerCertificate = function translatePeerCertificate(c) {
313313
if (!c)
314314
return null;
315315

316-
if (c.issuer != null) c.issuer = parseCertString(c.issuer);
316+
// TODO(tniessen): can we remove parseCertString without breaking anything?
317+
if (typeof c.issuer === 'string') c.issuer = parseCertString(c.issuer);
317318
if (c.issuerCertificate != null && c.issuerCertificate !== c) {
318319
c.issuerCertificate = translatePeerCertificate(c.issuerCertificate);
319320
}
320-
if (c.subject != null) c.subject = parseCertString(c.subject);
321+
// TODO(tniessen): can we remove parseCertString without breaking anything?
322+
if (typeof c.subject === 'string') c.subject = parseCertString(c.subject);
321323
if (c.infoAccess != null) {
322324
const info = c.infoAccess;
323325
c.infoAccess = ObjectCreate(null);

‎src/node_crypto_common.cc

+120-9
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ using v8::Value;
4141
namespace crypto {
4242

4343
static constexpr int X509_NAME_FLAGS =
44+
ASN1_STRFLGS_ESC_2253 |
4445
ASN1_STRFLGS_ESC_CTRL |
4546
ASN1_STRFLGS_UTF8_CONVERT |
4647
XN_FLAG_SEP_MULTILINE |
@@ -797,6 +798,94 @@ v8::MaybeLocal<v8::Value> GetInfoAccessString(
797798
return ToV8Value(env, bio);
798799
}
799800

801+
template <X509_NAME* get_name(const X509*)>
802+
static MaybeLocal<Value> GetX509NameObject(Environment* env, X509* cert) {
803+
X509_NAME* name = get_name(cert);
804+
CHECK_NOT_NULL(name);
805+
806+
int cnt = X509_NAME_entry_count(name);
807+
CHECK_GE(cnt, 0);
808+
809+
Local<Object> result =
810+
Object::New(env->isolate(), Null(env->isolate()), nullptr, nullptr, 0);
811+
if (result.IsEmpty()) {
812+
return MaybeLocal<Value>();
813+
}
814+
815+
for (int i = 0; i < cnt; i++) {
816+
X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, i);
817+
CHECK_NOT_NULL(entry);
818+
819+
// We intentionally ignore the value of X509_NAME_ENTRY_set because the
820+
// representation as an object does not allow grouping entries into sets
821+
// anyway, and multi-value RDNs are rare, i.e., the vast majority of
822+
// Relative Distinguished Names contains a single type-value pair only.
823+
const ASN1_OBJECT* type = X509_NAME_ENTRY_get_object(entry);
824+
const ASN1_STRING* value = X509_NAME_ENTRY_get_data(entry);
825+
826+
// If OpenSSL knows the type, use the short name of the type as the key, and
827+
// the numeric representation of the type's OID otherwise.
828+
int type_nid = OBJ_obj2nid(type);
829+
char type_buf[80];
830+
const char* type_str;
831+
if (type_nid != NID_undef) {
832+
type_str = OBJ_nid2sn(type_nid);
833+
CHECK_NOT_NULL(type_str);
834+
} else {
835+
OBJ_obj2txt(type_buf, sizeof(type_buf), type, true);
836+
type_str = type_buf;
837+
}
838+
839+
Local<String> v8_name;
840+
if (!String::NewFromUtf8(env->isolate(), type_str,
841+
NewStringType::kNormal).ToLocal(&v8_name)) {
842+
return MaybeLocal<Value>();
843+
}
844+
845+
// The previous implementation used X509_NAME_print_ex, which escapes some
846+
// characters in the value. The old implementation did not decode/unescape
847+
// values correctly though, leading to ambiguous and incorrect
848+
// representations. The new implementation only converts to Unicode and does
849+
// not escape anything.
850+
unsigned char* value_str;
851+
int value_str_size = ASN1_STRING_to_UTF8(&value_str, value);
852+
if (value_str_size < 0) {
853+
return Undefined(env->isolate());
854+
}
855+
856+
Local<String> v8_value;
857+
if (!String::NewFromUtf8(env->isolate(),
858+
reinterpret_cast<const char*>(value_str),
859+
NewStringType::kNormal,
860+
value_str_size).ToLocal(&v8_value)) {
861+
OPENSSL_free(value_str);
862+
return MaybeLocal<Value>();
863+
}
864+
865+
OPENSSL_free(value_str);
866+
867+
// For backward compatibility, we only create arrays if multiple values
868+
// exist for the same key. That is not great but there is not much we can
869+
// change here without breaking things. Note that this creates nested data
870+
// structures, yet still does not allow representing Distinguished Names
871+
// accurately.
872+
if (result->HasOwnProperty(env->context(), v8_name).ToChecked()) {
873+
Local<Value> accum =
874+
result->Get(env->context(), v8_name).ToLocalChecked();
875+
if (!accum->IsArray()) {
876+
accum = Array::New(env->isolate(), &accum, 1);
877+
result->Set(env->context(), v8_name, accum).Check();
878+
}
879+
Local<Array> array = accum.As<Array>();
880+
array->Set(env->context(), array->Length(), v8_value).Check();
881+
} else {
882+
result->Set(env->context(), v8_name, v8_value).Check();
883+
}
884+
}
885+
886+
return result;
887+
}
888+
800889
MaybeLocal<Value> GetFingerprintDigest(
801890
Environment* env,
802891
const EVP_MD* method,
@@ -1160,22 +1249,44 @@ MaybeLocal<Value> GetPeerCert(
11601249
return result;
11611250
}
11621251

1163-
MaybeLocal<Object> X509ToObject(Environment* env, X509* cert) {
1252+
MaybeLocal<Object> X509ToObject(
1253+
Environment* env,
1254+
X509* cert,
1255+
bool names_as_string) {
11641256
EscapableHandleScope scope(env->isolate());
11651257
Local<Context> context = env->context();
11661258
Local<Object> info = Object::New(env->isolate());
11671259

11681260
BIOPointer bio(BIO_new(BIO_s_mem()));
11691261

1262+
if (names_as_string) {
1263+
// TODO(tniessen): this branch should not have to exist. It is only here
1264+
// because toLegacyObject() does not actually return a legacy object, and
1265+
// instead represents subject and issuer as strings.
1266+
if (!Set<Value>(context,
1267+
info,
1268+
env->subject_string(),
1269+
GetSubject(env, bio, cert)) ||
1270+
!Set<Value>(context,
1271+
info,
1272+
env->issuer_string(),
1273+
GetIssuerString(env, bio, cert))) {
1274+
return MaybeLocal<Object>();
1275+
}
1276+
} else {
1277+
if (!Set<Value>(context,
1278+
info,
1279+
env->subject_string(),
1280+
GetX509NameObject<X509_get_subject_name>(env, cert)) ||
1281+
!Set<Value>(context,
1282+
info,
1283+
env->issuer_string(),
1284+
GetX509NameObject<X509_get_issuer_name>(env, cert))) {
1285+
return MaybeLocal<Object>();
1286+
}
1287+
}
1288+
11701289
if (!Set<Value>(context,
1171-
info,
1172-
env->subject_string(),
1173-
GetSubject(env, bio, cert)) ||
1174-
!Set<Value>(context,
1175-
info,
1176-
env->issuer_string(),
1177-
GetIssuerString(env, bio, cert)) ||
1178-
!Set<Value>(context,
11791290
info,
11801291
env->subjectaltname_string(),
11811292
GetSubjectAltNameString(env, bio, cert)) ||

‎src/node_crypto_common.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ v8::MaybeLocal<v8::Object> ECPointToBuffer(
122122

123123
v8::MaybeLocal<v8::Object> X509ToObject(
124124
Environment* env,
125-
X509* cert);
125+
X509* cert,
126+
bool names_as_string = false);
126127

127128
} // namespace crypto
128129
} // namespace node

‎test/fixtures/x509-escaping/create-certs.js

+141
Original file line numberDiff line numberDiff line change
@@ -500,3 +500,144 @@ for (let i = 0; i < infoAccessExtensions.length; i++) {
500500
});
501501
writeFileSync(`./info-${i}-cert.pem`, `${pem}\n`);
502502
}
503+
504+
const subjects = [
505+
[
506+
[
507+
{ type: oid.localityName, value: UTF8String.encode('Somewhere') }
508+
],
509+
[
510+
{ type: oid.commonName, value: UTF8String.encode('evil.example.com') }
511+
]
512+
],
513+
[
514+
[
515+
{
516+
type: oid.localityName,
517+
value: UTF8String.encode('Somewhere\0evil.example.com'),
518+
}
519+
]
520+
],
521+
[
522+
[
523+
{
524+
type: oid.localityName,
525+
value: UTF8String.encode('Somewhere\nCN=evil.example.com')
526+
}
527+
]
528+
],
529+
[
530+
[
531+
{
532+
type: oid.localityName,
533+
value: UTF8String.encode('Somewhere, CN = evil.example.com')
534+
}
535+
]
536+
],
537+
[
538+
[
539+
{
540+
type: oid.localityName,
541+
value: UTF8String.encode('Somewhere/CN=evil.example.com')
542+
}
543+
]
544+
],
545+
[
546+
[
547+
{
548+
type: oid.localityName,
549+
value: UTF8String.encode('M\u00fcnchen\\\nCN=evil.example.com')
550+
}
551+
]
552+
],
553+
[
554+
[
555+
{ type: oid.localityName, value: UTF8String.encode('Somewhere') },
556+
{ type: oid.commonName, value: UTF8String.encode('evil.example.com') },
557+
]
558+
],
559+
[
560+
[
561+
{
562+
type: oid.localityName,
563+
value: UTF8String.encode('Somewhere + CN=evil.example.com'),
564+
}
565+
]
566+
],
567+
[
568+
[
569+
{ type: oid.localityName, value: UTF8String.encode('L1') },
570+
{ type: oid.localityName, value: UTF8String.encode('L2') },
571+
],
572+
[
573+
{ type: oid.localityName, value: UTF8String.encode('L3') },
574+
]
575+
],
576+
[
577+
[
578+
{ type: oid.localityName, value: UTF8String.encode('L1') },
579+
],
580+
[
581+
{ type: oid.localityName, value: UTF8String.encode('L2') },
582+
],
583+
[
584+
{ type: oid.localityName, value: UTF8String.encode('L3') },
585+
],
586+
],
587+
];
588+
589+
for (let i = 0; i < subjects.length; i++) {
590+
const tbs = {
591+
version: 'v3',
592+
serialNumber: new BN('01', 16),
593+
signature: {
594+
algorithm: oid.sha256WithRSAEncryption,
595+
parameters: null_
596+
},
597+
issuer: {
598+
type: 'rdnSequence',
599+
value: subjects[i]
600+
},
601+
validity: {
602+
notBefore: { type: 'utcTime', value: now },
603+
notAfter: { type: 'utcTime', value: now + days * 86400000 }
604+
},
605+
subject: {
606+
type: 'rdnSequence',
607+
value: subjects[i]
608+
},
609+
subjectPublicKeyInfo: {
610+
algorithm: {
611+
algorithm: oid.rsaEncryption,
612+
parameters: null_
613+
},
614+
subjectPublicKey: {
615+
unused: 0,
616+
data: publicKey
617+
}
618+
}
619+
};
620+
621+
// Self-sign the certificate.
622+
const tbsDer = rfc5280.TBSCertificate.encode(tbs, 'der');
623+
const signature = crypto.createSign(digest).update(tbsDer).sign(privateKey);
624+
625+
// Construct the signed certificate.
626+
const cert = {
627+
tbsCertificate: tbs,
628+
signatureAlgorithm: {
629+
algorithm: oid.sha256WithRSAEncryption,
630+
parameters: null_
631+
},
632+
signature: {
633+
unused: 0,
634+
data: signature
635+
}
636+
};
637+
638+
// Store the signed certificate.
639+
const pem = rfc5280.Certificate.encode(cert, 'pem', {
640+
label: 'CERTIFICATE'
641+
});
642+
writeFileSync(`./subj-${i}-cert.pem`, `${pem}\n`);
643+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIE1zCCAr+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAvMRIwEAYDVQQHDAlTb21l
3+
d2hlcmUxGTAXBgNVBAMMEGV2aWwuZXhhbXBsZS5jb20wHhcNMjExMjIwMTQ1NzM1
4+
WhcNMzExMjE4MTQ1NzM1WjAvMRIwEAYDVQQHDAlTb21ld2hlcmUxGTAXBgNVBAMM
5+
EGV2aWwuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
6+
AQCxEWd00u9E9T/ko6WcCKjhZ7tjnfVylnA7M0EHOwvdivgD46eAb1omsonLagiV
7+
rZrG7EpYuMhtz+g3Yv1d0nvFvv8ge9UIdnN8EDTDzLpJ3KbNqHURraiXuBDqa3rd
8+
Y4JBakCcuYHl1bj1OTew7xl1FWc1je04rBTQGTFIRdmJZYyc9bIw9WkY6msod0w1
9+
PDcLhZS3emh/eYaL4zAQWrVhQfWzf4rZzFaI/a5n0o75aUkTuvxDDQ51V2d6WkSU
10+
3KbOnf2JW+QJXfzsNOeiYA9AnfY59evr4GEeG8VZdGuUG39uDCIWmAUT8elhXXqV
11+
q+NdBqc6VUNLDJbqCmMx/Ecp48EHO6X5uXm0xViZIVPNIzqiiRhVt4nFfwPQZrTg
12+
aq2+tD7/zD1yED4O1FhlDl5twH2N7+oG06HsEluQdLPrj7IedpneGVKMs078Ddov
13+
7j6icYv/RZHVetDlrzDDHjLJWwxyAWzdGdkhtMGPd6B9i4TtF/PU3J3nbpLn5XfE
14+
BFu4jJ+w+5Wvk5a60gF1ERy/OLBM/e8sro2sEBIpp1tN1wJVBZOtTIi4VVDhwDRQ
15+
Uiwb2d1Re7GQ7+mcz5D/01qxW6S+w0IKrpwJUjR3mpa0OU98KfKVJkeyeEBLkEhD
16+
dnGTDqZ9E/ickGosrW2gAAYKgzXk725dpxTdpLEosfDbpwIDAQABMA0GCSqGSIb3
17+
DQEBCwUAA4ICAQAnszSuVqfEmpjf2VMvk9TUuiop0tejHP+hB30IURJqA9K51edx
18+
IRszXXU4Sj8uHT88RpKxgDm/GcfEA0l2rWZ6Mal6pmUyjteJJPMVA6fgeNM8XvtJ
19+
eoxi2wm8FzxXJrPK7fOMG5/fLb7ENUZYFRHVFJ+Gk290DP7x81Gzb5tcsolrVqW+
20+
TZdV2aBZya28NjgXncjinIlD61I6LzoQbDInab5nEPKMRuRTXMLfbAypXrPAbsfz
21+
+Z6ZKhfNEo0/5cI4iG8MQXM1HgbFCkWOTPPeR53lo+1f9dN3IZ+1PYUjkOJzuxUZ
22+
HIA+Dy+S1ocfK582LqohexhjeC5AL74rJJcgns9ORxz2GN1buIRTzi9XL2egp7cd
23+
+XgZ3phpY4mIM0bH+DJ7eIqkM17WkEwJ3vazu7tEmIldc06Pmt2vFEcQB3T0bsw7
24+
lBZdwSEkqTb+IexaQerSyztuxKc2DhOLTqZfVPCd2LWhasNSHzGmanI3vmBy98MN
25+
LZzo7+G1BDMyMsl3DwEiwOGYARXJklU1LxCj6nVCTymNToLXtF2xHcZuK94Pqol9
26+
n8zMCUYNOr7USWA25GwfpN65UHN7YXsOl9XIMWl+iVA5QepAI9sL0n3CyFW0ZXgn
27+
DsZkfikYa+xhQSUANV4zDx1X8FxZmT0Op/+mhkvwL1+YKUHJy3WdXrIFgw==
28+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQHDBpTb21l
3+
d2hlcmUAZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0zMTEyMTgx
4+
NDU3MzVaMCUxIzAhBgNVBAcMGlNvbWV3aGVyZQBldmlsLmV4YW1wbGUuY29tMIIC
5+
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOlnAio4We7
6+
Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9XdJ7xb7/
7+
IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3sO8ZdRVn
8+
NY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+MwEFq1YUH1
9+
s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTnomAPQJ32
10+
OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpjMfxHKePB
11+
Bzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRYZQ5ebcB9
12+
je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8wwx4yyVsM
13+
cgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIBdREcvziw
14+
TP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q/9NasVuk
15+
vsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1toAAGCoM1
16+
5O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAmjKOoKxLwPY4
17+
e65pYTUSBctPZ2juW5uNs8UvH5O32OC9RhENJBIIKn3B9Z/wkexR2zcvaQmJObLW
18+
6mkR7O0tNgsXVYJFzLRBfjM/nyP6nafiCUekmoh9Kojq6x5IQQgEsK+Uw123kkoI
19+
w/h3hBYBq8+CFPnYtBLZBVVFMNGaATXrYJPCcjVrtAHYxIWaDN2R+1DWLRIV72sF
20+
hu4xGz0kmUbzforl/FA3gdgM7mwfZMF4+EoQZi5mShdWnyfzAHIbtahnA4lPNtx9
21+
vBqYIZ/a2ITsXmWc2KGs/rRG+SDLzg+H1Xudvu/y2d1ULpZQfT6bg6Ro855FiU9h
22+
TyHHQGGqlC9/DjHy//wERsFEJZh5/j21LGyalEjgfOYtzPkjZlIweYr8LlHTrauo
23+
/gWihriaaWAkD+2fwQ09CUHdvOG6yoT+j/E50FsekfqV3tKMwoZoph6dF1TWQg32
24+
JXV0akpd5ff1cca8sZgJfUksDfSkrwG7fl3tje30vQTlvNrhu2MCKFGQwyXed3qg
25+
86lx+sTZjxMYvqWWysKTx8aIJ95XAK2jJ2OEVI2X6cdgoAp6aMkycbttik4hDoPJ
26+
eAWaZo2UFs2MGoUbX9m4RzPqPuBHNFqoV6yRyS1K/3KWyxVVvamZY0Qgzmoi4coB
27+
hRlTO6GDkF7u1YQ7eZi7pP7U8OcklfE=
28+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEyTCCArGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQHDB1Tb21l
3+
d2hlcmUKQ049ZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0zMTEy
4+
MTgxNDU3MzVaMCgxJjAkBgNVBAcMHVNvbWV3aGVyZQpDTj1ldmlsLmV4YW1wbGUu
5+
Y29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOl
6+
nAio4We7Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9
7+
XdJ7xb7/IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3
8+
sO8ZdRVnNY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+Mw
9+
EFq1YUH1s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTn
10+
omAPQJ32OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpj
11+
MfxHKePBBzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRY
12+
ZQ5ebcB9je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8w
13+
wx4yyVsMcgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIB
14+
dREcvziwTP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q
15+
/9NasVukvsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1t
16+
oAAGCoM15O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAEMEW
17+
EElTS/lgeoWvTruGEqmpwS86NE+j+Ws+VnUXnjo2RSqs4tSICkBzJsi4g/WHNa5V
18+
TzD42MOmyQTUGaJ96Cpq8VmL8pE0mYKo1wXsi8WonDgaw0Eup6v9ga5kHPfKJBvV
19+
dqEP+upiAbYXxlISj+xgOVW5WBJ3tBic1Iyg/oOKlHwXYA0IKc1MOLlvh0EdVqj7
20+
2cYodO7nuAmeFLpf5RDtGTNMWt/whoqv+vUb5iy2pDdDNMJdoa0hT/L4E+ibl0ZA
21+
7W/RKkcXJ0RlZMA7rYGjQ2/lasHvMniHlfLZd2UtChVgs8hY/b1PCLubyiz1peCj
22+
Q8Y4VoveePnxfovTPvcvMxPbNiCLPJtsPhWq1KPbOyBpKBc/mJ6I5DmszQB16Jb2
23+
fq6RfrrXjC1C+vYN4KCUGPbS+J4eZ0a04C4OdSGED02YSOpLIBnfNRMDyXZQ6Hhd
24+
sZSvyOAD3UhugEloCV9cnFKVglbXaW3k97xeYg/86udVPrgiAEn7u3Lsr9U1wZ2x
25+
wFgE4js1IzeIvIZOk9wDQHPolUiPaZUvMZXfM7+i9X9qX9AgtUAxnO0y0U9zXrUB
26+
Xjdtfddb4XAHdrPnuBkCb/75JeQ4JroP3t59iY0SFuQ0TH9YkOJULrw7oTqqmLo+
27+
PAFMiK1/kbmpVsT92k2WLjPgrAXe+lslQPwXBNM=
28+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEzzCCAregAwIBAgIBATANBgkqhkiG9w0BAQsFADArMSkwJwYDVQQHDCBTb21l
3+
d2hlcmUsIENOID0gZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0z
4+
MTEyMTgxNDU3MzVaMCsxKTAnBgNVBAcMIFNvbWV3aGVyZSwgQ04gPSBldmlsLmV4
5+
YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLv
6+
RPU/5KOlnAio4We7Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjI
7+
bc/oN2L9XdJ7xb7/IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB
8+
5dW49Tk3sO8ZdRVnNY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3po
9+
f3mGi+MwEFq1YUH1s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvk
10+
CV387DTnomAPQJ32OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVD
11+
SwyW6gpjMfxHKePBBzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9
12+
chA+DtRYZQ5ebcB9je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR
13+
1XrQ5a8wwx4yyVsMcgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuV
14+
r5OWutIBdREcvziwTP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXux
15+
kO/pnM+Q/9NasVukvsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4
16+
nJBqLK1toAAGCoM15O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOC
17+
AgEAFvcwnV5K6KH4jvYFUccZDEVZ2WFuZsqJVD5N4nX5KgHmnSzyDYgHRRZ4oGiN
18+
eTgi+3B6S5TPRTMLUaO7hnFxilnfr3HlhsQhGVh+Qb+ovyL1evsrCu8CzmmFMJs1
19+
bHm/ct/HzDfNgrx7HEZbrpesNjka05UWhIewA/64IkSMFoGbrjb35WINpcHQNgvQ
20+
X5YnUTk3U+DyDHGeRvZ9dsYBXnK7Q+s6lbS1Bvl3G65SZq9fxqtxLnwloP5ms62j
21+
r7OLdQ/IDYFu0v/HKkA9Ms/NJyKtoPUXYyiP0qQPq2A9lDRW07goCaR7WApmU4Sr
22+
uYQVAPCFbEJGQtjUVUrmEdlEuNaiaMM7+iB5WEXaQ8M8gRX+4U7lbk7HsRSsHlDn
23+
9/1sAOxrWAnCffoYSrUwruD8SKVCTBlkYs5pPSIkfz/yzwNq5u6ebe5ATJBjIv+H
24+
N4nflcrY18oMAz694f+94RUFat/5wX+WsnNT4Av+bVz6Gv5nbGJGXurUArrne5F9
25+
G+ESYu2KuGIRhxrOrBIvZapv9lITlBm9t8kChBbR9YZC4dD0+lu72h4xH3iXeeBl
26+
MFmP1mk8zxuIwH6H/bM70B5NAHEw4U5guthnRU5YSK5EpvXhNl/JqdSp8xskfYCM
27+
62dhRqgQNL0HZxKJO61bn3XBvVKLPNpCqBD5KQsI0R4wevM=
28+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEyTCCArGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQHDB1Tb21l
3+
d2hlcmUvQ049ZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0zMTEy
4+
MTgxNDU3MzVaMCgxJjAkBgNVBAcMHVNvbWV3aGVyZS9DTj1ldmlsLmV4YW1wbGUu
5+
Y29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOl
6+
nAio4We7Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9
7+
XdJ7xb7/IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3
8+
sO8ZdRVnNY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+Mw
9+
EFq1YUH1s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTn
10+
omAPQJ32OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpj
11+
MfxHKePBBzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRY
12+
ZQ5ebcB9je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8w
13+
wx4yyVsMcgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIB
14+
dREcvziwTP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q
15+
/9NasVukvsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1t
16+
oAAGCoM15O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAQD16
17+
wSsZodV3hk98VYDyXBuQdzrlF1zXm5n7Dx+ONGw62d3FRRaegbkwBfvUf7P+ZfR/
18+
qUFZQwWKYZ+hYos/gIvYuBRJSSg8nrGrHkp+AXIxQ6ZmgVAat3OnLdzG+k0Cras6
19+
vzRrEohL3JnXCBVZ+4MMnNrZFhGzQ9rHGJtrarkZ5NQMhH8VbfdtuKDpwS8O9mtI
20+
MqoNTIViocqtBem8ZD5z+m9A5UT8DMKwL+gjDQQ3j/flfmAq5bcqZkkIrJol3mrp
21+
4Ol1Hc4/tVMa1wsnEtYGWEOfBJqANY3m5IiEBHIyeP67NR68fdlZ+XFpdHNl5/LV
22+
XwjGquv0jSE3CbKR1ez5sefn1fmCWVZi5mZV6O8jpT7Ztu1XL8jOxTxtCMKE6cCC
23+
xgEL2HFG4JWeA/z5ZXT8U+4Bfiu1GXBMxF5LJc89DORTBRIWMR1IHca+nOb2zHNF
24+
v4QOfqLKF+ko5D/ie9Xg1s49l6lI8NReg9NRRp2sc90Zxc0Pqz7wdNH2SMUC/+gR
25+
kWhz77OhACeXpcRQVy0Bi64l5Or+05ZB2piK6OemcFUKIybKjxUbzuwZdrqj0vK6
26+
Tw1nemA1BCH8X+b1rz6kDKPycBAEdtMoRSFzbtZbdjBR1g0PLGeYn8rL2gsLMpaN
27+
1XTCTb7BAAy0Ky4cpMduD+uYGbma9V4ER3RLdL8=
28+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEyTCCArGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQHDB1Nw7xu
3+
Y2hlblwKQ049ZXZpbC5leGFtcGxlLmNvbTAeFw0yMTEyMjAxNDU3MzVaFw0zMTEy
4+
MTgxNDU3MzVaMCgxJjAkBgNVBAcMHU3DvG5jaGVuXApDTj1ldmlsLmV4YW1wbGUu
5+
Y29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOl
6+
nAio4We7Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9
7+
XdJ7xb7/IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3
8+
sO8ZdRVnNY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+Mw
9+
EFq1YUH1s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTn
10+
omAPQJ32OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpj
11+
MfxHKePBBzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRY
12+
ZQ5ebcB9je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8w
13+
wx4yyVsMcgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIB
14+
dREcvziwTP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q
15+
/9NasVukvsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1t
16+
oAAGCoM15O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAipRw
17+
3Q8C0CUYTQJlYTAdmATrboUFATpex+ZFhQgQPPWs/tUvf8zWU+DdDjFjrLNCY+ew
18+
FaURBnNQ92AE3LVDayu3Jh6TMoHKMAnPOERaiMuHDoKr/T4JVk2vWSBck6aYbokl
19+
7W7/ucMTVyPS9tLiuIwyJ+0dta+ucQSjIZj2RtCzsOtxdbUqt/7iTJrl8EjZGGbH
20+
FTKSbFBY2mR9oFKhoyCaVV0Alw1//napqdzu93gNqZx3cXskA0T63GxyhjhVpFq8
21+
d1ILGB3yKAiIzc5epNKx8ZPSUddx7zK0FAXRtBGHcOTES3+kTljkxmXAFDGTrMk0
22+
fsWgKfDDkDEGaUHL43524HLnPUoQASdQ9Uk5r7TDkl/kATv5w+HpWKdd3sxcSH8m
23+
UeUFCFdJbcOyqKfF7jz8kCe08Xt2sEW5tKZb4xWjI+mm01PCNeyCsaAw4OlSDUEm
24+
63fCsXY/b+i0hOxdd/eusoq3B76ngOEGaEJ8jOvpxeyHuet9kDet5M48aQRE9S9x
25+
HJWLL+80mFt4yiRHPUob/WP+4L7EnBjmiVBevEO0sptYLqymdRuCy4Ub4/QIQnNW
26+
kFasltzL/WEe1TzpTNziqOk1jEHA06D5Euwy/mI+S0Y0uvFOYC+tVkspsCNikrTu
27+
Fj0Lqyg5tqQJM3msSEfJvaJhUydaeIZp1Cr535Y=
28+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIE0zCCArugAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMSswEAYDVQQHDAlTb21l
3+
d2hlcmUwFwYDVQQDDBBldmlsLmV4YW1wbGUuY29tMB4XDTIxMTIyMDE0NTczNVoX
4+
DTMxMTIxODE0NTczNVowLTErMBAGA1UEBwwJU29tZXdoZXJlMBcGA1UEAwwQZXZp
5+
bC5leGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALER
6+
Z3TS70T1P+SjpZwIqOFnu2Od9XKWcDszQQc7C92K+APjp4BvWiayictqCJWtmsbs
7+
Sli4yG3P6Ddi/V3Se8W+/yB71Qh2c3wQNMPMukncps2odRGtqJe4EOpret1jgkFq
8+
QJy5geXVuPU5N7DvGXUVZzWN7TisFNAZMUhF2YlljJz1sjD1aRjqayh3TDU8NwuF
9+
lLd6aH95hovjMBBatWFB9bN/itnMVoj9rmfSjvlpSRO6/EMNDnVXZ3paRJTcps6d
10+
/Ylb5Ald/Ow056JgD0Cd9jn16+vgYR4bxVl0a5Qbf24MIhaYBRPx6WFdepWr410G
11+
pzpVQ0sMluoKYzH8RynjwQc7pfm5ebTFWJkhU80jOqKJGFW3icV/A9BmtOBqrb60
12+
Pv/MPXIQPg7UWGUOXm3AfY3v6gbToewSW5B0s+uPsh52md4ZUoyzTvwN2i/uPqJx
13+
i/9FkdV60OWvMMMeMslbDHIBbN0Z2SG0wY93oH2LhO0X89Tcnedukufld8QEW7iM
14+
n7D7la+TlrrSAXURHL84sEz97yyujawQEimnW03XAlUFk61MiLhVUOHANFBSLBvZ
15+
3VF7sZDv6ZzPkP/TWrFbpL7DQgqunAlSNHealrQ5T3wp8pUmR7J4QEuQSEN2cZMO
16+
pn0T+JyQaiytbaAABgqDNeTvbl2nFN2ksSix8NunAgMBAAEwDQYJKoZIhvcNAQEL
17+
BQADggIBAAdRC4tmZb5tukc4pIdnzRyrzNq3uefQNLcrZpZaCKAWvey+AFOZw88N
18+
nnjUT0A3bXA2YJPKQtRaSJG+UBH3xgRNOM0ttvKYqmzZDt/ygzxRlTMt80AVVyMG
19+
P06D5UUZHEX6aUchS/noDI5jewZy23jINEAzQv8B72r8WjV/LwjbJ1IoBg08gJhO
20+
QQCfeDaJ0sAQCL1tdlwiS6Q3N6rkC3jLzBHCzXP0FN5OF5rxr6nlfHiTOuhTdodR
21+
p/UrLVADdvpXq6SegbTvZ7/KwNWzzAmOEx2MAHFQKh46S1+RHQE3L7SV9dqV2XCe
22+
OxfBPPXTy+AiceKhVL0+jhdI/VWIdhTHSCeFuzrGbrLQwWLCDZ5AZjS/JaBXuVGl
23+
WILzz3ZG6ekdqMY/qG8weDEFv49f03MGWoX27uhkz4qtumLzrXEspzL7GwUfnDZo
24+
zyF9Jo9vJVNmiz/N2DnUd0X5hdHUsjnN8vPN+3u5kkvfXTgT9wUrMgzECu/tyC92
25+
GAX0MqY6lKJwTT+pxkZPUNGMbP8c3BuO9NVGPUeOA+/4sgsws+V0TDF7umNk2nq3
26+
vCuS+QFZXAR4Ns2xgIOMH8XQjRZ4qSp3HsFNehOqSQrFvcgjMLo0RcgiwgReUMl+
27+
Pnhjk+V4ttEIUe3UswaRHD9moG4sgCfFk/bafwCvdKonD6mBETMa
28+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEzTCCArWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAqMSgwJgYDVQQHDB9Tb21l
3+
d2hlcmUgKyBDTj1ldmlsLmV4YW1wbGUuY29tMB4XDTIxMTIyMDE0NTczNVoXDTMx
4+
MTIxODE0NTczNVowKjEoMCYGA1UEBwwfU29tZXdoZXJlICsgQ049ZXZpbC5leGFt
5+
cGxlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALERZ3TS70T1
6+
P+SjpZwIqOFnu2Od9XKWcDszQQc7C92K+APjp4BvWiayictqCJWtmsbsSli4yG3P
7+
6Ddi/V3Se8W+/yB71Qh2c3wQNMPMukncps2odRGtqJe4EOpret1jgkFqQJy5geXV
8+
uPU5N7DvGXUVZzWN7TisFNAZMUhF2YlljJz1sjD1aRjqayh3TDU8NwuFlLd6aH95
9+
hovjMBBatWFB9bN/itnMVoj9rmfSjvlpSRO6/EMNDnVXZ3paRJTcps6d/Ylb5Ald
10+
/Ow056JgD0Cd9jn16+vgYR4bxVl0a5Qbf24MIhaYBRPx6WFdepWr410GpzpVQ0sM
11+
luoKYzH8RynjwQc7pfm5ebTFWJkhU80jOqKJGFW3icV/A9BmtOBqrb60Pv/MPXIQ
12+
Pg7UWGUOXm3AfY3v6gbToewSW5B0s+uPsh52md4ZUoyzTvwN2i/uPqJxi/9FkdV6
13+
0OWvMMMeMslbDHIBbN0Z2SG0wY93oH2LhO0X89Tcnedukufld8QEW7iMn7D7la+T
14+
lrrSAXURHL84sEz97yyujawQEimnW03XAlUFk61MiLhVUOHANFBSLBvZ3VF7sZDv
15+
6ZzPkP/TWrFbpL7DQgqunAlSNHealrQ5T3wp8pUmR7J4QEuQSEN2cZMOpn0T+JyQ
16+
aiytbaAABgqDNeTvbl2nFN2ksSix8NunAgMBAAEwDQYJKoZIhvcNAQELBQADggIB
17+
AAG8vjV7c4B4yKO2BDhufVjkmzot97SPf4qR0qJATAV+Iifm5D2YL/dr36kyvTiK
18+
JoPU/0vztcnh5X75YzvEtD4xh5zg3FQdAEpGx4zZkNXkJt2syz3V3DFG9Te4GH3n
19+
/a39z4yn2J2MG2uXj+TTSJR23ICAgqNkj4EtrwvOouAqLCR/yZuYaUM6ZPmEYrHM
20+
5wwiMCheDgMUYvFhTIKAwalnQitCGQCFr5WvTHU/0oVn498miZEU5LPAIiuhIQoA
21+
UI/tro47evU/Nli8WY9UImLbcWkbIS7MogtWhjDQXd80G3sX+9DpVO43S2Cf4shB
22+
yXl49bvqITMXdurSQrNKbfQ5aLDmKno4Qjs9wZMmi2xhIKczuB4bdtQDsC0/LiSr
23+
oydiSP9uxYatT6SedzgkypTOL/5qtuh14Z7aRio5s4WrIDDJ1RVlWJGffq4hF+j/
24+
cu5OHo4cyvN42+bnyYzAWpOE7h8Nmi0D14zvm1FE3FKVSlBZzScBBungVdJkchAP
25+
4JleXVqfH5skLgMiYCa3qocfUEfeKTCVXJUxaPIvBILtcOYzx75B0izsVlsd/dr+
26+
DqoIKN9aMGyuKR0QZtmW97eCxaH6Dm7lVuym56hiQrT3J0PL2iU+LU1R9UfLE/pL
27+
RjUWW/gbxxNq8dbFybiUM7Sj+6tWuVvLygA04lMeDIDq
28+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADAlMRYwCQYDVQQHDAJMMTAJ
3+
BgNVBAcMAkwyMQswCQYDVQQHDAJMMzAeFw0yMTEyMjAxNDU3MzVaFw0zMTEyMTgx
4+
NDU3MzVaMCUxFjAJBgNVBAcMAkwxMAkGA1UEBwwCTDIxCzAJBgNVBAcMAkwzMIIC
5+
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsRFndNLvRPU/5KOlnAio4We7
6+
Y531cpZwOzNBBzsL3Yr4A+OngG9aJrKJy2oIla2axuxKWLjIbc/oN2L9XdJ7xb7/
7+
IHvVCHZzfBA0w8y6Sdymzah1Ea2ol7gQ6mt63WOCQWpAnLmB5dW49Tk3sO8ZdRVn
8+
NY3tOKwU0BkxSEXZiWWMnPWyMPVpGOprKHdMNTw3C4WUt3pof3mGi+MwEFq1YUH1
9+
s3+K2cxWiP2uZ9KO+WlJE7r8Qw0OdVdnelpElNymzp39iVvkCV387DTnomAPQJ32
10+
OfXr6+BhHhvFWXRrlBt/bgwiFpgFE/HpYV16lavjXQanOlVDSwyW6gpjMfxHKePB
11+
Bzul+bl5tMVYmSFTzSM6ookYVbeJxX8D0Ga04GqtvrQ+/8w9chA+DtRYZQ5ebcB9
12+
je/qBtOh7BJbkHSz64+yHnaZ3hlSjLNO/A3aL+4+onGL/0WR1XrQ5a8wwx4yyVsM
13+
cgFs3RnZIbTBj3egfYuE7Rfz1Nyd526S5+V3xARbuIyfsPuVr5OWutIBdREcvziw
14+
TP3vLK6NrBASKadbTdcCVQWTrUyIuFVQ4cA0UFIsG9ndUXuxkO/pnM+Q/9NasVuk
15+
vsNCCq6cCVI0d5qWtDlPfCnylSZHsnhAS5BIQ3Zxkw6mfRP4nJBqLK1toAAGCoM1
16+
5O9uXacU3aSxKLHw26cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAGNhY0vKd8Os9
17+
75+HHQH03BugatuIykpSu+tj8OYr2/7VLT76qUaKdkAZV0m9TiS8MitHZieEbig3
18+
EozQtYrTZQbiFjiV8FudPsmAXZxcz1TdE25mZykWe24FmZNdeMQmoVRZYbg3gb/M
19+
sTEDbnV3DoW6X8LWMlitaBpisxg/LqHakATvj6Otvts8RFhI1c/JFx8THuY14Fj1
20+
sJ8eFdwebPK35V4ZNtH8bevVo9MvnUS290fF1WDC1dnjZ1zYqHT7sPoGbCFF4kne
21+
TF2Ef12BgUNtgJKnXeEV5Gull4iOQS8qTkWCIm8jbz1+9ap8nqVcGn60bkwiMmgz
22+
hNyBW7c31MvEfedfCwFma/uV1yMB2nGwX47TMnTTjwc5b2I/lOrFOfeh2JD9QVZF
23+
XFKRsVXqCwa3aLc1fc93M9kEHzKWzGgMjYvJzZEGsoqTil22NmQXIG7jKjLth7zF
24+
4Sc/qBDXsLaqUaWQveZ9U6suFYr9u2X7h3KkciFtsZPFK+AZGO07z/4nWEeo4frV
25+
RyltN38BmJxwBSxNEZFBiMJ9AEmg2EhgBXJbEhN9XCwpW2EEp+M09AfcebzKjJ+h
26+
3Q7AWlTPawz/PQzzunZzNMkq7/6Y/dIFg/Ak8RIPkMVb3xE9oD0wMWigyiK05UUI
27+
832NnZXih3qq15MfVS4eTSeKrNcFt3c=
28+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIExzCCAq+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAnMQswCQYDVQQHDAJMMTEL
3+
MAkGA1UEBwwCTDIxCzAJBgNVBAcMAkwzMB4XDTIxMTIyMDE0NTczNVoXDTMxMTIx
4+
ODE0NTczNVowJzELMAkGA1UEBwwCTDExCzAJBgNVBAcMAkwyMQswCQYDVQQHDAJM
5+
MzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALERZ3TS70T1P+SjpZwI
6+
qOFnu2Od9XKWcDszQQc7C92K+APjp4BvWiayictqCJWtmsbsSli4yG3P6Ddi/V3S
7+
e8W+/yB71Qh2c3wQNMPMukncps2odRGtqJe4EOpret1jgkFqQJy5geXVuPU5N7Dv
8+
GXUVZzWN7TisFNAZMUhF2YlljJz1sjD1aRjqayh3TDU8NwuFlLd6aH95hovjMBBa
9+
tWFB9bN/itnMVoj9rmfSjvlpSRO6/EMNDnVXZ3paRJTcps6d/Ylb5Ald/Ow056Jg
10+
D0Cd9jn16+vgYR4bxVl0a5Qbf24MIhaYBRPx6WFdepWr410GpzpVQ0sMluoKYzH8
11+
RynjwQc7pfm5ebTFWJkhU80jOqKJGFW3icV/A9BmtOBqrb60Pv/MPXIQPg7UWGUO
12+
Xm3AfY3v6gbToewSW5B0s+uPsh52md4ZUoyzTvwN2i/uPqJxi/9FkdV60OWvMMMe
13+
MslbDHIBbN0Z2SG0wY93oH2LhO0X89Tcnedukufld8QEW7iMn7D7la+TlrrSAXUR
14+
HL84sEz97yyujawQEimnW03XAlUFk61MiLhVUOHANFBSLBvZ3VF7sZDv6ZzPkP/T
15+
WrFbpL7DQgqunAlSNHealrQ5T3wp8pUmR7J4QEuQSEN2cZMOpn0T+JyQaiytbaAA
16+
BgqDNeTvbl2nFN2ksSix8NunAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAEeFRIyV
17+
5PdD7Xipg3byNhcCH6I8gADM+Ipnxic93COfQrWCKd/lnsJzxml7VhyANScUTx44
18+
wkYs+kW9Xi/tEViVwrsFzlTB3YwaAYPiGNtr98B4JBUfLneHSh8IUeeMUnBeLt4O
19+
eqo3ts38hCfY3B3E2FtV9nRBKu91ZwE+pInWftdTJ6pIkltr+t9kPbVFW72hYfQJ
20+
rdtyzIiSkTnJElcvNcWtsqEmTMLewgZz/bjbZkQh/LXQDT7oepZBZ5Qb4F8kwytb
21+
wGC/OFoByWyXYfuPWKb2obdnbb5xa1vg8rLVdVgY25q+VeNItBB/FSzf0Pnxd9od
22+
jVVtzvby57A0IT7XpTu8RFAkuWmZp4FO5kDyXLNgsd6md/qeqcO5V7dY6MSKeIXw
23+
nMYTBWuxOZPMw2RnxjcfkEdN/5sDuYHnzuizkH+OiwPPfs2qa4EETaxo5xxmTcy+
24+
pDh0GEOIgyazpJnncgG1k1ABOcHevRaCpm8NuXexkfpAHEORNfOflRkJDICXSUxv
25+
5o2VjOhqj8gRqLvpGBW3hCxVM/Of2Fzdye0ldoDhzcW0WxjzmcjcC5EEEVSapwok
26+
K5+ZvVFjqW2j619UICFf95tCtB025AzWWwVVQ9rlnCWL0MOrOwe66vYERG2MUYAD
27+
jcB7FUOjXh2+3Gkh1PzXiXCQatDLhIVt9Vus
28+
-----END CERTIFICATE-----

‎test/parallel/test-x509-escaping.js

+113
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,119 @@ const { hasOpenSSL3 } = common;
253253
}
254254
}
255255

256+
// Test escaping rules for the subject field.
257+
{
258+
const expectedSubjects = [
259+
{
260+
text: 'L=Somewhere\nCN=evil.example.com',
261+
legacy: {
262+
L: 'Somewhere',
263+
CN: 'evil.example.com',
264+
},
265+
},
266+
{
267+
text: 'L=Somewhere\\00evil.example.com',
268+
legacy: {
269+
L: 'Somewhere\0evil.example.com',
270+
},
271+
},
272+
{
273+
text: 'L=Somewhere\\0ACN=evil.example.com',
274+
legacy: {
275+
L: 'Somewhere\nCN=evil.example.com'
276+
},
277+
},
278+
{
279+
text: 'L=Somewhere\\, CN = evil.example.com',
280+
legacy: {
281+
L: 'Somewhere, CN = evil.example.com'
282+
},
283+
},
284+
{
285+
text: 'L=Somewhere/CN=evil.example.com',
286+
legacy: {
287+
L: 'Somewhere/CN=evil.example.com'
288+
},
289+
},
290+
{
291+
text: 'L=München\\\\\\0ACN=evil.example.com',
292+
legacy: {
293+
L: 'München\\\nCN=evil.example.com'
294+
}
295+
},
296+
{
297+
text: 'L=Somewhere + CN=evil.example.com',
298+
legacy: {
299+
L: 'Somewhere',
300+
CN: 'evil.example.com',
301+
}
302+
},
303+
{
304+
text: 'L=Somewhere \\+ CN=evil.example.com',
305+
legacy: {
306+
L: 'Somewhere + CN=evil.example.com'
307+
}
308+
},
309+
// Observe that the legacy representation cannot properly distinguish
310+
// between multi-value RDNs and multiple single-value RDNs.
311+
{
312+
text: 'L=L1 + L=L2\nL=L3',
313+
legacy: {
314+
L: ['L1', 'L2', 'L3']
315+
},
316+
},
317+
{
318+
text: 'L=L1\nL=L2\nL=L3',
319+
legacy: {
320+
L: ['L1', 'L2', 'L3']
321+
},
322+
},
323+
];
324+
325+
const serverKey = fixtures.readSync('x509-escaping/server-key.pem', 'utf8');
326+
327+
for (let i = 0; i < expectedSubjects.length; i++) {
328+
const pem = fixtures.readSync(`x509-escaping/subj-${i}-cert.pem`, 'utf8');
329+
const expected = expectedSubjects[i];
330+
331+
// X509Certificate interface is not supported in v12.x & v14.x. Disable
332+
// checks for certX509.subject and certX509.issuer with expected
333+
// text. The testcase is ported from v17.x
334+
//
335+
// Test the subject property of the X509Certificate API.
336+
// const cert = new X509Certificate(pem);
337+
// assert.strictEqual(cert.subject, expected.text);
338+
// The issuer MUST be the same as the subject since the cert is self-signed.
339+
// assert.strictEqual(cert.issuer, expected.text);
340+
341+
// Test that the certificate obtained by checkServerIdentity has the correct
342+
// subject property.
343+
const server = tls.createServer({
344+
key: serverKey,
345+
cert: pem,
346+
}, common.mustCall((conn) => {
347+
conn.destroy();
348+
server.close();
349+
})).listen(common.mustCall(() => {
350+
const { port } = server.address();
351+
tls.connect(port, {
352+
ca: pem,
353+
servername: 'example.com',
354+
checkServerIdentity: (hostname, peerCert) => {
355+
assert.strictEqual(hostname, 'example.com');
356+
const expectedObject = Object.assign(Object.create(null),
357+
expected.legacy);
358+
assert.deepStrictEqual(peerCert.subject, expectedObject);
359+
// The issuer MUST be the same as the subject since the cert is
360+
// self-signed. Otherwise, OpenSSL would have already rejected the
361+
// certificate while connecting to the TLS server.
362+
assert.deepStrictEqual(peerCert.issuer, expectedObject);
363+
},
364+
}, common.mustCall());
365+
}));
366+
}
367+
}
368+
256369
// The internal parsing logic must match the JSON specification exactly.
257370
{
258371
// This list is partially based on V8's own JSON tests.

0 commit comments

Comments
 (0)
Please sign in to comment.