Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 3bfd74c

Browse files
author
Atsushi Kanamori
committed
Implement System.Security.Cryptography.RSACng
1 parent c47d44a commit 3bfd74c

File tree

15 files changed

+1135
-50
lines changed

15 files changed

+1135
-50
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.Runtime.InteropServices;
7+
8+
9+
namespace Internal.Cryptography
10+
{
11+
/// <summary>
12+
/// Holds a managed string marshaled as a LPCWSTR.
13+
/// </summary>
14+
internal sealed class SafeUnicodeStringHandle : SafeHandle
15+
{
16+
/// <summary>
17+
/// Marshal a String to a native LPCWSTR. It is permitted to pass "null" for the string.
18+
/// </summary>
19+
public SafeUnicodeStringHandle(string s)
20+
: base(IntPtr.Zero, ownsHandle: true)
21+
{
22+
handle = Marshal.StringToHGlobalUni(s);
23+
}
24+
25+
public sealed override bool IsInvalid
26+
{
27+
get
28+
{
29+
return handle == IntPtr.Zero;
30+
}
31+
}
32+
33+
protected sealed override bool ReleaseHandle()
34+
{
35+
Marshal.FreeHGlobal(handle);
36+
return true;
37+
}
38+
}
39+
}
40+
41+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
/// <summary>
11+
/// BCrypt types related to asymmetric encryption algorithms
12+
/// </summary>
13+
internal partial class BCrypt
14+
{
15+
[StructLayout(LayoutKind.Sequential)]
16+
internal struct BCRYPT_OAEP_PADDING_INFO
17+
{
18+
/// <summary>
19+
/// Null-terminated Unicode string that identifies the hashing algorithm used to create the padding.
20+
/// </summary>
21+
internal IntPtr pszAlgId;
22+
23+
/// <summary>
24+
/// Address of a buffer that contains the data used to create the padding.
25+
/// </summary>
26+
internal IntPtr pbLabel;
27+
28+
/// <summary>
29+
/// Number of bytes in the pbLabel buffer.
30+
/// </summary>
31+
internal int cbLabel;
32+
}
33+
34+
[StructLayout(LayoutKind.Sequential)]
35+
internal struct BCRYPT_PKCS1_PADDING_INFO
36+
{
37+
/// <summary>
38+
/// Null-terminated Unicode string that identifies the hashing algorithm used to create the padding.
39+
/// </summary>
40+
internal IntPtr pszAlgId;
41+
}
42+
43+
[StructLayout(LayoutKind.Sequential)]
44+
internal struct BCRYPT_PSS_PADDING_INFO
45+
{
46+
/// <summary>
47+
/// Null-terminated Unicode string that identifies the hashing algorithm used to create the padding.
48+
/// </summary>
49+
internal IntPtr pszAlgId;
50+
51+
/// <summary>
52+
/// The size, in bytes, of the random salt to use for the padding.
53+
/// </summary>
54+
internal int cbSalt;
55+
}
56+
}
57+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
//
11+
// These structures define the layout of CNG key blobs passed to NCryptImportKey
12+
//
13+
internal partial class BCrypt
14+
{
15+
/// <summary>
16+
/// Magic numbers identifying blob types
17+
/// </summary>
18+
internal enum KeyBlobMagicNumber : int
19+
{
20+
BCRYPT_ECDH_PUBLIC_P256_MAGIC = 0x314B4345,
21+
BCRYPT_ECDH_PUBLIC_P384_MAGIC = 0x334B4345,
22+
BCRYPT_ECDH_PUBLIC_P521_MAGIC = 0x354B4345,
23+
BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345,
24+
BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345,
25+
BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345,
26+
BCRYPT_RSAPUBLIC_MAGIC = 0x31415352,
27+
BCRYPT_RSAPRIVATE_MAGIC = 0x32415352,
28+
BCRYPT_RSAFULLPRIVATE_MAGIC = 0x33415352,
29+
BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4d42444b,
30+
}
31+
32+
33+
/// <summary>
34+
/// Well known key blob types
35+
/// </summary>
36+
internal static class KeyBlobType
37+
{
38+
internal const string BCRYPT_RSAFULLPRIVATE_BLOB = "RSAFULLPRIVATEBLOB";
39+
internal const string BCRYPT_RSAPRIVATE_BLOB = "RSAPRIVATEBLOB";
40+
internal const string BCRYPT_PUBLIC_KEY_BLOB = "RSAPUBLICBLOB";
41+
}
42+
43+
44+
/// <summary>
45+
/// The BCRYPT_RSAKEY_BLOB structure is used as a header for an RSA public key or private key BLOB in memory.
46+
/// </summary>
47+
[StructLayout(LayoutKind.Sequential)]
48+
internal struct BCRYPT_RSAKEY_BLOB
49+
{
50+
internal KeyBlobMagicNumber Magic;
51+
internal int BitLength;
52+
internal int cbPublicExp;
53+
internal int cbModulus;
54+
internal int cbPrime1;
55+
internal int cbPrime2;
56+
}
57+
}
58+
}

