Skip to content

Commit 6a5bc53

Browse files
authored
[iOS] Implement DSA, RSA, EC key import/export (#51926)
1 parent 1a18528 commit 6a5bc53

38 files changed

+1535
-784
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.Security.Cryptography;
7+
8+
namespace Internal.Cryptography
9+
{
10+
internal static partial class AsymmetricAlgorithmHelpers
11+
{
12+
// Encodes a EC key as an uncompressed set of concatenated scalars,
13+
// optionally including the private key. To omit the private parameter,
14+
// "d" must have a length of zero.
15+
public static void EncodeToUncompressedAnsiX963Key(
16+
ReadOnlySpan<byte> x,
17+
ReadOnlySpan<byte> y,
18+
ReadOnlySpan<byte> d,
19+
Span<byte> destination)
20+
{
21+
const byte UncompressedKeyPrefix = 0x04;
22+
if (x.Length != y.Length || (d.Length > 0 && d.Length != y.Length))
23+
throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
24+
25+
int size = 1 + x.Length + y.Length + d.Length; // 0x04 || X || Y { || D }
26+
27+
if (destination.Length < size)
28+
{
29+
Debug.Fail("destination.Length < size");
30+
throw new CryptographicException();
31+
}
32+
33+
destination[0] = UncompressedKeyPrefix;
34+
x.CopyTo(destination.Slice(1));
35+
y.CopyTo(destination.Slice(1 + x.Length));
36+
d.CopyTo(destination.Slice(1 + x.Length + y.Length));
37+
}
38+
39+
public static void DecodeFromUncompressedAnsiX963Key(
40+
ReadOnlySpan<byte> ansiKey,
41+
bool hasPrivateKey,
42+
out ECParameters ret)
43+
{
44+
ret = default;
45+
46+
const byte UncompressedKeyPrefix = 0x04;
47+
if (ansiKey.Length < 1 || ansiKey[0] != UncompressedKeyPrefix)
48+
throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
49+
50+
int fieldCount = hasPrivateKey ? 3 : 2;
51+
int fieldSize = (ansiKey.Length - 1) / fieldCount;
52+
53+
if (ansiKey.Length != 1 + fieldSize * fieldCount)
54+
throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
55+
56+
ret.Q = new ECPoint {
57+
X = ansiKey.Slice(1, fieldSize).ToArray(),
58+
Y = ansiKey.Slice(1 + fieldSize, fieldSize).ToArray()
59+
};
60+
61+
if (hasPrivateKey)
62+
{
63+
ret.D = ansiKey.Slice(1 + fieldSize + fieldSize, fieldSize).ToArray();
64+
}
65+
}
66+
}
67+
}

src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ private static extern int AppleCryptoNative_SecKeychainEnumerateIdentities(
6666
out SafeCFArrayHandle matches,
6767
out int pOSStatus);
6868

69-
internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeKeychainItemHandle item)
69+
private static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeHandle item)
7070
{
7171
bool addedRef = false;
7272

@@ -85,6 +85,12 @@ internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeKeychainItemH
8585
}
8686
}
8787

