Skip to content

Commit 96b0a55

Browse files
vcsjonessteveisok
andauthored
[release/8.0-staging] macOS: Set certificate as a dependency of private key handles (#96993)
* macOS: Set certificate as a dependency of private key handles * [iOS] Skip PublicPrivateKey_IndependentLifetimes_DSA test (#96685) DSA is not supported --------- Co-authored-by: Steve Pfister <steveisok@users.noreply.github.com>
1 parent 85c2772 commit 96b0a55

File tree

4 files changed

+178
-0
lines changed

4 files changed

+178
-0
lines changed

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

+23
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,38 @@ namespace System.Security.Cryptography.Apple
178178
{
179179
internal sealed class SafeSecKeyRefHandle : SafeHandle
180180
{
181+
private SafeHandle? _parentHandle;
182+
181183
public SafeSecKeyRefHandle()
182184
: base(IntPtr.Zero, ownsHandle: true)
183185
{
184186
}
185187

188+
internal void SetParentHandle(SafeHandle parentHandle)
189+
{
190+
Debug.Assert(_parentHandle is null);
191+
192+
bool added = false;
193+
parentHandle.DangerousAddRef(ref added);
194+
_parentHandle = parentHandle;
195+
196+
// If we became invalid while the parent handle was being incremented, release the parent handle since
197+
// ReleaseHandle will not get called.
198+
if (IsInvalid)
199+
{
200+
_parentHandle.DangerousRelease();
201+
_parentHandle = null;
202+
}
203+
}
204+
186205
protected override bool ReleaseHandle()
187206
{
188207
Interop.CoreFoundation.CFRelease(handle);
189208
SetHandle(IntPtr.Zero);
209+
210+
_parentHandle?.DangerousRelease();
211+
_parentHandle = null;
212+
190213
return true;
191214
}
192215

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Keys.macOS.cs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ internal sealed partial class AppleCertificatePal : ICertificatePal
2929
publicKey = Interop.AppleCrypto.ImportEphemeralKey(_certData.SubjectPublicKeyInfo, false);
3030
}
3131

32+
privateKey.SetParentHandle(_certHandle);
3233
return new DSAImplementation.DSASecurityTransforms(publicKey, privateKey);
3334
}
3435

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.cs

+3
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ public byte[] SubjectPublicKeyInfo
330330
Debug.Assert(!_identityHandle.IsInvalid);
331331
SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle);
332332
SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle);
333+
privateKey.SetParentHandle(_certHandle);
333334
Debug.Assert(!publicKey.IsInvalid);
334335

335336
return new RSAImplementation.RSASecurityTransforms(publicKey, privateKey);
@@ -343,6 +344,7 @@ public byte[] SubjectPublicKeyInfo
343344
Debug.Assert(!_identityHandle.IsInvalid);
344345
SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle);
345346
SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle);
347+
privateKey.SetParentHandle(_certHandle);
346348
Debug.Assert(!publicKey.IsInvalid);
347349

348350
return new ECDsaImplementation.ECDsaSecurityTransforms(publicKey, privateKey);
@@ -356,6 +358,7 @@ public byte[] SubjectPublicKeyInfo
356358
Debug.Assert(!_identityHandle.IsInvalid);
357359
SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle);
358360
SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle);
361+
privateKey.SetParentHandle(_certHandle);
359362
Debug.Assert(!publicKey.IsInvalid);
360363

361364
return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(publicKey, privateKey);

src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs

+151
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System.Collections.Generic;
55
using System.IO;
66
using System.Runtime.InteropServices;
7+
using System.Security.Cryptography.Dsa.Tests;
8+
using System.Security.Cryptography.X509Certificates.Tests.CertificateCreation;
79
using System.Threading;
810
using Microsoft.DotNet.XUnitExtensions;
911
using Test.Cryptography;
@@ -25,6 +27,155 @@ public CertTests(ITestOutputHelper output)
2527
_log = output;
2628
}
2729