src/System.Security.Cryptography.Cng/src/Interop/NCrypt/NCrypt.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ internal static partial class NCrypt
4444
[DllImport(Interop.Libraries.NCrypt, CharSet = CharSet.Unicode)]
4545
internal static extern ErrorCode NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags);
4646

47+
[DllImport(Interop.Libraries.NCrypt, CharSet = CharSet.Unicode)]
48+
internal static unsafe extern ErrorCode NCryptEncrypt(SafeNCryptKeyHandle hKey, [In] byte[] pbInput, int cbInput, void* pPaddingInfo, [Out] byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags);
49+
50+
[DllImport(Interop.Libraries.NCrypt, CharSet = CharSet.Unicode)]
51+
internal static unsafe extern ErrorCode NCryptDecrypt(SafeNCryptKeyHandle hKey, [In] byte[] pbInput, int cbInput, void* pPaddingInfo, [Out] byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags);
52+
53+
[DllImport(Interop.Libraries.NCrypt, CharSet = CharSet.Unicode)]
54+
internal static unsafe extern ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey, void* pPaddingInfo, [In] byte[] pbHashValue, int cbHashValue, [Out] byte[] pbSignature, int cbSignature, out int pcbResult, AsymmetricPaddingMode dwFlags);
55+
56+
[DllImport(Interop.Libraries.NCrypt, CharSet = CharSet.Unicode)]
57+
internal static unsafe extern ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey, void *pPaddingInfo, [In] byte[] pbHashValue, int cbHashValue, [In] byte[] pbSignature, int cbSignature, AsymmetricPaddingMode dwFlags);
58+
4759
/// <summary>
4860
/// Result codes from NCrypt APIs
4961
/// </summary>
@@ -68,5 +80,14 @@ internal struct NCRYPT_UI_POLICY
6880
public IntPtr pszFriendlyName;
6981
public IntPtr pszDescription;
7082
}
83+
84+
internal enum AsymmetricPaddingMode : int
85+
{
86+
NCRYPT_NO_PADDING_FLAG = 0x00000001,
87+
NCRYPT_PAD_PKCS1_FLAG = 0x00000002, // NCryptEncrypt/Decrypt or NCryptSignHash/VerifySignature
88+
NCRYPT_PAD_OAEP_FLAG = 0x00000004, // NCryptEncrypt/Decrypt
89+
NCRYPT_PAD_PSS_FLAG = 0x00000008, // NCryptSignHash/VerifySignature
90+
}
7191
}
72-
}
92+
}
93+

src/System.Security.Cryptography.Cng/src/Resources/Strings.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@
132132
<data name="Cryptography_ArgRSAaRequiresRSAKey" xml:space="preserve">
133133
<value>Keys used with the RSACng algorithm must have an algorithm group of RSA.</value>
134134
</data>
135+
<data name="Cryptography_HashAlgorithmNameNullOrEmpty" xml:space="preserve">
136+
<value>The hash algorithm name cannot be null or empty.</value>
137+
</data>
135138
<data name="Cryptography_InvalidAlgorithmGroup" xml:space="preserve">
136139
<value>The algorithm group '{0}' is invalid.</value>
137140
</data>
@@ -183,4 +186,7 @@
183186
<data name="WorkInProgress" xml:space="preserve">
184187
<value>WorkInProgress.</value>
185188
</data>
189+
<data name="WorkInProgress_UnsupportedHash" xml:space="preserve">
190+
<value>Unsupported hash algorithm. RSACng currently supports only MD5, SHA1, SHA256, SHA384 and SHA512.</value>
191+
</data>
186192
</root>

src/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,23 @@
5252
<Compile Include="System\Security\Cryptography\CngUIPolicy.cs" />
5353
<Compile Include="System\Security\Cryptography\CngUIProtectionLevels.cs" />
5454
<Compile Include="System\Security\Cryptography\RSACng.cs" />
55+
<Compile Include="System\Security\Cryptography\RSACng.EncryptDecrypt.cs" />
56+
<Compile Include="System\Security\Cryptography\RSACng.HashData.cs" />
57+
<Compile Include="System\Security\Cryptography\RSACng.ImportExport.cs" />
58+
<Compile Include="System\Security\Cryptography\RSACng.Key.cs" />
59+
<Compile Include="System\Security\Cryptography\RSACng.SignVerify.cs" />
5560

5661
<Compile Include="Microsoft\Win32\SafeHandles\NCryptSafeHandles.cs" />
5762

5863
<Compile Include="Internal\Cryptography\Helpers.cs" />
5964
<Compile Include="Internal\Cryptography\KeyPropertyName.cs" />
6065
<Compile Include="Internal\Cryptography\ProviderPropertyName.cs" />
66+
<Compile Include="Internal\Cryptography\SafeUnicodeStringHandle.cs" />
6167

