Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
154ab21
Move PfxAsn to Common for sharing with X509Certificates
bartonjs Aug 26, 2019
31d5a9c
Move VerifyMac logic to PfxAsn.manual.cs
bartonjs Aug 26, 2019
ad73818
Move EncryptedDataAsn to src/Common
bartonjs Aug 27, 2019
6ba2d9d
Loader works (all tests pass)
bartonjs Sep 17, 2019
81da280
Both import and export pass, Windows should build again
bartonjs Sep 18, 2019
c23c6ff
Re-enable some disabled PFX tests for Linux
bartonjs Sep 19, 2019
347f2f0
Start prepping for move to shared Unix
bartonjs Sep 19, 2019
e74a9f6
Make the PFX exporter have no specific PAL references
bartonjs Sep 19, 2019
9f5820e
Re-enable a bunch of tests on macOS
bartonjs Sep 19, 2019
12771bc
Fix macOS compile
bartonjs Sep 19, 2019
01624d8
Change ExportPublicKeyAsPkcs12 to expect success on macOS
bartonjs Sep 20, 2019
b1ef2da
Apple p12 export uses UnixExportProvider (modulo SafePasswordHandle)
bartonjs Sep 20, 2019
7f47141
Factor out UnixPkcs12Reader
bartonjs Sep 21, 2019
1783a5a
With some native changes, most of the cert tests pass now
bartonjs Sep 24, 2019
2388510
Remove exportable from X509MoveToKeychain
bartonjs Sep 24, 2019
52e8701
Add native support
bartonjs Sep 24, 2019
443c862
Reading char2-explicit works
bartonjs Sep 26, 2019
d7ce17e
Writing Char2-explicit ECPrivateKey works
bartonjs Sep 26, 2019
4fd38f6
Reading and writing im/explicit char-2 works in all formats
bartonjs Sep 26, 2019
d7a7c59
Im/export of explicit prime works for all formats
bartonjs Sep 26, 2019
4b4017d
Fix exception
bartonjs Sep 26, 2019
3a50954
Merge commit 'bbc47bb872adc78099ff47e9ec6d65d92b1a2fce' into custom_p…
bartonjs Oct 9, 2019
fa0cc8f
Merge commit 'a7ee8cdccce7d2607d58f9b93ce319209a238f7a' into custom_p…
bartonjs Oct 9, 2019
4d2876a
Merge remote-tracking branch 'dotnet/master' into custom_pfx_loader_rb
bartonjs Oct 9, 2019
a94d543
Fix bad merge
bartonjs Oct 9, 2019
a259952
Simplify SafePasswordHandle
bartonjs Oct 9, 2019
d2881db
Commit asn.xml generated changes
bartonjs Oct 9, 2019
dfee702
Address missed namespace renames.
bartonjs Oct 10, 2019
bac76db
Remove remaining explicit EC parameters prototype code
bartonjs Oct 10, 2019
23ea49b
Fix stale method doc
bartonjs Oct 10, 2019
d9d8711
Some missed namespace updates
bartonjs Oct 10, 2019
23ce0e3
Revert unnecessary method
bartonjs Oct 10, 2019
d6680b5
Self-review cleanup
bartonjs Oct 10, 2019
667bbd4
Make the empty collection exception message nicer
bartonjs Oct 10, 2019
f2da56e
Start defining complex PFX tests
bartonjs Oct 10, 2019
bd5d0df
Add more tests
bartonjs Oct 11, 2019
93550fd
Correctly handle reading no mac with null-vs-empty password
bartonjs Oct 11, 2019
f4efc90
Merge remote-tracking branch 'dotnet/master' into custom_pfx_loader_rb
bartonjs Oct 24, 2019
bf02f6f
Add more PFX loader tests
bartonjs Oct 28, 2019
34d7ec1
Shroud the invalid keys
bartonjs Oct 28, 2019
d513909
More tests to increase coverage
bartonjs Oct 28, 2019
43738bd
Add a fallback PFX detector on macOS
bartonjs Oct 29, 2019
d250146
Address a tripping assert (yay comprehensive tests)
bartonjs Oct 29, 2019
2fbbc76
Add a test for corrupt certs (ccov)
bartonjs Oct 29, 2019
942aa74
Make tests take into account Apple's private key matching
bartonjs Oct 29, 2019
ba7a183
Make reference correctly release if CFStringCreateFromSpan throws
bartonjs Oct 29, 2019
8c6f2c4
Run import in both exportable and not
bartonjs Oct 29, 2019
0302bec
Speed up the tests a bit
bartonjs Oct 29, 2019
1bff605
Handle public/private mismatch during exportable import on macOS
bartonjs Oct 30, 2019
edf52e8
Add a test for multiple import in a non-canonical form
bartonjs Oct 30, 2019
511a0de
Self-review cleanup
bartonjs Oct 30, 2019
fa5db2a
Add missing CFRelease
bartonjs Oct 30, 2019
9ea3428
Address some differences between Windows 8.1 and Windows 10
bartonjs Nov 2, 2019
fe28861
Fix tests on macOS for duplicate item entry
bartonjs Nov 7, 2019
5f1416c
Simplify ownership of cfPassphrase in X509ImportCertificate
bartonjs Nov 7, 2019
4bf42f8
Avoid double-release on double-Dispose of ApplePkcs12CertLoader
bartonjs Nov 7, 2019
1839b21
Save pfxAsn to a local until after the potential throw
bartonjs Nov 7, 2019
107e630
Make GrowIfNeeded prefer the local to the ref.
bartonjs Nov 7, 2019
5143fb9
Break up UnixPkcs12Reader.Decrypt
bartonjs Nov 7, 2019
0df06c4
Dispose the old pal after making a new one.
bartonjs Nov 7, 2019
ae4f02a
Fix nasty arraypool imbalance
bartonjs Nov 7, 2019
d985688
Clarify some array rental lifetimes
bartonjs Nov 7, 2019
8efbfdb
Properly return temporary decryption buffers on null-vs-empty fallback
bartonjs Nov 7, 2019
aa0b23d
Handle rare "wrong failure" reason in Empty/Null_NoMac
bartonjs Nov 8, 2019
2f25a31
Move the Win10 PFX failure changover back a few builds
bartonjs Nov 8, 2019
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
25 changes: 8 additions & 17 deletions src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,21 @@ internal static partial class CoreFoundation
[DllImport(Libraries.CoreFoundationLibrary)]
private static extern CFIndex CFDataGetLength(SafeCFDataHandle cfData);