88+
internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeKeychainItemHandle item)
89+
=> SecKeychainItemCopyKeychain((SafeHandle)item);
90+
91+
internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeSecKeyRefHandle item)
92+
=> SecKeychainItemCopyKeychain((SafeHandle)item);
93+
8894
internal static SafeKeychainHandle SecKeychainItemCopyKeychain(IntPtr item)
8995
{
9096
SafeKeychainHandle keychain;

src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.cs

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,6 @@ internal static partial class AppleCrypto
1616
private const int kErrorSeeError = -2;
1717
private const int kPlatformNotSupported = -5;
1818

19-
private static int AppleCryptoNative_SecKeyImportEphemeral(
20-
ReadOnlySpan<byte> pbKeyBlob,
21-
int isPrivateKey,
22-
out SafeSecKeyRefHandle ppKeyOut,
23-
out int pOSStatus) =>
24-
AppleCryptoNative_SecKeyImportEphemeral(
25-
ref MemoryMarshal.GetReference(pbKeyBlob),
26-
pbKeyBlob.Length,
27-
isPrivateKey,
28-
out ppKeyOut,
29-
out pOSStatus);
30-
31-
[DllImport(Libraries.AppleCryptoNative)]
32-
private static extern int AppleCryptoNative_SecKeyImportEphemeral(
33-
ref byte pbKeyBlob,
34-
int cbKeyBlob,
35-
int isPrivateKey,
36-
out SafeSecKeyRefHandle ppKeyOut,
37-
out int pOSStatus);
38-
3919
[DllImport(Libraries.AppleCryptoNative)]
4020
private static extern ulong AppleCryptoNative_SecKeyGetSimpleKeySizeInBytes(SafeSecKeyRefHandle publicKey);
4121

@@ -102,40 +82,27 @@ internal static int GetSimpleKeySizeInBits(SafeSecKeyRefHandle publicKey)
10282
return (int)(keySizeInBytes * 8);
10383
}
10484
}
105-
106-
internal static SafeSecKeyRefHandle ImportEphemeralKey(ReadOnlySpan<byte> keyBlob, bool hasPrivateKey)
107-
{
108-
Debug.Assert(keyBlob != null);
109-
110-
SafeSecKeyRefHandle keyHandle;
111-
int osStatus;
112-
113-
int ret = AppleCryptoNative_SecKeyImportEphemeral(
114-
keyBlob,
115-
hasPrivateKey ? 1 : 0,
116-
out keyHandle,
117-
out osStatus);
118-
119-
if (ret == 1 && !keyHandle.IsInvalid)
120-
{
121-
return keyHandle;
122-
}
123-
124-
if (ret == 0)
125-
{
126-
throw CreateExceptionForOSStatus(osStatus);
127-
}
128-
129-
Debug.Fail($"SecKeyImportEphemeral returned {ret}");
130-
throw new CryptographicException();
131-
}
13285
}
13386
}
13487

