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

Commit 98576a8

Browse files
committed
Merge pull request #1966 from bartonjs/add-cryptography-rsa
Add System.Security.Cryptography.RSA Source and Tests
2 parents 4488cec + 670332a commit 98576a8

35 files changed

+8413
-0
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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.Runtime.InteropServices;
6+
7+
using Microsoft.Win32.SafeHandles;
8+
9+
internal static partial class Interop
10+
{
11+
internal static partial class libcrypto
12+
{
13+
[DllImport(Libraries.LibCrypto)]
14+
internal static extern void BN_clear_free(IntPtr a);
15+
16+
[DllImport(Libraries.LibCrypto)]
17+
private static extern IntPtr BN_bin2bn(byte[] s, int len, IntPtr zero);
18+
19+
[DllImport(Libraries.LibCrypto)]
20+
private static extern unsafe int BN_bn2bin(SafeBignumHandle a, byte* to);
21+
22+
[DllImport(Libraries.LibCrypto)]
23+
private static extern int BN_num_bits(SafeBignumHandle a);
24+
25+
/// <summary>
26+
/// Returns the number of bytes needed to export a BIGNUM.
27+
/// </summary>
28+
/// <remarks>This is a macro in bn.h, expanded here.</remarks>
29+
private static int BN_num_bytes(SafeBignumHandle a)
30+
{
31+
return (BN_num_bits(a) + 7) / 8;
32+
}
33+
34+
internal static IntPtr CreateBignumPtr(byte[] bigEndianValue)
35+
{
36+
if (bigEndianValue == null)
37+
{
38+
return IntPtr.Zero;
39+
}
40+
41+
IntPtr handle = BN_bin2bn(bigEndianValue, bigEndianValue.Length, IntPtr.Zero);
42+
return handle;
43+
}
44+
45+
internal static SafeBignumHandle CreateBignum(byte[] bigEndianValue)
46+
{
47+
IntPtr handle = CreateBignumPtr(bigEndianValue);
48+
return new SafeBignumHandle(handle, true);
49+
}
50+
51+
private static byte[] ExtractBignum(IntPtr bignum, int targetSize)
52+
{
53+
// Given that the only reference held to bignum is an IntPtr, create an unowned SafeHandle
54+
// to ensure that we don't destroy the key after extraction.
55+
using (SafeBignumHandle handle = new SafeBignumHandle(bignum, ownsHandle: false))
56+
{
57+
return ExtractBignum(handle, targetSize);
58+
}
59+
}
60+
61+
private static unsafe byte[] ExtractBignum(SafeBignumHandle bignum, int targetSize)
62+
{
63+
if (bignum == null || bignum.IsInvalid)
64+
{
65+
return null;
66+
}
67+
68+
int compactSize = BN_num_bytes(bignum);
69+
70+
if (targetSize < compactSize)
71+
{
72+
targetSize = compactSize;
73+
}
74+
75+
// OpenSSL BIGNUM values do not record leading zeroes.
76+
// Windows Crypt32 does.
77+
//
78+
// Since RSACryptoServiceProvider already checks that RSAParameters.DP.Length is
79+
// exactly half of RSAParameters.Modulus.Length, we need to left-pad (big-endian)
80+
// the array with zeroes.
81+
int offset = targetSize - compactSize;
82+
83+
byte[] buf = new byte[targetSize];
84+
85+
fixed (byte* to = buf)
86+
{
87+
byte* start = to + offset;
88+
BN_bn2bin(bignum, start);
89+
}
90+
91+
return buf;
92+
}
93+
}
94+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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+
using System.Security.Cryptography;
8+
9+
using Microsoft.Win32.SafeHandles;
10+
11+
// On Linux and MacOS sizeof(long)==sizeof(void*) across x86/x64
12+
using NativeLong=System.IntPtr;
13+
14+
internal static partial class Interop
15+
{
16+
internal static partial class libcrypto
17+
{
18+
[DllImport(Libraries.LibCrypto)]
19+
internal static extern SafeRsaHandle RSA_new();
20+
21+
[DllImport(Libraries.LibCrypto)]
22+
internal static unsafe extern SafeRsaHandle d2i_RSAPublicKey(IntPtr zero, byte** ppin, int len);
23+
24+
[DllImport(Libraries.LibCrypto)]
25+
internal static extern void RSA_free(IntPtr rsa);
26+
27+
[DllImport(Libraries.LibCrypto)]
28+
internal extern static int RSA_public_encrypt(int flen, byte[] from, byte[] to, SafeRsaHandle rsa, OpenSslRsaPadding padding);
29+
30+
[DllImport(Libraries.LibCrypto)]
31+
internal extern static int RSA_private_decrypt(int flen, byte[] from, byte[] to, SafeRsaHandle rsa, OpenSslRsaPadding padding);
32+
33+
[DllImport(Libraries.LibCrypto)]
34+
internal static extern int RSA_size(SafeRsaHandle rsa);
35+
36+
[DllImport(Libraries.LibCrypto)]
37+
internal static extern int RSA_generate_key_ex(SafeRsaHandle rsa, int bits, SafeBignumHandle e, IntPtr zero);
38+
39+
internal static unsafe RSAParameters ExportRsaParameters(SafeRsaHandle key, bool includePrivateParameters)
40+
{
41+
Debug.Assert(
42+
key != null && !key.IsInvalid,
43+
"Callers should check the key is invalid and throw an exception with a message");
44+
45+
if (key == null || key.IsInvalid)
46+
{
47+
throw new CryptographicException();
48+
}
49+
50+
RSAParameters rsaParameters;
51+
bool addedRef = false;
52+
53+
try
54+
{
55+
key.DangerousAddRef(ref addedRef);
56+
RSA_ST* rsaStructure = (RSA_ST*)key.DangerousGetHandle();
57+
58+
int modulusSize = RSA_size(key);
59+
60+
// RSACryptoServiceProvider expects P, DP, Q, DQ, and InverseQ to all
61+
// be padded up to half the modulus size.
62+
int halfModulus = modulusSize / 2;
63+
64+
rsaParameters = new RSAParameters
65+
{
66+
Modulus = ExtractBignum(rsaStructure->n, modulusSize),
67+
Exponent = ExtractBignum(rsaStructure->e, 0),
68+
};
69+
70+
if (includePrivateParameters)
71+
{
72+
rsaParameters.D = ExtractBignum(rsaStructure->d, modulusSize);
73+
rsaParameters.P = ExtractBignum(rsaStructure->p, halfModulus);
74+
rsaParameters.DP = ExtractBignum(rsaStructure->dmp1, halfModulus);
75+
rsaParameters.Q = ExtractBignum(rsaStructure->q, halfModulus);
76+
rsaParameters.DQ = ExtractBignum(rsaStructure->dmq1, halfModulus);
77+
rsaParameters.InverseQ = ExtractBignum(rsaStructure->iqmp, halfModulus);
78+
}
79+
}
80+
finally
81+
{
82+
if (addedRef)
83+
{
84+
key.DangerousRelease();
85+
}
86+
}
87+
88+
return rsaParameters;
89+
}
90+
91+
internal enum OpenSslRsaPadding
92+
{
93+
Invalid,
94+
RSA_PKCS1_PADDING = 1,
95+
RSA_SSLV23_PADDING = 2,
96+
RSA_NO_PADDING = 3,
97+
RSA_PKCS1_OAEP_PADDING = 4,
98+
RSA_X931_PADDING = 5,
99+
RSA_PKCS1_PSS_PADDING = 6,
100+
}
101+
102+
[StructLayout(LayoutKind.Sequential)]
103+
internal struct RSA_ST
104+
{
105+
private int pad;
106+
private NativeLong version;
107+
private IntPtr meth;
108+
private IntPtr engine;
109+
public IntPtr n;
110+
public IntPtr e;
111+
public IntPtr d;
112+
public IntPtr p;
113+
public IntPtr q;
114+
public IntPtr dmp1;
115+
public IntPtr dmq1;
116+
public IntPtr iqmp;
117+
}
118+
}
119+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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.Runtime.InteropServices;
6+
using System.Security;
7+
8+
namespace Microsoft.Win32.SafeHandles
9+
{
10+
[SecurityCritical]
11+
internal sealed class SafeBignumHandle : SafeHandle
12+
{
13+
internal SafeBignumHandle(IntPtr handle, bool ownsHandle)
14+
: base(handle, ownsHandle)
15+
{
16+
}
17+
18+
protected override bool ReleaseHandle()
19+
{
20+
Interop.libcrypto.BN_clear_free(handle);
21+
SetHandle(IntPtr.Zero);
22+
return true;
23+
}
24+
25+
public override bool IsInvalid
26+
{
27+
get { return handle == IntPtr.Zero; }
28+
}
29+
}
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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.Security;
6+
using System.Runtime.InteropServices;
7+
8+
namespace Microsoft.Win32.SafeHandles
9+
{
10+
[SecurityCritical]
11+
internal sealed class SafeRsaHandle : SafeHandle
12+
{
13+
private SafeRsaHandle() :
14+
base(IntPtr.Zero, ownsHandle: true)
15+
{
16+
}
17+
18+
protected override bool ReleaseHandle()
19+
{
20+
Interop.libcrypto.RSA_free(handle);
21+
SetHandle(IntPtr.Zero);
22+
return true;
23+
}
24+
25+
public override bool IsInvalid
26+
{
27+
get { return handle == IntPtr.Zero; }
28+
}
29+
}
30+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 14
4+
VisualStudioVersion = 14.0.22911.2
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Security.Cryptography.RSA", "src\System.Security.Cryptography.RSA.csproj", "{51869113-2C21-4CD4-AA71-3F421E586E40}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Security.Cryptography.RSA.Tests", "tests\System.Security.Cryptography.RSA.Tests.csproj", "{332AC4A4-99DF-4E12-84C5-F5298A2BF241}"
9+
EndProject
10+
Global
11+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12+
Linux_Debug|Any CPU = Linux_Debug|Any CPU
13+
Linux_Release|Any CPU = Linux_Release|Any CPU
14+
OSX_Debug|Any CPU = OSX_Debug|Any CPU
15+
OSX_Release|Any CPU = OSX_Release|Any CPU
16+
Windows_Debug|Any CPU = Windows_Debug|Any CPU
17+
Windows_Release|Any CPU = Windows_Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{51869113-2C21-4CD4-AA71-3F421E586E40}.Linux_Debug|Any CPU.ActiveCfg = Linux_Debug|Any CPU
21+
{51869113-2C21-4CD4-AA71-3F421E586E40}.Linux_Debug|Any CPU.Build.0 = Linux_Debug|Any CPU
22+
{51869113-2C21-4CD4-AA71-3F421E586E40}.Linux_Release|Any CPU.ActiveCfg = Linux_Release|Any CPU
23+
{51869113-2C21-4CD4-AA71-3F421E586E40}.Linux_Release|Any CPU.Build.0 = Linux_Release|Any CPU
24+
{51869113-2C21-4CD4-AA71-3F421E586E40}.OSX_Debug|Any CPU.ActiveCfg = OSX_Debug|Any CPU
25+
{51869113-2C21-4CD4-AA71-3F421E586E40}.OSX_Debug|Any CPU.Build.0 = OSX_Debug|Any CPU
26+
{51869113-2C21-4CD4-AA71-3F421E586E40}.OSX_Release|Any CPU.ActiveCfg = OSX_Release|Any CPU
27+
{51869113-2C21-4CD4-AA71-3F421E586E40}.OSX_Release|Any CPU.Build.0 = OSX_Release|Any CPU
28+
{51869113-2C21-4CD4-AA71-3F421E586E40}.Windows_Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU
29+
{51869113-2C21-4CD4-AA71-3F421E586E40}.Windows_Debug|Any CPU.Build.0 = Windows_Debug|Any CPU
30+
{51869113-2C21-4CD4-AA71-3F421E586E40}.Windows_Release|Any CPU.ActiveCfg = Windows_Release|Any CPU
31+
{51869113-2C21-4CD4-AA71-3F421E586E40}.Windows_Release|Any CPU.Build.0 = Windows_Release|Any CPU
32+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.Linux_Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.Linux_Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.Linux_Release|Any CPU.ActiveCfg = Debug|Any CPU
35+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.Linux_Release|Any CPU.Build.0 = Debug|Any CPU
36+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.OSX_Debug|Any CPU.ActiveCfg = Debug|Any CPU
37+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.OSX_Debug|Any CPU.Build.0 = Debug|Any CPU
38+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.OSX_Release|Any CPU.ActiveCfg = Debug|Any CPU
39+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.OSX_Release|Any CPU.Build.0 = Debug|Any CPU
40+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.Windows_Debug|Any CPU.ActiveCfg = Debug|Any CPU
41+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.Windows_Debug|Any CPU.Build.0 = Debug|Any CPU
42+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.Windows_Release|Any CPU.ActiveCfg = Debug|Any CPU
43+
{332AC4A4-99DF-4E12-84C5-F5298A2BF241}.Windows_Release|Any CPU.Build.0 = Debug|Any CPU
44+
EndGlobalSection
45+
GlobalSection(SolutionProperties) = preSolution
46+
HideSolutionNode = FALSE
47+
EndGlobalSection
48+
EndGlobal
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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.Security.Cryptography;
7+
8+
namespace Internal.Cryptography
9+
{
10+
internal static class Helpers
11+
{
12+
public static byte[] CloneByteArray(this byte[] src)
13+
{
14+
return (byte[])(src.Clone());
15+
}
16+
}
17+
}
18+
19+

0 commit comments

Comments
 (0)