6268
<Compile Include="Interop\Interop.Libraries.cs" />
6369
<Compile Include="Interop\NCrypt\NCrypt.cs" />
70+
<Compile Include="Interop\BCrypt\Interop.Blobs.cs" />
71+
<Compile Include="Interop\BCrypt\Interop.AsymmetricEncryption.Types.cs" />
6472

6573
</ItemGroup>
6674
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.IO;
7+
8+
using Microsoft.Win32.SafeHandles;
9+
10+
using Internal.Cryptography;
11+
12+
using ErrorCode = Interop.NCrypt.ErrorCode;
13+
using AsymmetricPaddingMode = Interop.NCrypt.AsymmetricPaddingMode;
14+
using BCRYPT_OAEP_PADDING_INFO = Interop.BCrypt.BCRYPT_OAEP_PADDING_INFO;
15+
16+
namespace System.Security.Cryptography
17+
{
18+
public sealed partial class RSACng : RSA
19+
{
20+
/// <summary>
21+
/// Encrypts data using the public key.
22+
/// </summary>
23+
public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
24+
{
25+
unsafe
26+
{
27+
return EncryptOrDecrypt(data, padding, Interop.NCrypt.NCryptEncrypt);
28+
}
29+
}
30+
31+
/// <summary>
32+
/// Decrypts data using the private key.
33+
/// </summary>
34+
public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
35+
{
36+
unsafe
37+
{
38+
return EncryptOrDecrypt(data, padding, Interop.NCrypt.NCryptDecrypt);
39+
}
40+
}
41+
42+
//
43+
// Conveniently, Encrypt() and Decrypt() are identical save for the actual P/Invoke call to CNG. Thus, both
44+
// api's invoke this common helper with the "transform" parameter determining whether encryption or decryption is done.
45+
//
46+
private byte[] EncryptOrDecrypt(byte[] data, RSAEncryptionPadding padding, EncryptOrDecryptAction encryptOrDecrypt)
47+
{
48+
if (data == null)
49+
throw new ArgumentNullException("data");
50+
51+
if (padding == null)
52+
throw new ArgumentNullException("padding");
53+
54+
unsafe
55+
{
56+
SafeNCryptKeyHandle keyHandle = Key.Handle;
57+
switch (padding.Mode)
58+
{
59+
case RSAEncryptionPaddingMode.Pkcs1:
60+
return EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_PKCS1_FLAG, null, encryptOrDecrypt);
61+
62+
case RSAEncryptionPaddingMode.Oaep:
63+
{
64+
using (SafeUnicodeStringHandle safeHashAlgorithmName = new SafeUnicodeStringHandle(padding.OaepHashAlgorithm.Name))
65+
{
66+
BCRYPT_OAEP_PADDING_INFO paddingInfo = new BCRYPT_OAEP_PADDING_INFO()
67+
{
68+
pszAlgId = safeHashAlgorithmName.DangerousGetHandle(),
69+
70+
// It would nice to put randomized data here but RSAEncryptionPadding does not at this point provide support for this.
71+
pbLabel = IntPtr.Zero,
72+
cbLabel = 0,
73+
};
74+
return EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_OAEP_FLAG, &paddingInfo, encryptOrDecrypt);
75+
}
76+
}
77+
78+
default:
79+
throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode);
80+
}
81+
}
82+
}
83+
84+
//
85+
// Now that the padding mode and information have been marshaled to their native counterparts, perform the encryption or decryption.
86+
//
87+
private static unsafe byte[] EncryptOrDecrypt(SafeNCryptKeyHandle key, byte[] input, AsymmetricPaddingMode paddingMode, void* paddingInfo, EncryptOrDecryptAction encryptOrDecrypt)
88+
{
89+
int numBytesNeeded;
90+
ErrorCode errorCode = encryptOrDecrypt(key, input, input.Length, paddingInfo, null, 0, out numBytesNeeded, paddingMode);
91+
if (errorCode != ErrorCode.ERROR_SUCCESS)
92+
throw errorCode.ToCryptographicException();
93+
94+
byte[] output = new byte[numBytesNeeded];
95+
errorCode = encryptOrDecrypt(key, input, input.Length, paddingInfo, output, numBytesNeeded, out numBytesNeeded, paddingMode);
96+
if (errorCode != ErrorCode.ERROR_SUCCESS)
97+
throw errorCode.ToCryptographicException();
98+
99+
return output;
100+
}
101+
102+
// Delegate binds to either NCryptEncrypt() or NCryptDecrypt() depending on which api was called.
103+
private unsafe delegate ErrorCode EncryptOrDecryptAction(SafeNCryptKeyHandle hKey, byte[] pbInput, int cbInput, void* pPaddingInfo, byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags);
104+
}
105+
}
106+

0 commit comments

Comments
 (0)