internal static unsafe Span<byte> CFDataDangerousGetSpan(SafeCFDataHandle cfData)
{
long length = CFDataGetLength(cfData).ToInt64();
byte* dataBytes = CFDataGetBytePtr(cfData);
return new Span<byte>(dataBytes, checked((int)length));
Comment thread
stephentoub marked this conversation as resolved.
}

internal static byte[] CFGetData(SafeCFDataHandle cfData)
{
bool addedRef = false;

try
{
cfData.DangerousAddRef(ref addedRef);
long length = CFDataGetLength(cfData).ToInt64();

if (length == 0)
{
return Array.Empty<byte>();
}

byte[] bytes = new byte[length];

unsafe
{
byte* dataBytes = CFDataGetBytePtr(cfData);
Marshal.Copy((IntPtr)dataBytes, bytes, 0, bytes.Length);
}

return bytes;

return CFDataDangerousGetSpan(cfData).ToArray();
}
finally
{
Expand Down
39 changes: 38 additions & 1 deletion src/Common/src/Interop/OSX/Interop.CoreFoundation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

using CFStringRef = System.IntPtr;
using CFArrayRef = System.IntPtr;

using CFIndex = System.IntPtr;

internal static partial class Interop
{
Expand Down Expand Up @@ -38,6 +38,24 @@ private enum CFStringBuiltInEncodings : uint
kCFStringEncodingUTF32LE = 0x1c000100
}

/// <summary>
/// Creates a CFStringRef from a specified range of memory with a specified encoding.
/// Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
/// <param name="alloc">Should be IntPtr.Zero</param>
/// <param name="bytes">The pointer to the beginning of the encoded string.</param>
/// <param name="numBytes">The number of bytes in the encoding to read.</param>
/// <param name="encoding">The encoding type.</param>
/// <param name="isExternalRepresentation">Whether or not a BOM is present.</param>
/// <returns>A CFStringRef on success, otherwise a SafeCreateHandle(IntPtr.Zero).</returns>
[DllImport(Interop.Libraries.CoreFoundationLibrary)]
private static extern SafeCreateHandle CFStringCreateWithBytes(
IntPtr alloc,
IntPtr bytes,
CFIndex numBytes,
CFStringBuiltInEncodings encoding,
bool isExternalRepresentation);

/// <summary>
/// Creates a CFStringRef from a 8-bit String object. Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
Expand Down Expand Up @@ -86,6 +104,25 @@ internal static SafeCreateHandle CFStringCreateWithCString(IntPtr utf8str)
return CFStringCreateWithCString(IntPtr.Zero, utf8str, CFStringBuiltInEncodings.kCFStringEncodingUTF8);
}

/// <summary>
/// Creates a CFStringRef from a span of chars.
/// Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
/// <param name="source">The chars to make a CFString version of.</param>
/// <returns>A CFStringRef on success, otherwise a SafeCreateHandle(IntPtr.Zero).</returns>
internal static unsafe SafeCreateHandle CFStringCreateFromSpan(ReadOnlySpan<char> source)
{
fixed (char* sourcePtr = source)
{
return CFStringCreateWithBytes(
IntPtr.Zero,
(IntPtr)sourcePtr,
new CFIndex(source.Length * 2),
Comment thread
bartonjs marked this conversation as resolved.
CFStringBuiltInEncodings.kCFStringEncodingUTF16,
isExternalRepresentation: false);
}
}

/// <summary>
/// Creates a pointer to an unmanaged CFArray containing the input values. Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ private static extern int AppleCryptoNative_SecKeyExport(
out SafeCFDataHandle cfDataOut,
out int pOSStatus);

