8303465: KeyStore of type KeychainStore, provider Apple does not show all trusted certificates

Reviewed-by: mbaesken, weijun
This commit is contained in:
Christoph Langer 2023-06-05 08:36:47 +00:00
parent 11fb5b2209
commit ac41c03003
3 changed files with 213 additions and 52 deletions

View file

@ -69,7 +69,7 @@ public final class KeychainStore extends KeyStoreSpi {
Certificate cert;
long certRef; // SecCertificateRef for this key
// Each KeyStore.TrustedCertificateEntry have 2 attributes:
// Each KeyStore.TrustedCertificateEntry has 2 attributes:
// 1. "trustSettings" -> trustSettings.toString()
// 2. "2.16.840.1.113894.746875.1.1" -> trustedKeyUsageValue
// The 1st one is mainly for debugging use. The 2nd one is similar
@ -652,7 +652,6 @@ public final class KeychainStore extends KeyStoreSpi {
_releaseKeychainItemRef(((TrustedCertEntry)entry).certRef);
}
} else {
Certificate certElem;
KeyEntry keyEntry = (KeyEntry)entry;
if (keyEntry.chain != null) {
@ -804,8 +803,26 @@ public final class KeychainStore extends KeyStoreSpi {
tce.cert = cert;
tce.certRef = keychainItemRef;
// Check whether a certificate with same alias already exists and is the same
// If yes, we can return here - the existing entry must have the same
// properties and trust settings
if (entries.contains(alias.toLowerCase())) {
int uniqueVal = 1;
String originalAlias = alias;
var co = entries.get(alias.toLowerCase());
while (co != null) {
if (co instanceof TrustedCertEntry tco) {
if (tco.cert.equals(tce.cert)) {
return;
}
}
alias = originalAlias + " " + uniqueVal++;
co = entries.get(alias.toLowerCase());
}
}
tce.trustSettings = new ArrayList<>();
Map<String,String> tmpMap = new LinkedHashMap<>();
Map<String, String> tmpMap = new LinkedHashMap<>();
for (int i = 0; i < inputTrust.size(); i++) {
if (inputTrust.get(i) == null) {
tce.trustSettings.add(tmpMap);
@ -828,9 +845,10 @@ public final class KeychainStore extends KeyStoreSpi {
} catch (Exception e) {
isSelfSigned = false;
}
if (tce.trustSettings.isEmpty()) {
if (isSelfSigned) {
// If a self-signed certificate has an empty trust settings,
// If a self-signed certificate has trust settings without specific entries,
// trust it for all purposes
tce.trustedKeyUsageValue = KnownOIDs.anyExtendedKeyUsage.value();
} else {
@ -843,11 +861,19 @@ public final class KeychainStore extends KeyStoreSpi {
for (var oneTrust : tce.trustSettings) {
var result = oneTrust.get("kSecTrustSettingsResult");
// https://developer.apple.com/documentation/security/sectrustsettingsresult?language=objc
// 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot
// 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot,
// 3 = kSecTrustSettingsResultDeny
// If missing, a default value of kSecTrustSettingsResultTrustRoot is assumed
// for self-signed certificates (see doc for SecTrustSettingsCopyTrustSettings).
// (see doc for SecTrustSettingsCopyTrustSettings).
// Note that the same SecPolicyOid can appear in multiple trust settings
// for different kSecTrustSettingsAllowedError and/or kSecTrustSettingsPolicyString.
// If we find explicit distrust in some record, we ignore the certificate
if ("3".equals(result)) {
return;
}
// Trust, if explicitly trusted or result is null and certificate is self signed
if ((result == null && isSelfSigned)
|| "1".equals(result) || "2".equals(result)) {
// When no kSecTrustSettingsPolicy, it means everything
@ -867,20 +893,13 @@ public final class KeychainStore extends KeyStoreSpi {
tce.trustedKeyUsageValue = values.toString();
}
}
// Make a creation date.
if (creationDate != 0)
tce.date = new Date(creationDate);
else
tce.date = new Date();
int uniqueVal = 1;
String originalAlias = alias;
while (entries.containsKey(alias.toLowerCase())) {
alias = originalAlias + " " + uniqueVal;
uniqueVal++;
}
entries.put(alias.toLowerCase(), tce);
} catch (Exception e) {
// The certificate will be skipped.

View file

@ -381,6 +381,35 @@ errOut:
#define ADDNULL(list) (*env)->CallBooleanMethod(env, list, jm_listAdd, NULL)
static void addTrustSettingsToInputTrust(JNIEnv *env, jmethodID jm_listAdd, CFArrayRef trustSettings, jobject inputTrust)
{
CFIndex count = CFArrayGetCount(trustSettings);
for (int i = 0; i < count; i++) {
CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i);
CFIndex size = CFDictionaryGetCount(oneTrust);
const void * keys [size];
const void * values [size];
CFDictionaryGetKeysAndValues(oneTrust, keys, values);
for (int j = 0; j < size; j++) {
NSString* s = [NSString stringWithFormat:@"%@", keys[j]];
ADD(inputTrust, s);
s = [NSString stringWithFormat:@"%@", values[j]];
ADD(inputTrust, s);
}
SecPolicyRef certPolicy;
certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy);
if (certPolicy != NULL) {
CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy);
ADD(inputTrust, @"SecPolicyOid");
NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")];
ADD(inputTrust, s);
CFRelease(policyDict);
}
ADDNULL(inputTrust);
}
}
static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore)
{
// Search the user keychain list for all X509 certificates.
@ -435,47 +464,41 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore)
goto errOut;
}
// Only add certificates with trusted settings
CFArrayRef trustSettings;
if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings)
== errSecItemNotFound) {
// See KeychainStore::createTrustedCertEntry for content of inputTrust
// We load trust settings from domains kSecTrustSettingsDomainUser and kSecTrustSettingsDomainAdmin
// kSecTrustSettingsDomainSystem is ignored because it seems to only contain data for root certificates
jobject inputTrust = NULL;
CFArrayRef trustSettings = NULL;
// Load user trustSettings into inputTrust
if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings) == errSecSuccess && trustSettings != NULL) {
inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons);
if (inputTrust == NULL) {
CFRelease(trustSettings);
goto errOut;
}
addTrustSettingsToInputTrust(env, jm_listAdd, trustSettings, inputTrust);
CFRelease(trustSettings);
}
// Load admin trustSettings into inputTrust
trustSettings = NULL;
if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainAdmin, &trustSettings) == errSecSuccess && trustSettings != NULL) {
if (inputTrust == NULL) {
inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons);
}
if (inputTrust == NULL) {
CFRelease(trustSettings);
goto errOut;
}
addTrustSettingsToInputTrust(env, jm_listAdd, trustSettings, inputTrust);
CFRelease(trustSettings);
}
// Only add certificates with trust settings
if (inputTrust == NULL) {
continue;
}
// See KeychainStore::createTrustedCertEntry for content of inputTrust
jobject inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons);
if (inputTrust == NULL) {
CFRelease(trustSettings);
goto errOut;
}
// Dump everything inside trustSettings into inputTrust
CFIndex count = CFArrayGetCount(trustSettings);
for (int i = 0; i < count; i++) {
CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i);
CFIndex size = CFDictionaryGetCount(oneTrust);
const void * keys [size];
const void * values [size];
CFDictionaryGetKeysAndValues(oneTrust, keys, values);
for (int j = 0; j < size; j++) {
NSString* s = [NSString stringWithFormat:@"%@", keys[j]];
ADD(inputTrust, s);
s = [NSString stringWithFormat:@"%@", values[j]];
ADD(inputTrust, s);
}
SecPolicyRef certPolicy;
certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy);
if (certPolicy != NULL) {
CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy);
ADD(inputTrust, @"SecPolicyOid");
NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")];
ADD(inputTrust, s);
CFRelease(policyDict);
}
ADDNULL(inputTrust);
}
CFRelease(trustSettings);
// Find the creation date.
jlong creationDate = getModDateFromItem(env, theItem);