Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ internal static SafeSecIdentityHandle X509CopyWithPrivateKey(
out identityHandle,
out osStatus);

SafeTemporaryKeychainHandle.TrackItem(identityHandle);

if (result == 1)
{
Debug.Assert(!identityHandle.IsInvalid);
Expand All @@ -288,13 +290,25 @@ internal static SafeSecIdentityHandle X509CopyWithPrivateKey(
{
SafeSecIdentityHandle identityHandle;
int osStatus;
int result;

int result = AppleCryptoNative_X509MoveToKeychain(
cert,
targetKeychain,
privateKey ?? SafeSecKeyRefHandle.InvalidHandle,
out identityHandle,
out osStatus);
// Ensure the keychain is not disposed, if there is any associated one
using (SafeKeychainHandle keychain = Interop.AppleCrypto.SecKeychainItemCopyKeychain(cert))
{
// AppleCryptoNative_X509MoveToKeychain can change the keychain of the input
// certificate, so we need to reflect that change.
SafeTemporaryKeychainHandle.UntrackItem(cert.DangerousGetHandle());

result = AppleCryptoNative_X509MoveToKeychain(
cert,
targetKeychain,
privateKey ?? SafeSecKeyRefHandle.InvalidHandle,
out identityHandle,
out osStatus);

SafeTemporaryKeychainHandle.TrackItem(cert);
SafeTemporaryKeychainHandle.TrackItem(identityHandle);
}

if (result == 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,5 @@ public static ICertificatePal FromBlob(

return result ?? FromDerBlob(rawData, GetDerCertContentType(rawData), password, keyStorageFlags);
}

// No temporary keychain on iOS
partial void DisposeTempKeychain();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ namespace System.Security.Cryptography.X509Certificates
{
internal sealed partial class AppleCertificatePal : ICertificatePal
{
private SafeKeychainHandle? _tempKeychain;

public static ICertificatePal FromBlob(
ReadOnlySpan<byte> rawData,
SafePasswordHandle password,
Expand Down Expand Up @@ -53,20 +51,7 @@ public static ICertificatePal FromBlob(

using (keychain)
{
AppleCertificatePal ret = ImportPkcs12(rawData, password, exportable, keychain);
if (!persist)
{
// If we used temporary keychain we need to prevent deletion.
// on 10.15+ if keychain is unlinked, certain certificate operations may fail.
bool success = false;
keychain.DangerousAddRef(ref success);
if (success)
{
ret._tempKeychain = keychain;
}
}

return ret;
return ImportPkcs12(rawData, password, exportable, keychain);
}
}

Expand All @@ -92,11 +77,6 @@ public static ICertificatePal FromBlob(
throw new CryptographicException();
}

public void DisposeTempKeychain()
{
Interlocked.Exchange(ref _tempKeychain, null)?.Dispose();
}

internal unsafe byte[] ExportPkcs8(ReadOnlySpan<char> password)
{
Debug.Assert(_identityHandle != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,6 @@ public void Dispose()

_certHandle = null!;
_identityHandle = null;

DisposeTempKeychain();
}

internal SafeSecCertificateHandle CertificateHandle => _certHandle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public void MoveTo(X509Certificate2Collection collection)

using (safeSecKeyRefHandle)
{
ICertificatePal newPal;
AppleCertificatePal newPal;

// SecItemImport doesn't seem to respect non-exportable import for PKCS#8,
// only PKCS#12.
Expand All @@ -119,6 +119,11 @@ public void MoveTo(X509Certificate2Collection collection)

X509Certificate2 cert = new X509Certificate2(newPal);
collection.Add(cert);

if (newPal != pal)
{
pal.Dispose();
}
Comment on lines +123 to +126
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: With this object disposed in deterministic manner the existing tests should reliably trip on miscounted references without a GC being triggered.

}
}
}
Expand Down