[DllImport(Interop.Libraries.NCrypt, CharSet = CharSet.Unicode)]
internal static extern unsafe ErrorCode NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, [In] void* pbInput, int cbInput, CngPropertyOptions dwFlags);
+ internal static ErrorCode NCryptGetByteProperty(SafeNCryptHandle hObject, string pszProperty, ref byte result, CngPropertyOptions options = CngPropertyOptions.None)
+ {
+ int cbResult;
+ ErrorCode errorCode;
+
+ unsafe
+ {
+ fixed (byte* pResult = &result)
+ {
+ errorCode = Interop.NCrypt.NCryptGetProperty(
+ hObject,
+ pszProperty,
+ pResult,
+ sizeof(byte),
+ out cbResult,
+ options);
+ }
+ }
+
+ if (errorCode == ErrorCode.ERROR_SUCCESS)
+ {
+ Debug.Assert(cbResult == sizeof(byte));
+ }
+
+ return errorCode;
+ }
+
internal static ErrorCode NCryptGetIntProperty(SafeNCryptHandle hObject, string pszProperty, ref int result)
{
int cbResult;
if (keySpec == CryptKeySpec.CERT_NCRYPT_KEY_SPEC)
{
using (SafeNCryptKeyHandle keyHandle = new SafeNCryptKeyHandle(handle.DangerousGetHandle(), handle))
- using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None))
{
- if (typeof(T) == typeof(RSA))
- return (T)(object)new RSACng(cngKey);
- if (typeof(T) == typeof(ECDsa))
- return (T)(object)new ECDsaCng(cngKey);
- if (typeof(T) == typeof(DSA))
- return (T)(object)new DSACng(cngKey);
-
- Debug.Fail($"Unknown CNG key type request: {typeof(T).FullName}");
- return null;
+ CngKeyHandleOpenOptions options = CngKeyHandleOpenOptions.None;
+ byte clrIsEphemeral = 0;
+ Interop.NCrypt.ErrorCode errorCode = Interop.NCrypt.NCryptGetByteProperty(keyHandle, "CLR IsEphemeral", ref clrIsEphemeral, CngPropertyOptions.CustomProperty);
+
+ if (errorCode == Interop.NCrypt.ErrorCode.ERROR_SUCCESS && clrIsEphemeral == 1)
+ {
+ options |= CngKeyHandleOpenOptions.EphemeralKey;
+ }
+
+ using (CngKey cngKey = CngKey.Open(keyHandle, options))
+ {
+ if (typeof(T) == typeof(RSA))
+ return (T)(object)new RSACng(cngKey);
+ if (typeof(T) == typeof(ECDsa))
+ return (T)(object)new ECDsaCng(cngKey);
+ if (typeof(T) == typeof(DSA))
+ return (T)(object)new DSACng(cngKey);
+
+ Debug.Fail($"Unknown CNG key type request: {typeof(T).FullName}");
+ return null;
+ }
}
}
<Compile Include="$(CommonPath)\Interop\Windows\NCrypt\Interop.ErrorCode.cs">
<Link>Common\Interop\Windows\NCrypt\Interop.ErrorCode.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\NCrypt\Interop.Properties.cs">
+ <Link>Common\Interop\Windows\NCrypt\Interop.Properties.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\Interop\Windows\NCrypt\Interop.NCryptFreeObject.cs">
<Link>Common\Interop\Windows\NCrypt\Interop.NCryptFreeObject.cs</Link>
</Compile>
return rsa;
}
+
+ internal static DSA MakeExportable(this DSA dsa)
+ {
+ if (dsa is DSACng dsaCng)
+ {
+ const CngExportPolicies Exportability =
+ CngExportPolicies.AllowExport |
+ CngExportPolicies.AllowPlaintextExport;
+
+ if ((dsaCng.Key.ExportPolicy & Exportability) == CngExportPolicies.AllowExport)
+ {
+ DSA copy = DSA.Create();
+
+ copy.ImportEncryptedPkcs8PrivateKey(
+ nameof(MakeExportable),
+ dsa.ExportEncryptedPkcs8PrivateKey(
+ nameof(MakeExportable),
+ new PbeParameters(
+ PbeEncryptionAlgorithm.TripleDes3KeyPkcs12,
+ HashAlgorithmName.SHA1,
+ 2048)),
+ out _);
+ return copy;
+ }
+ }
+
+ return dsa;
+ }
+
+ internal static ECDsa MakeExportable(this ECDsa ecdsa)
+ {
+ if (ecdsa is ECDsaCng dsaCng)
+ {
+ const CngExportPolicies Exportability =
+ CngExportPolicies.AllowExport |
+ CngExportPolicies.AllowPlaintextExport;
+
+ if ((dsaCng.Key.ExportPolicy & Exportability) == CngExportPolicies.AllowExport)
+ {
+ ECDsa copy = ECDsa.Create();
+
+ copy.ImportEncryptedPkcs8PrivateKey(
+ nameof(MakeExportable),
+ ecdsa.ExportEncryptedPkcs8PrivateKey(
+ nameof(MakeExportable),
+ new PbeParameters(
+ PbeEncryptionAlgorithm.TripleDes3KeyPkcs12,
+ HashAlgorithmName.SHA1,
+ 2048)),
+ out _);
+ return copy;
+ }
+ }
+
+ return ecdsa;
+ }
}
}
Assert.Equal(Oids.DocumentDescription, cms.SignerInfos[0].UnsignedAttributes[0].Oid.Value);
}
+ [Fact]
+ public static void AddSigner_RSA_EphemeralKey()
+ {
+ using (RSA rsa = RSA.Create())
+ using (X509Certificate2 publicCertificate = Certificates.RSA2048SignatureOnly.GetCertificate())
+ using (X509Certificate2 certificateWithKey = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey(exportable: true))
+ {
+ if (certificateWithKey == null)
+ {
+ return;
+ }
+
+ using (RSA privateKey = certificateWithKey.GetRSAPrivateKey())
+ using (RSA exportableKey = privateKey.MakeExportable())
+ {
+ rsa.ImportParameters(exportableKey.ExportParameters(true));
+ }
+ using (X509Certificate2 certWithEphemeralKey = publicCertificate.CopyWithPrivateKey(rsa))
+ {
+ ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 });
+ SignedCms cms = new SignedCms(content, false);
+ CmsSigner signer = new CmsSigner(certWithEphemeralKey);
+ cms.ComputeSignature(signer);
+ }
+ }
+ }
+
+ [Fact]
+ public static void AddSigner_DSA_EphemeralKey()
+ {
+ using (DSA dsa = DSA.Create())
+ using (X509Certificate2 publicCertificate = Certificates.Dsa1024.GetCertificate())
+ using (X509Certificate2 certificateWithKey = Certificates.Dsa1024.TryGetCertificateWithPrivateKey(exportable: true))
+ {
+ if (certificateWithKey == null)
+ {
+ return;
+ }
+
+ using (DSA privateKey = certificateWithKey.GetDSAPrivateKey())
+ using (DSA exportableKey = privateKey.MakeExportable())
+ {
+ dsa.ImportParameters(exportableKey.ExportParameters(true));
+ }
+ using (X509Certificate2 certWithEphemeralKey = publicCertificate.CopyWithPrivateKey(dsa))
+ {
+ ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 });
+ SignedCms cms = new SignedCms(content, false);
+ CmsSigner signer = new CmsSigner(certWithEphemeralKey)
+ {
+ DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1)
+ };
+ cms.ComputeSignature(signer);
+ }
+ }
+ }
+
+ [Fact]
+ public static void AddSigner_ECDSA_EphemeralKey()
+ {
+ using (ECDsa ecdsa = ECDsa.Create())
+ using (X509Certificate2 publicCertificate = Certificates.ECDsaP256Win.GetCertificate())
+ using (X509Certificate2 certificateWithKey = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey(exportable: true))
+ {
+ if (certificateWithKey == null)
+ {
+ return;
+ }
+
+ using (ECDsa privateKey = certificateWithKey.GetECDsaPrivateKey())
+ using (ECDsa exportableKey = privateKey.MakeExportable())
+ {
+ ecdsa.ImportParameters(exportableKey.ExportParameters(true));
+ }
+ using (X509Certificate2 certWithEphemeralKey = publicCertificate.CopyWithPrivateKey(ecdsa))
+ {
+ ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 });
+ SignedCms cms = new SignedCms(content, false);
+ CmsSigner signer = new CmsSigner(certWithEphemeralKey);
+ cms.ComputeSignature(signer);
+ }
+ }
+ }
+
private static void VerifyWithExplicitPrivateKey(X509Certificate2 cert, AsymmetricAlgorithm key)
{
using (var pubCert = new X509Certificate2(cert.RawData))