Skip to content

Commit 1fe7f74

Browse files
committed
crypto: improve system certificate enumeration logic on macOS
1) Fixed macOS default for missing kSecTrustSettingsResult When kSecTrustSettingsResult is absent from a trust settings dictionary, Apple specifies kSecTrustSettingsResultTrustRoot as the default value. Previously, the trust result evaluation (deny check, self-issued check, TrustAsRoot check) was inside the block that only executed when kSecTrustSettingsResult was explicitly present. When the key was absent, the function fell through to return UNSPECIFIED, incorrectly rejecting self-signed certificates that should have been trusted via the default. Move the trust result evaluation outside the conditional block so the default value of kSecTrustSettingsResultTrustRoot flows through the same code path as explicit values. This aligns with Chromium's trust_store_mac.cc implementation. 2) Fix CFRelease leak in IsTrustDictionaryTrustedForPolicy: the CFDictionaryRef returned by SecPolicyCopyProperties(policy_ref) was not released when the policy OID matched kSecPolicyAppleSSL. 3) Deduplicate certificates: SecItemCopyMatching can return the same certificate from multiple keychains. 4) Filter expired certificates.
1 parent 2f8063e commit 1fe7f74

File tree

7 files changed

+310
-28
lines changed

7 files changed

+310
-28
lines changed

src/crypto/crypto_context.cc

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,10 @@ TrustStatus IsTrustDictionaryTrustedForPolicy(CFDictionaryRef trust_dict,
369369
CFStringRef policy_oid = reinterpret_cast<CFStringRef>(
370370
const_cast<void*>(CFDictionaryGetValue(policy_dict, kSecPolicyOid)));
371371

372-
if (!CFEqual(policy_oid, kSecPolicyAppleSSL)) {
372+
bool matches_ssl = CFEqual(policy_oid, kSecPolicyAppleSSL);
373+
CFRelease(policy_dict);
374+
375+
if (!matches_ssl) {
373376
return TrustStatus::UNSPECIFIED;
374377
}
375378
}
@@ -386,35 +389,44 @@ TrustStatus IsTrustDictionaryTrustedForPolicy(CFDictionaryRef trust_dict,
386389
&trust_settings_result)) {
387390
return TrustStatus::UNSPECIFIED;
388391
}
392+
}
389393