internal static byte[] SecKeyExport(
internal static SafeCFDataHandle SecKeyExportData(
SafeSecKeyRefHandle key,
bool exportPrivate,
string password)
ReadOnlySpan<char> password)
{
SafeCreateHandle exportPassword = exportPrivate
? CoreFoundation.CFStringCreateWithCString(password)
? CoreFoundation.CFStringCreateFromSpan(password)
: s_nullExportString;

int ret;
Expand All @@ -53,25 +53,31 @@ internal static byte[] SecKeyExport(
}
}

byte[] exportedData;

using (cfData)
if (ret == 1)
{
if (ret == 0)
{
throw CreateExceptionForOSStatus(osStatus);
}
return cfData;
}

if (ret != 1)
{
Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}");
throw new CryptographicException();
}
cfData.Dispose();

exportedData = CoreFoundation.CFGetData(cfData);
if (ret == 0)
{
throw CreateExceptionForOSStatus(osStatus);
}

return exportedData;
Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}");
throw new CryptographicException();
}

internal static byte[] SecKeyExport(
SafeSecKeyRefHandle key,
bool exportPrivate,
string password)
{
using (SafeCFDataHandle cfData = SecKeyExportData(key, exportPrivate, password))
{
return CoreFoundation.CFGetData(cfData);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -357,5 +357,18 @@ namespace System.Security.Cryptography.Apple
{
internal sealed class SafeSecKeyRefHandle : SafeKeychainItemHandle
{
protected override void Dispose(bool disposing)
{
if (disposing && SafeHandleCache<SafeSecKeyRefHandle>.IsCachedInvalidHandle(this))
Comment thread
krwq marked this conversation as resolved.
{
return;
}

base.Dispose(disposing);
}

public static SafeSecKeyRefHandle InvalidHandle =>
SafeHandleCache<SafeSecKeyRefHandle>.GetInvalidHandle(
() => new SafeSecKeyRefHandle());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ private static extern int AppleCryptoNative_X509CopyWithPrivateKey(
out SafeSecIdentityHandle pIdentityHandleOut,
out int pOSStatus);

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_X509MoveToKeychain(
SafeSecCertificateHandle certHandle,
SafeKeychainHandle targetKeychain,
SafeSecKeyRefHandle privateKeyHandle,
out SafeSecIdentityHandle pIdentityHandleOut,
out int pOSStatus);

internal static byte[] X509GetRawData(SafeSecCertificateHandle cert)
{
int osStatus;
Expand Down Expand Up @@ -117,39 +125,24 @@ internal static SafeSecCertificateHandle X509ImportCertificate(
bool exportable,
out SafeSecIdentityHandle identityHandle)
{
SafeSecCertificateHandle certHandle;
int osStatus;
int ret;

SafeCreateHandle cfPassphrase = s_nullExportString;
SafeCreateHandle cfPassphrase = null;
bool releasePassword = false;

try
{
if (!importPassword.IsInvalid)
{
importPassword.DangerousAddRef(ref releasePassword);
IntPtr passwordHandle = importPassword.DangerousGetHandle();

if (passwordHandle != IntPtr.Zero)
{
cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle);
}
cfPassphrase = CoreFoundation.CFStringCreateFromSpan(importPassword.DangerousGetSpan());
Comment thread
bartonjs marked this conversation as resolved.
}

ret = AppleCryptoNative_X509ImportCertificate(
return X509ImportCertificate(
bytes,
bytes.Length,
contentType,
cfPassphrase,
keychain,
exportable ? 1 : 0,
out certHandle,
out identityHandle,
out osStatus);

SafeTemporaryKeychainHandle.TrackItem(certHandle);
SafeTemporaryKeychainHandle.TrackItem(identityHandle);
exportable,
out identityHandle);
Comment thread
krwq marked this conversation as resolved.
}
finally
{
Expand All @@ -158,11 +151,36 @@ internal static SafeSecCertificateHandle X509ImportCertificate(
importPassword.DangerousRelease();
}

if (cfPassphrase != s_nullExportString)
{
cfPassphrase.Dispose();
}
cfPassphrase?.Dispose();
}
}

internal static SafeSecCertificateHandle X509ImportCertificate(
byte[] bytes,
X509ContentType contentType,
SafeCreateHandle importPassword,
SafeKeychainHandle keychain,
bool exportable,
out SafeSecIdentityHandle identityHandle)
{
SafeSecCertificateHandle certHandle;
int osStatus;

SafeCreateHandle cfPassphrase = importPassword ?? s_nullExportString;

int ret = AppleCryptoNative_X509ImportCertificate(
bytes,
bytes.Length,
contentType,
cfPassphrase,
keychain,
exportable ? 1 : 0,
out certHandle,
out identityHandle,
out osStatus);

SafeTemporaryKeychainHandle.TrackItem(certHandle);
SafeTemporaryKeychainHandle.TrackItem(identityHandle);

if (ret == 1)
{
Expand Down Expand Up @@ -385,6 +403,56 @@ internal static SafeSecIdentityHandle X509CopyWithPrivateKey(
throw new CryptographicException();
}

internal static SafeSecIdentityHandle X509MoveToKeychain(
SafeSecCertificateHandle cert,
SafeKeychainHandle targetKeychain,
SafeSecKeyRefHandle privateKey)
{
SafeSecIdentityHandle identityHandle;
int osStatus;

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

if (result == 0)
{
identityHandle.Dispose();
throw CreateExceptionForOSStatus(osStatus);
}

if (result != 1)
{
Debug.Fail($"AppleCryptoNative_X509MoveToKeychain returned {result}");
throw new CryptographicException();
}

if (privateKey?.IsInvalid == false)
{
// If a PFX has a mismatched association between a private key and the
// certificate public key then MoveToKeychain will write the NULL SecIdentityRef
// (after cleaning up the temporary key).
//
// When that happens, just treat the import as public-only.
if (!identityHandle.IsInvalid)
{
return identityHandle;
}
}

// If the cert in the PFX had no key, but it was imported with PersistKeySet (imports into
// the default keychain) and a matching private key was already there, then an
// identityHandle could be found. But that's not desirable, since neither Windows or Linux would
// do that matching.
//
// So dispose the handle, no matter what.
identityHandle.Dispose();
return null;
}

private static byte[] X509Export(X509ContentType contentType, SafeCreateHandle cfPassphrase, IntPtr[] certHandles)
{
Debug.Assert(contentType == X509ContentType.Pkcs7 || contentType == X509ContentType.Pkcs12);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal static partial class Crypto
internal static extern SafeX509CrlHandle DecodeX509Crl(byte[] buf, int len);

[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_DecodeX509")]
internal static extern SafeX509Handle DecodeX509(byte[] buf, int len);
internal static extern SafeX509Handle DecodeX509(ref byte buf, int len);

[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509DerSize")]
internal static extern int GetX509DerSize(SafeX509Handle x);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal bool HasNullEquivalentParameters()
return RepresentsNull(Parameters);
}

private static bool RepresentsNull(ReadOnlyMemory<byte>? parameters)
internal static bool RepresentsNull(ReadOnlyMemory<byte>? parameters)
{
if (parameters == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<asn:Sequence
xmlns:asn="http://schemas.dot.net/asnxml/201808/"
name="DigestInfoAsn"
namespace="System.Security.Cryptography.Pkcs.Asn1">
namespace="System.Security.Cryptography.Asn1">

<!--
https://tools.ietf.org/html/rfc2313#section-10.1.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.Security.Cryptography;
using System.Security.Cryptography.Asn1;

namespace System.Security.Cryptography.Pkcs.Asn1
namespace System.Security.Cryptography.Asn1
{
[StructLayout(LayoutKind.Sequential)]
internal partial struct DigestInfoAsn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<asn:Sequence
xmlns:asn="http://schemas.dot.net/asnxml/201808/"
name="CertBagAsn"
namespace="System.Security.Cryptography.Pkcs.Asn1">
namespace="System.Security.Cryptography.Asn1.Pkcs12">

<!--
https://tools.ietf.org/html/rfc7292#section-4.2.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.Security.Cryptography;
using System.Security.Cryptography.Asn1;

namespace System.Security.Cryptography.Pkcs.Asn1
namespace System.Security.Cryptography.Asn1.Pkcs12
{
[StructLayout(LayoutKind.Sequential)]
internal partial struct CertBagAsn
Expand Down
Loading