30+
[Fact]
31+
public static void PublicPrivateKey_IndependentLifetimes_ECDsa()
32+
{
33+
X509Certificate2 loaded;
34+
35+
using (ECDsa ca = ECDsa.Create(ECCurve.NamedCurves.nistP256))
36+
{
37+
CertificateRequest req = new("CN=potatos", ca, HashAlgorithmName.SHA256);
38+
39+
using (X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(3)))
40+
{
41+
loaded = new X509Certificate2(cert.Export(X509ContentType.Pkcs12, "carrots"), "carrots");
42+
}
43+
}
44+
45+
using (ECDsa verifyKey = loaded.GetECDsaPublicKey())
46+
{
47+
byte[] signature;
48+
byte[] data = RandomNumberGenerator.GetBytes(32);
49+
50+
using (ECDsa signingKey = loaded.GetECDsaPrivateKey())
51+
{
52+
loaded.Dispose();
53+
signature = signingKey.SignHash(data);
54+
}
55+
56+
Assert.True(verifyKey.VerifyHash(data, signature), nameof(verifyKey.VerifyHash));
57+
}
58+
}
59+
60+
[Fact]
61+
public static void PublicPrivateKey_IndependentLifetimes_ECDiffieHellman()
62+
{
63+
X509Certificate2 loaded;
64+
65+
using (ECDsa ca = ECDsa.Create(ECCurve.NamedCurves.nistP256))
66+
using (ECDiffieHellman ecdh = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256))
67+
{
68+
CertificateRequest issuerRequest = new CertificateRequest(
69+
new X500DistinguishedName("CN=root"),
70+
ca,
71+
HashAlgorithmName.SHA256);
72+
73+
issuerRequest.CertificateExtensions.Add(
74+
new X509BasicConstraintsExtension(true, false, 0, true));
75+
76+
CertificateRequest request = new CertificateRequest(
77+
new X500DistinguishedName("CN=potato"),
78+
new PublicKey(ecdh),
79+
HashAlgorithmName.SHA256);
80+
81+
request.CertificateExtensions.Add(
82+
new X509BasicConstraintsExtension(false, false, 0, true));
83+
request.CertificateExtensions.Add(
84+
new X509KeyUsageExtension(X509KeyUsageFlags.KeyAgreement, true));
85+
86+
DateTimeOffset notBefore = DateTimeOffset.UtcNow;
87+
DateTimeOffset notAfter = notBefore.AddDays(30);
88+
byte[] serial = [1, 2, 3, 4, 5, 6, 7, 8];
89+
90+
using (X509Certificate2 issuer = issuerRequest.CreateSelfSigned(notBefore, notAfter))
91+
using (X509Certificate2 cert = request.Create(issuer, notBefore, notAfter, serial))
92+
using (X509Certificate2 certWithKey = cert.CopyWithPrivateKey(ecdh))
93+
{
94+
loaded = new X509Certificate2(certWithKey.Export(X509ContentType.Pkcs12, "carrots"), "carrots");;
95+
}
96+
}
97+
98+
using (ECDiffieHellman partyB = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256))
99+
using (ECDiffieHellman partyAPrivateKey = loaded.GetECDiffieHellmanPrivateKey())
100+
using (ECDiffieHellman partyAPublicKey = loaded.GetECDiffieHellmanPublicKey())
101+
{
102+
loaded.Dispose();
103+
byte[] derivedB = partyB.DeriveKeyFromHash(partyAPublicKey.PublicKey, HashAlgorithmName.SHA256, null, null);
104+
byte[] derivedA = partyAPrivateKey.DeriveKeyFromHash(partyB.PublicKey, HashAlgorithmName.SHA256, null, null);
105+
Assert.Equal(derivedB, derivedA);
106+
}
107+
}
108+
109+
[Fact]
110+
public static void PublicPrivateKey_IndependentLifetimes_RSA()
111+
{
112+
X509Certificate2 loaded;
113+
114+
using (RSA ca = RSA.Create(2048))
115+
{
116+
CertificateRequest req = new("CN=potatos", ca, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
117+
118+
using (X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(3)))
119+
{
120+
loaded = new X509Certificate2(cert.Export(X509ContentType.Pkcs12, "carrots"), "carrots");
121+
}
122+
}
123+
124+
using (RSA verifyKey = loaded.GetRSAPublicKey())
125+
{
126+
byte[] signature;
127+
byte[] data = RandomNumberGenerator.GetBytes(32);
128+
129+
using (RSA signingKey = loaded.GetRSAPrivateKey())
130+
{
131+
loaded.Dispose();
132+
signature = signingKey.SignHash(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
133+
}
134+
135+
Assert.True(verifyKey.VerifyHash(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1), nameof(verifyKey.VerifyHash));
136+
}
137+
}
138+
139+
[Fact]
140+
[SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")]
141+
public static void PublicPrivateKey_IndependentLifetimes_DSA()
142+
{
143+
X509Certificate2 loaded;
144+
145+
using (DSA ca = DSA.Create())
146+
{
147+
ca.ImportParameters(DSATestData.GetDSA1024Params());
148+
DSAX509SignatureGenerator gen = new DSAX509SignatureGenerator(ca);
149+
X500DistinguishedName dn = new X500DistinguishedName("CN=potatos");
150+
151+
CertificateRequest req = new CertificateRequest(
152+
dn,
153+
gen.PublicKey,
154+
HashAlgorithmName.SHA1);
155+
156+
using (X509Certificate2 cert = req.Create(dn, gen, DateTimeOffset.Now, DateTimeOffset.Now.AddDays(3), new byte[] { 1, 2, 3 }))
157+
using (X509Certificate2 certWithKey = cert.CopyWithPrivateKey(ca))
158+
{
159+
160+
loaded = new X509Certificate2(certWithKey.Export(X509ContentType.Pkcs12, "carrots"), "carrots");
161+
}
162+
}
163+
164+
using (DSA verifyKey = loaded.GetDSAPublicKey())
165+
{
166+
byte[] signature;
167+
byte[] data = RandomNumberGenerator.GetBytes(20);
168+
169+
using (DSA signingKey = loaded.GetDSAPrivateKey())
170+
{
171+
loaded.Dispose();
172+
signature = signingKey.CreateSignature(data);
173+
}
174+
175+
Assert.True(verifyKey.VerifySignature(data, signature), nameof(verifyKey.VerifySignature));
176+
}
177+
}
178+
28179
[Fact]
29180
public static void RaceDisposeAndKeyAccess()
30181
{

0 commit comments

Comments
 (0)