390-
if (trust_settings_result == kSecTrustSettingsResultDeny) {
391-
return TrustStatus::DISTRUSTED;
392-
}
393-
394-
// This is a bit of a hack: if the cert is self-issued allow either
395-
// kSecTrustSettingsResultTrustRoot or kSecTrustSettingsResultTrustAsRoot on
396-
// the basis that SecTrustSetTrustSettings should not allow creating an
397-
// invalid trust record in the first place. (The spec is that
398-
// kSecTrustSettingsResultTrustRoot can only be applied to root(self-signed)
399-
// certs and kSecTrustSettingsResultTrustAsRoot is used for other certs.)
400-
// This hack avoids having to check the signature on the cert which is slow
401-
// if using the platform APIs, and may require supporting MD5 signature
402-
// algorithms on some older OSX versions or locally added roots, which is
403-
// undesirable in the built-in signature verifier.
404-
if (is_self_issued) {
405-
return trust_settings_result == kSecTrustSettingsResultTrustRoot ||
406-
trust_settings_result == kSecTrustSettingsResultTrustAsRoot
407-
? TrustStatus::TRUSTED
408-
: TrustStatus::UNSPECIFIED;
409-
}
410-
411-
// kSecTrustSettingsResultTrustAsRoot can only be applied to non-root certs.
412-
return (trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
394+
// When kSecTrustSettingsResult is absent from the trust dict,
395+
// Apple docs specify kSecTrustSettingsResultTrustRoot as the default.
396+
// Refs
397+
// https://github.com/apple-oss-distributions/Security/blob/db15acbe6a7f257a859ad9a3bb86097bfe0679d9/trust/headers/SecTrustSettings.h#L119-L122
398+
// This is also enforced at write time for self-signed certs get TrustRoot,
399+
// and non-self-signed certs cannot have an empty settings,
400+
// Refs
401+
// https://github.com/apple-oss-distributions/Security/blob/db15acbe6a7f257a859ad9a3bb86097bfe0679d9/OSX/sec/Security/SecTrustStore.c#L196-L207
402+
403+
if (trust_settings_result == kSecTrustSettingsResultDeny) {
404+
return TrustStatus::DISTRUSTED;
405+
}
406+
407+
// From
408+
// https://source.chromium.org/chromium/chromium/src/+/main:net/cert/internal/trust_store_mac.cc;l=144-146
409+
// This is a bit of a hack: if the cert is self-issued allow either
410+
// kSecTrustSettingsResultTrustRoot or kSecTrustSettingsResultTrustAsRoot on
411+
// the basis that SecTrustSetTrustSettings should not allow creating an
412+
// invalid trust record in the first place. (The spec is that
413+
// kSecTrustSettingsResultTrustRoot can only be applied to root(self-signed)
414+
// certs and kSecTrustSettingsResultTrustAsRoot is used for other certs.)
415+
// This hack avoids having to check the signature on the cert which is slow
416+
// if using the platform APIs, and may require supporting MD5 signature
417+
// algorithms on some older OSX versions or locally added roots, which is
418+
// undesirable in the built-in signature verifier.
419+
if (is_self_issued) {
420+
return (trust_settings_result == kSecTrustSettingsResultTrustRoot ||
421+
trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
413422
? TrustStatus::TRUSTED
414423
: TrustStatus::UNSPECIFIED;
415424
}
416425

417-
return TrustStatus::UNSPECIFIED;
426+
// kSecTrustSettingsResultTrustAsRoot can only be applied to non-root certs.
427+
return (trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
428+
? TrustStatus::TRUSTED
429+
: TrustStatus::UNSPECIFIED;
418430
}
419431

420432
TrustStatus IsTrustSettingsTrustedForPolicy(CFArrayRef trust_settings,
@@ -447,6 +459,17 @@ bool IsCertificateTrustValid(SecCertificateRef ref) {
447459
CFArrayCreateMutable(nullptr, 1, &kCFTypeArrayCallBacks);
448460
CFArraySetValueAtIndex(subj_certs, 0, ref);
449461

462+
// SecTrustEvaluateWithError is used to check whether an individual
463+
// certificate is trusted by the system — not to validate it for a
464+
// specific role (server, intermediate, etc.). We just need a minimal
465+
// policy that guarantees the certificate can be chained to a known
466+
// trust anchor while filtering out irrelevant certificates.
467+
//
468+
// Refs
469+
// https://github.com/apple-oss-distributions/Security/blob/db15acbe6a7f257a859ad9a3bb86097bfe0679d9/OSX/sec/Security/SecPolicy.c#L1855-L1890
470+
// SecPolicyCreateSSL (both mark EKU optional):
471+
// server=true -> BasicX509 + serverAuth + anyExtendedKeyUsage + SGC
472+
// server=false -> BasicX509 + clientAuth + anyExtendedKeyUsage
450473
SecPolicyRef policy = SecPolicyCreateSSL(false, nullptr);
451474
OSStatus ortn =
452475
SecTrustCreateWithCertificates(subj_certs, policy, &sec_trust);
@@ -516,6 +539,21 @@ bool IsCertificateTrustedForPolicy(X509* cert, SecCertificateRef ref) {
516539
return false;
517540
}
518541

542+
// Checks if a certificate has expired.
543+
// Returns true if the certificate's notAfter date is in the past.
544+
static bool IsCertificateExpired(X509* cert) {
545+
// X509_cmp_current_time returns:
546+
// -1 if the time is in the past (expired)
547+
// 0 if there was an error
548+
// 1 if the time is in the future (not yet expired)
549+
ASN1_TIME* not_after = X509_get_notAfter(cert);
550+
if (not_after == nullptr) {
551+
return false;
552+
}
553+
int cmp = X509_cmp_current_time(not_after);
554+
return cmp < 0;
555+
}
556+
519557
void ReadMacOSKeychainCertificates(
520558
std::vector<X509*>* system_root_certificates_X509) {
521559
CFTypeRef search_keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
@@ -543,6 +581,10 @@ void ReadMacOSKeychainCertificates(
543581

544582
CFIndex count = CFArrayGetCount(curr_anchors);
545583

584+
// Track seen certificates to detect duplicates (same cert in multiple
585+
// keychains).
586+
std::set<X509*, X509Less> seen_certs;
587+
546588
for (int i = 0; i < count; ++i) {
547589
SecCertificateRef cert_ref = reinterpret_cast<SecCertificateRef>(
548590
const_cast<void*>(CFArrayGetValueAtIndex(curr_anchors, i)));
@@ -568,11 +610,28 @@ void ReadMacOSKeychainCertificates(
568610
}
569611

570612
bool is_valid = IsCertificateTrustedForPolicy(cert, cert_ref);
571-
if (is_valid) {
572-
system_root_certificates_X509->emplace_back(cert);
573-
} else {
613+
if (!is_valid) {
614+
X509_free(cert);
615+
continue;
616+
}
617+
618+
// Skip duplicate certificates.
619+
auto [it, inserted] = seen_certs.insert(cert);
620+
if (!inserted) {
621+
X509_free(cert);
622+
continue;
623+
}
624+
625+
// Skip expired certificates.
626+
if (IsCertificateExpired(cert)) {
627+
per_process::Debug(DebugCategory::CRYPTO,
628+
"Skipping expired system certificate\n");
629+
seen_certs.erase(it);
574630
X509_free(cert);
631+
continue;
575632
}
633+
634+
system_root_certificates_X509->emplace_back(cert);
576635
}
577636
CFRelease(curr_anchors);
578637
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDSzCCAjOgAwIBAgIUJmC4/h9L3c8Arrxyo/+O73Owt9AwDQYJKoZIhvcNAQEL
3+
BQAwNTEhMB8GA1UEAwwYTm9kZUpTLVRlc3QtRXhwaXJlZC1Sb290MRAwDgYDVQQK
4+
DAdOb2RlLmpzMB4XDTIwMDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowNTEhMB8G
5+
A1UEAwwYTm9kZUpTLVRlc3QtRXhwaXJlZC1Sb290MRAwDgYDVQQKDAdOb2RlLmpz
6+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArkeWKdWP/YG89KiMCTYe
7+
9Q3DSMwzKeYrjtK5FeVxSHqo5za01VYCYsHpmTZFTM5P6nU6c889vZ0+C5lS1QAa
8+
JLgkUYpMMTk7ycAO0VKYugB9vHI3mQUH3Fkk7cilq5r1KyR1RqaGx7xhe49iFyXo
9+
zIE7C9fAQ4/DhqkuZlCnFg9PQLzKikgRdUUZsR/kvEAK30YyCcVs5po5oTGZB1VB
10+
gRoNYLPqSBCUtgfHEw2EIqRNQtgVwEuJVEclwbijkgIm4NFDCFKixbVE8KQFAuxr
11+
SFO8msDonubXJhCXZkNeh+diBMl7lglZEQU8o6ax0lqrV8mCPselXc0ipR/eD6aG
12+
XQIDAQABo1MwUTAdBgNVHQ4EFgQUN+lQVSG+5zPWNm/XiMcDMk9GVnowHwYDVR0j
13+
BBgwFoAUN+lQVSG+5zPWNm/XiMcDMk9GVnowDwYDVR0TAQH/BAUwAwEB/zANBgkq
14+
hkiG9w0BAQsFAAOCAQEASj1j/atg8qr3aZEsukmFaBf7eu4c0O0WwogTwCaSAEtn
15+
6et9G9rbg++X3JDHBv2PKiYdhSKTz2Kdsaau0fw0fmDkrFB5fwvzi6JU+LZ0Q8Ur
16+
jPlcks8sIdByX9mAbmf0Hur1qkPBoOm4BQNHrhEA+ExD8jbTAUukAOwH0mZV9ZzC
17+
B/7qVXwV6JmvdLXKDeinScnu1AIcYJVPUFEO2ZSkl+4XsdFogN+t4ryisosgFCtL
18+
AWkxA2/InBPBncDOUiceagJQdkGiyhsySxj/niLe8XUUo0p6/bYgqV7loJV6qkDA
19+
6KPH1RyigZMJ0SxNvj5oW1wcS3N2i/uX5csq9/w2nA==
20+
-----END CERTIFICATE-----
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCuR5Yp1Y/9gbz0
3+
qIwJNh71DcNIzDMp5iuO0rkV5XFIeqjnNrTVVgJiwemZNkVMzk/qdTpzzz29nT4L
4+
mVLVABokuCRRikwxOTvJwA7RUpi6AH28cjeZBQfcWSTtyKWrmvUrJHVGpobHvGF7
5+
j2IXJejMgTsL18BDj8OGqS5mUKcWD09AvMqKSBF1RRmxH+S8QArfRjIJxWzmmjmh
6+
MZkHVUGBGg1gs+pIEJS2B8cTDYQipE1C2BXAS4lURyXBuKOSAibg0UMIUqLFtUTw
7+
pAUC7GtIU7yawOie5tcmEJdmQ16H52IEyXuWCVkRBTyjprHSWqtXyYI+x6VdzSKl
8+
H94PpoZdAgMBAAECggEAAoFI6UUGktBAlQuvJ5q9iywteGhm+90xFxZ0TppDrJUG
9+
xHwG0WIxGpZK80bSbC4y+92/f1alPop6D9SeWi1sMsbqzrk8KyD1eQrnq56ST2oe
10+
ZI0Hu41U9ZfabgiKSRMrHvmhLejK2ygcBpijAk4rMHVTEfKB8vaoCtF3t0TFgg2k
11+
MkliPU9hCkrr17aYZSw8R2HiTM8hqixm7GrTihZzGFUa+N4NHZEbVkLQpCnifBly
12+
1ZyNL03FDSBClYRGvp2W6qO8uUi3cQuXyfXhoeHZx5AW+pVsmmZsxhNnyeJwCDcU
13+
+WgxU9uNeHWEYwVg3fTP3LLgRhnVBr3UuYI4GtWBiQKBgQDhRC9cTlOG9SrgHPZT
14+
reZNDQ9hhrcb1kbo1yJBIHFTihv0UIS6S36PmN+OgshbF5+G8Y8fvJJ8vuzMpDfC
15+
GNR53F6DHH/bngv3lgws/6F0X41TrzOA6+MkA4D6s5uGscSODCGuxkwjuZuKQzwo
16+
zAhx8t6RX/w1k4uXFPfYgX5vBwKBgQDGDppVCyRGpijdAdXVmmiQz/7q9M6YdFDX
17+
53pt8CIdCfYiT3+iOwPqHAddQdwVewC3WrEZ7S3PdxpKgDDpYkJ0+NtHhlYdGLR9
18+
Ti6Cvmfc+r8yYnmEAqIXAeac5A4MYYTh+FadgtzqVPhp8Q61xkXFwajo/tV1EcSt
19+
/A+rC0XiewKBgQCqniBZA6JUJ8FvucAApUg3t9qcfZKW7PcMSFXTiiULpyGBLLM6
20+
/w8+6AT7RadHB192r+M9oHA7N8jXPtJUmsXj/rs/Bwj4aH6b6fQS6RN6txyt85dI
21+
4GFL17OLLxpvLJm5FQs1+0+UB3L9h+s64z7KP6+/4DmAwt4JcoI+Y+ZFZQKBgHbE
22+
YwAEgmhrU63UX+qLgZD1aaRz0T/S4HfYM66hhZNsWdERYzRht2M4E6J00AmBjVhm
23+
ZjVp6UKz5WwvmyUY60lBwh0ODa29Ft7ddz6n95ioNOd97eifu5uYZDZI+7Oo9wqa
24+
5TXnN5q+AYlmKLAQid6g1y2BQ3fEg/DhanPjerDpAoGBAM7Sm4E4UMOCzHFCHehh
25+
bTzfz8oNGpkWI+5UctXZOkzQ3YVtTwB/+b5nuG7D5EI8mRGHmhXerkM2uAmShmWl
26+
a9Xyo9nswhDjanBcji+ZWIXFqOodclK4/3VE+VnbzlZMqpGnxUl88wWeBXEualZ0
27+
Z3lq9DNf/Fywlcl69thCNXQs
28+
-----END PRIVATE KEY-----
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDUTCCAjmgAwIBAgIUapTwHjAOdsC1qQUcKRKprxW7GvswDQYJKoZIhvcNAQEL
3+
BQAwNzEjMCEGA1UEAwwaTm9kZUpTLVRlc3QtTm8tUmVzdWx0LVJvb3QxEDAOBgNV
4+
BAoMB05vZGUuanMwIBcNMjYwNDA0MTgzMjUyWhgPMjEyNjAzMTExODMyNTJaMDcx
5+
IzAhBgNVBAMMGk5vZGVKUy1UZXN0LU5vLVJlc3VsdC1Sb290MRAwDgYDVQQKDAdO
6+
b2RlLmpzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomij2nGU2Lg0
7+
sD9OitlQlIAC6QEm3ziS8c3yX+wIhV/sZ/mRFt1cvFfIX7LFYl6zzDMI0Wgho422
8+
H9T4I5RRZ2Ms6IZ1e6+ERlqWDm3pS2nFglOR3AA1QUDt0pSfrtQjIc2yxeTMP7EO
9+
/LRZuGp1VOrR2kDP9JZC0+WLBBvtz/yFUICdrfDnm2d9b632Gl3CU/GiotMvt+oZ
10+
8J3WEh4y0vVdIwzMLV/jhoIgi0e9sZzSkB4ZmHCBOwl4asj8Yj8PS4q4AvXH2jms
11+
HmrsxUezsRrw9seccisexcC+akrJ4s1cqGXmi6MxqP3FO6yhSBdge6fMSNAETZ70
12+
qKL/Kk1t6QIDAQABo1MwUTAdBgNVHQ4EFgQULJNqn+YwH/flAsr8460+8ej1XQkw
13+
HwYDVR0jBBgwFoAULJNqn+YwH/flAsr8460+8ej1XQkwDwYDVR0TAQH/BAUwAwEB
14+
/zANBgkqhkiG9w0BAQsFAAOCAQEAMl8+Y1xRagBBKOluPcvTyV5BTleXLtF95wsj
15+
6SCTsSzmJiYQtBZA/pSoTS8+gEQb9hjN6dbYRDVY2pGyLrEVI/YE4zr4+Ug8vBXw
16+
UnZx4a76bUiT8iC7rBsqSeui7R56lbPQlxYjEKyX3oZgW9WzZ9NT3z9/u3mfTrW/
17+
TculuifSQHAi1X5r9IXFFABMyD8gDtQSfG+0e1E9KLyCMO3p8H/snz1hXIAUFnBD
18+
2b5QYiQRxUN2aO4PqJakxhDN844Vv6O+vQX618Fn+MqaL6qyPzJRo9qJe70/5xIu
19+
4Xs3ajp8Y3c5bb9vGtgLWsb3eUJ+AcZ2kttaBaIgiKqk4hTrDA==
20+
-----END CERTIFICATE-----
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCiaKPacZTYuDSw
3+
P06K2VCUgALpASbfOJLxzfJf7AiFX+xn+ZEW3Vy8V8hfssViXrPMMwjRaCGjjbYf
4+
1PgjlFFnYyzohnV7r4RGWpYObelLacWCU5HcADVBQO3SlJ+u1CMhzbLF5Mw/sQ78
5+
tFm4anVU6tHaQM/0lkLT5YsEG+3P/IVQgJ2t8OebZ31vrfYaXcJT8aKi0y+36hnw
6+
ndYSHjLS9V0jDMwtX+OGgiCLR72xnNKQHhmYcIE7CXhqyPxiPw9LirgC9cfaOawe
7+
auzFR7OxGvD2x5xyKx7FwL5qSsnizVyoZeaLozGo/cU7rKFIF2B7p8xI0ARNnvSo
8+
ov8qTW3pAgMBAAECggEAFFg1W7zqCh1GhGQ5y4zzhNa7BpiIAmMiN+Y2RtaDhBRY
9+
bKHyZJcwRxZcBG5EPvwMBo1yEla6uAlILO6kcu3hJf63dnK5hC1KzaFgC3NbS9Yh
10+
JsqfNUcAD3+PCy0RCnk1OXEnbut9ZpEghn7Kf8kzj4Km7RySBa/5CSBHwikEKQ85
11+
jO4dvtx0wDYbbvTrIcCKS74KoM10hioR3478m/6sx3sYau1bOUAUgvBBQc4WmdwJ
12+
Mxxz2ZqTQp4IyTGLIIS7vhyJLG9dcb1fapuMXjRYUn9i2ku9mbB+p/n514RLS+2W
13+
iS9IokYY2NwYv5Ue2cxvtmE5uVqx7Hkm4FEeFQJCHQKBgQDWLaT5mWRZaIMGyoas
14+
lmqqYM0gLlb9E9mxyPMc/BV9flV/8dTzSVarvGDJtefVQB4LHBOCXQYj9ZiqUsND
15+
5b7ZH/sVBNBfm/FR6rebKYXc0HOzhmCsoiuVSMRmpLZsWVpeX2BzCVJzES5ejm0o
16+
t3nGFzzLyOwWxrvp5zcGLwsNWwKBgQDCHyMHQ052QBujBs6PICd6pQ+VtcTSmXbX
17+
TUcUFptzdRRIZdnuM2r2ZhUcBV1RSrHFfpzeyMuTW1+sU4I+ggOeM15ui1XRJjHI
18+
FId6Ahj35QR/Jqrlyhde5g/xElY7/4Vlvw27DlrqrSa8QzsM32J8RoQB3SppuPlz
19+
lnZoB5uBCwKBgBuYbfUq6l8KtDcfyRJbnwqsxkErN1IMSLQ7a/eEE1DEAkgl5IYk
20+
IOKntuDGa0RyqmxMBcd6LNxdPHpVh4ssAtb+497la+OluAYR8+4t/21f/khXPAWC
21+
L5NgeM2w00BKkvYt28N2pATnZc4RE8d3PF1liRPIo4KbwIJ2pARL82SZAoGAW8cu
22+
33sx+HR83IoWVNLl93VctfJ3eP53knmF1niNzHuZOFV3QMhslMxUxKfAo/OFsxMW
23+
hbo3jZbQ1/+vf3Am17//sJIN49GEDc2u879UILfVdWxJtlTi0cpB1T9PKBS59A3t
24+
Jvg1geiVfMLog0CGJq2MMfln2Q5MWhrUJoEaQ1sCgYBGIrbT0Tpb/09w1qZ21kDk
25+
4iBAnKiy3KAeXlq3bVpfV+u/Ah6K7nvKIFtende/FoKFhiIu7YAFkOhIWRvBsRZ0
26+
KWt2rlHfjlk+iZN9NqaWQqfmUvemiuBxie7ddFmMJwK2YYmmSI1hnTQlVLHuqMs6
27+
fDfGqsm1Edj00rLA7f6ylg==
28+
-----END PRIVATE KEY-----

test/system-ca/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,34 @@ security add-certificates \
1818
security add-certificates \
1919
-k /Users/$USER/Library/Keychains/login.keychain-db \
2020
test/fixtures/keys/non-trusted-intermediate-ca.pem
21+
security add-trusted-cert \
22+
-k /Users/$USER/Library/Keychains/login.keychain-db \
23+
test/fixtures/keys/expired-root-cert.pem
24+
# Self-signed cert with trust settings that lack kSecTrustSettingsResult.
25+
security add-trusted-cert \
26+
-k /Users/$USER/Library/Keychains/login.keychain-db \
27+
test/fixtures/keys/selfsigned-no-result-root-cert.pem
28+
security trust-settings-export /tmp/node-trust-settings.plist
29+
CERT_SHA1=$(openssl x509 \
30+
-in test/fixtures/keys/selfsigned-no-result-root-cert.pem \
31+
-fingerprint -sha1 -noout | sed 's/.*=//;s/://g')
32+
/usr/libexec/PlistBuddy \
33+
-c "Delete :trustList:${CERT_SHA1}:trustSettings" \
34+
/tmp/node-trust-settings.plist
35+
/usr/libexec/PlistBuddy \
36+
-c "Add :trustList:${CERT_SHA1}:trustSettings array" \
37+
/tmp/node-trust-settings.plist
38+
/usr/libexec/PlistBuddy \
39+
-c "Add :trustList:${CERT_SHA1}:trustSettings:0 dict" \
40+
/tmp/node-trust-settings.plist
41+
security trust-settings-import /tmp/node-trust-settings.plist
42+
rm /tmp/node-trust-settings.plist
43+
# Duplicate cert in a second keychain
44+
security create-keychain -p "test" /tmp/node-test-dup.keychain
45+
security add-certificates \
46+
-k /tmp/node-test-dup.keychain \
47+
test/fixtures/keys/fake-startcom-root-cert.pem
48+
security list-keychains -d user -s login.keychain-db /tmp/node-test-dup.keychain
2149
```
2250

2351
**Removing the certificate**
@@ -29,6 +57,12 @@ security delete-certificate -c 'NodeJS-Test-Intermediate-CA' \
2957
-t /Users/$USER/Library/Keychains/login.keychain-db
3058
security delete-certificate -c 'NodeJS-Non-Trusted-Test-Intermediate-CA' \
3159
-t /Users/$USER/Library/Keychains/login.keychain-db
60+
security delete-certificate -c 'NodeJS-Test-Expired-Root' \
61+
-t /Users/$USER/Library/Keychains/login.keychain-db
62+
security delete-certificate -c 'NodeJS-Test-No-Result-Root' \
63+
-t /Users/$USER/Library/Keychains/login.keychain-db
64+
security list-keychains -d user -s login.keychain-db
65+
security delete-keychain /tmp/node-test-dup.keychain
3266
```
3367

3468
## Windows

0 commit comments

Comments
 (0)