13588
namespace System.Security.Cryptography.Apple
13689
{
137-
internal sealed class SafeSecKeyRefHandle : SafeKeychainItemHandle
90+
internal sealed class SafeSecKeyRefHandle : SafeHandle
13891
{
92+
public SafeSecKeyRefHandle()
93+
: base(IntPtr.Zero, ownsHandle: true)
94+
{
95+
}
96+
97+
protected override bool ReleaseHandle()
98+
{
99+
Interop.CoreFoundation.CFRelease(handle);
100+
SetHandle(IntPtr.Zero);
101+
return true;
102+
}
103+
104+
public override bool IsInvalid => handle == IntPtr.Zero;
105+
139106
protected override void Dispose(bool disposing)
140107
{
141108
if (disposing && SafeHandleCache<SafeSecKeyRefHandle>.IsCachedInvalidHandle(this))
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.Runtime.InteropServices;
7+
using System.Security.Cryptography;
8+
using System.Security.Cryptography.Apple;
9+
using Microsoft.Win32.SafeHandles;
10+
11+
internal static partial class Interop
12+
{
13+
internal static partial class AppleCrypto
14+
{
15+
internal enum PAL_KeyAlgorithm : uint
16+
{
17+
Unknown = 0,
18+
EC = 1,
19+
RSA = 2,
20+
}
21+
22+
internal static unsafe SafeSecKeyRefHandle CreateDataKey(
23+
ReadOnlySpan<byte> keyData,
24+
PAL_KeyAlgorithm keyAlgorithm,
25+
bool isPublic)
26+
{
27+
fixed (byte* pKey = keyData)
28+
{
29+
int result = AppleCryptoNative_SecKeyCreateWithData(
30+
pKey,
31+
keyData.Length,
32+
keyAlgorithm,
33+
isPublic ? 1 : 0,
34+
out SafeSecKeyRefHandle dataKey,
35+
out SafeCFErrorHandle errorHandle);
36+
37+
using (errorHandle)
38+
{
39+
switch (result)
40+
{
41+
case kSuccess:
42+
return dataKey;
43+
case kErrorSeeError:
44+
throw CreateExceptionForCFError(errorHandle);
45+
default:
46+
Debug.Fail($"SecKeyCreateWithData returned {result}");
47+
throw new CryptographicException();
48+
}
49+
}
50+
}
51+
}
52+
53+
internal static byte[] SecKeyCopyExternalRepresentation(
54+
SafeSecKeyRefHandle key)
55+
{
56+
int result = AppleCryptoNative_SecKeyCopyExternalRepresentation(
57+
key,
58+
out SafeCFDataHandle data,
59+
out SafeCFErrorHandle errorHandle);
60+
61+
using (errorHandle)
62+
using (data)
63+
{
64+
switch (result)
65+
{
66+
case kSuccess:
67+
return CoreFoundation.CFGetData(data);
68+
case kErrorSeeError:
69+
throw CreateExceptionForCFError(errorHandle);
70+
default:
71+
Debug.Fail($"SecKeyCopyExternalRepresentation returned {result}");
72+
throw new CryptographicException();
73+
}
74+
}
75+
}
76+
77+
[DllImport(Libraries.AppleCryptoNative)]
78+
private static unsafe extern int AppleCryptoNative_SecKeyCreateWithData(
79+
byte* pKey,
80+
int cbKey,
81+
PAL_KeyAlgorithm keyAlgorithm,
82+
int isPublic,
83+
out SafeSecKeyRefHandle pDataKey,
84+
out SafeCFErrorHandle pErrorOut);
85+
86+
[DllImport(Libraries.AppleCryptoNative)]
87+
private static unsafe extern int AppleCryptoNative_SecKeyCopyExternalRepresentation(
88+
SafeSecKeyRefHandle key,
89+
out SafeCFDataHandle pDataOut,
90+
out SafeCFErrorHandle pErrorOut);
91+
92+
[DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SecKeyCopyPublicKey")]
93+
internal static unsafe extern SafeSecKeyRefHandle CopyPublicKey(SafeSecKeyRefHandle privateKey);
94+
}
95+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,53 @@ internal static partial class AppleCrypto
1414
{
1515
private static readonly SafeCreateHandle s_nullExportString = new SafeCreateHandle();
1616

17+
private static int AppleCryptoNative_SecKeyImportEphemeral(
18+
ReadOnlySpan<byte> pbKeyBlob,
19+
int isPrivateKey,
20+
out SafeSecKeyRefHandle ppKeyOut,
21+
out int pOSStatus) =>
22+
AppleCryptoNative_SecKeyImportEphemeral(
23+
ref MemoryMarshal.GetReference(pbKeyBlob),
24+
pbKeyBlob.Length,
25+
isPrivateKey,
26+
out ppKeyOut,
27+
out pOSStatus);
28+
29+
[DllImport(Libraries.AppleCryptoNative)]
30+
private static extern int AppleCryptoNative_SecKeyImportEphemeral(
31+
ref byte pbKeyBlob,
32+
int cbKeyBlob,
33+
int isPrivateKey,
34+
out SafeSecKeyRefHandle ppKeyOut,
35+
out int pOSStatus);
36+
37+
internal static SafeSecKeyRefHandle ImportEphemeralKey(ReadOnlySpan<byte> keyBlob, bool hasPrivateKey)
38+
{
39+
Debug.Assert(keyBlob != null);
40+
41+
SafeSecKeyRefHandle keyHandle;
42+
int osStatus;
43+
44+
int ret = AppleCryptoNative_SecKeyImportEphemeral(
45+
keyBlob,
46+
hasPrivateKey ? 1 : 0,
47+
out keyHandle,
48+
out osStatus);
49+
50+
if (ret == 1 && !keyHandle.IsInvalid)
51+
{
52+
return keyHandle;
53+
}
54+
55+
if (ret == 0)
56+
{
57+
throw CreateExceptionForOSStatus(osStatus);
58+
}
59+
60+
Debug.Fail($"SecKeyImportEphemeral returned {ret}");
61+
throw new CryptographicException();
62+
}
63+
1764
[DllImport(Libraries.AppleCryptoNative)]
1865
private static extern int AppleCryptoNative_SecKeyExport(
1966
SafeSecKeyRefHandle? key,

src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ internal static SafeSecKeyRefHandle X509GetPrivateKeyFromIdentity(SafeSecIdentit
331331
SafeSecKeyRefHandle key;
332332
int osStatus = AppleCryptoNative_X509CopyPrivateKeyFromIdentity(identity, out key);
333333

334-
SafeTemporaryKeychainHandle.TrackItem(key);
334+
//SafeTemporaryKeychainHandle.TrackItem(key);
335335

336336
if (osStatus != 0)
337337
{
@@ -354,7 +354,7 @@ internal static SafeSecKeyRefHandle X509GetPublicKey(SafeSecCertificateHandle ce
354354
int osStatus;
355355
int ret = AppleCryptoNative_X509GetPublicKey(cert, out publicKey, out osStatus);
356356

357-
SafeTemporaryKeychainHandle.TrackItem(publicKey);
357+
//SafeTemporaryKeychainHandle.TrackItem(publicKey);
358358

359359
if (ret == 1)
360360
{

0 commit comments

Comments
 (0)