Skip to content

Commit

Permalink
[release/8.0-staging] macOS: Set certificate as a dependency of priva…
Browse files Browse the repository at this point in the history
…te 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>
  • Loading branch information
vcsjones and steveisok authored Jan 17, 2024
1 parent 85c2772 commit 96b0a55
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,38 @@ namespace System.Security.Cryptography.Apple
{
internal sealed class SafeSecKeyRefHandle : SafeHandle
{
private SafeHandle? _parentHandle;

public SafeSecKeyRefHandle()
: base(IntPtr.Zero, ownsHandle: true)
{
}

internal void SetParentHandle(SafeHandle parentHandle)
{
Debug.Assert(_parentHandle is null);

bool added = false;
parentHandle.DangerousAddRef(ref added);
_parentHandle = parentHandle;

// If we became invalid while the parent handle was being incremented, release the parent handle since
// ReleaseHandle will not get called.
if (IsInvalid)
{
_parentHandle.DangerousRelease();
_parentHandle = null;
}
}

protected override bool ReleaseHandle()
{
Interop.CoreFoundation.CFRelease(handle);
SetHandle(IntPtr.Zero);

_parentHandle?.DangerousRelease();
_parentHandle = null;

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal sealed partial class AppleCertificatePal : ICertificatePal
publicKey = Interop.AppleCrypto.ImportEphemeralKey(_certData.SubjectPublicKeyInfo, false);
}

privateKey.SetParentHandle(_certHandle);
return new DSAImplementation.DSASecurityTransforms(publicKey, privateKey);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ public byte[] SubjectPublicKeyInfo
Debug.Assert(!_identityHandle.IsInvalid);
SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle);
SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle);
privateKey.SetParentHandle(_certHandle);
Debug.Assert(!publicKey.IsInvalid);

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

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

return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(publicKey, privateKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography.Dsa.Tests;
using System.Security.Cryptography.X509Certificates.Tests.CertificateCreation;
using System.Threading;
using Microsoft.DotNet.XUnitExtensions;
using Test.Cryptography;
Expand All @@ -25,6 +27,155 @@ public CertTests(ITestOutputHelper output)
_log = output;
}

[Fact]
public static void PublicPrivateKey_IndependentLifetimes_ECDsa()
{
X509Certificate2 loaded;

using (ECDsa ca = ECDsa.Create(ECCurve.NamedCurves.nistP256))
{
CertificateRequest req = new("CN=potatos", ca, HashAlgorithmName.SHA256);

using (X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(3)))
{
loaded = new X509Certificate2(cert.Export(X509ContentType.Pkcs12, "carrots"), "carrots");
}
}

using (ECDsa verifyKey = loaded.GetECDsaPublicKey())
{
byte[] signature;
byte[] data = RandomNumberGenerator.GetBytes(32);

using (ECDsa signingKey = loaded.GetECDsaPrivateKey())
{
loaded.Dispose();
signature = signingKey.SignHash(data);
}

Assert.True(verifyKey.VerifyHash(data, signature), nameof(verifyKey.VerifyHash));
}
}

[Fact]
public static void PublicPrivateKey_IndependentLifetimes_ECDiffieHellman()
{
X509Certificate2 loaded;

using (ECDsa ca = ECDsa.Create(ECCurve.NamedCurves.nistP256))
using (ECDiffieHellman ecdh = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256))
{
CertificateRequest issuerRequest = new CertificateRequest(
new X500DistinguishedName("CN=root"),
ca,
HashAlgorithmName.SHA256);

issuerRequest.CertificateExtensions.Add(
new X509BasicConstraintsExtension(true, false, 0, true));

CertificateRequest request = new CertificateRequest(
new X500DistinguishedName("CN=potato"),
new PublicKey(ecdh),
HashAlgorithmName.SHA256);

request.CertificateExtensions.Add(
new X509BasicConstraintsExtension(false, false, 0, true));
request.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.KeyAgreement, true));

DateTimeOffset notBefore = DateTimeOffset.UtcNow;
DateTimeOffset notAfter = notBefore.AddDays(30);
byte[] serial = [1, 2, 3, 4, 5, 6, 7, 8];

using (X509Certificate2 issuer = issuerRequest.CreateSelfSigned(notBefore, notAfter))
using (X509Certificate2 cert = request.Create(issuer, notBefore, notAfter, serial))
using (X509Certificate2 certWithKey = cert.CopyWithPrivateKey(ecdh))
{
loaded = new X509Certificate2(certWithKey.Export(X509ContentType.Pkcs12, "carrots"), "carrots");;
}
}

using (ECDiffieHellman partyB = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256))
using (ECDiffieHellman partyAPrivateKey = loaded.GetECDiffieHellmanPrivateKey())
using (ECDiffieHellman partyAPublicKey = loaded.GetECDiffieHellmanPublicKey())
{
loaded.Dispose();
byte[] derivedB = partyB.DeriveKeyFromHash(partyAPublicKey.PublicKey, HashAlgorithmName.SHA256, null, null);
byte[] derivedA = partyAPrivateKey.DeriveKeyFromHash(partyB.PublicKey, HashAlgorithmName.SHA256, null, null);
Assert.Equal(derivedB, derivedA);
}
}

[Fact]
public static void PublicPrivateKey_IndependentLifetimes_RSA()
{
X509Certificate2 loaded;

using (RSA ca = RSA.Create(2048))
{
CertificateRequest req = new("CN=potatos", ca, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

using (X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(3)))
{
loaded = new X509Certificate2(cert.Export(X509ContentType.Pkcs12, "carrots"), "carrots");
}
}

using (RSA verifyKey = loaded.GetRSAPublicKey())
{
byte[] signature;
byte[] data = RandomNumberGenerator.GetBytes(32);

using (RSA signingKey = loaded.GetRSAPrivateKey())
{
loaded.Dispose();
signature = signingKey.SignHash(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}

Assert.True(verifyKey.VerifyHash(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1), nameof(verifyKey.VerifyHash));
}
}

[Fact]
[SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")]
public static void PublicPrivateKey_IndependentLifetimes_DSA()
{
X509Certificate2 loaded;

using (DSA ca = DSA.Create())
{
ca.ImportParameters(DSATestData.GetDSA1024Params());
DSAX509SignatureGenerator gen = new DSAX509SignatureGenerator(ca);
X500DistinguishedName dn = new X500DistinguishedName("CN=potatos");

CertificateRequest req = new CertificateRequest(
dn,
gen.PublicKey,
HashAlgorithmName.SHA1);

using (X509Certificate2 cert = req.Create(dn, gen, DateTimeOffset.Now, DateTimeOffset.Now.AddDays(3), new byte[] { 1, 2, 3 }))
using (X509Certificate2 certWithKey = cert.CopyWithPrivateKey(ca))
{

loaded = new X509Certificate2(certWithKey.Export(X509ContentType.Pkcs12, "carrots"), "carrots");
}
}

using (DSA verifyKey = loaded.GetDSAPublicKey())
{
byte[] signature;
byte[] data = RandomNumberGenerator.GetBytes(20);

using (DSA signingKey = loaded.GetDSAPrivateKey())
{
loaded.Dispose();
signature = signingKey.CreateSignature(data);
}

Assert.True(verifyKey.VerifySignature(data, signature), nameof(verifyKey.VerifySignature));
}
}

[Fact]
public static void RaceDisposeAndKeyAccess()
{
Expand Down

0 comments on commit 96b0a55

Please sign in to comment.