mirrored from https://www.bouncycastle.org/repositories/bc-csharp
-
Notifications
You must be signed in to change notification settings - Fork 602
Expand file tree
/
Copy pathDotNetUtilities.cs
More file actions
442 lines (398 loc) · 19.9 KB
/
Copy pathDotNetUtilities.cs
File metadata and controls
442 lines (398 loc) · 19.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
using System;
#if NET5_0_OR_GREATER
using System.Runtime.Versioning;
#endif
using System.Security.Cryptography;
using SystemX509 = System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509;
#if NETCOREAPP1_0_OR_GREATER || NET47_OR_GREATER || NETSTANDARD1_6_OR_GREATER
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.EC;
#endif
namespace Org.BouncyCastle.Security
{
/// <summary>
/// A class containing methods to interface the BouncyCastle world to the .NET Crypto world.
/// </summary>
public static class DotNetUtilities
{
/// <summary>
/// Create an System.Security.Cryptography.X509Certificate from an X509CertificateStructure.
/// </summary>
/// <param name="x509Struct"></param>
/// <returns>A System.Security.Cryptography.X509Certificate.</returns>
// TODO[api] Change return type to X509Certificate2
#if NET5_0_OR_GREATER
[UnsupportedOSPlatform("browser")]
#endif
public static SystemX509.X509Certificate ToX509Certificate(X509CertificateStructure x509Struct)
{
byte[] data = x509Struct.GetEncoded(Asn1Encodable.Der);
#if NET9_0_OR_GREATER
return SystemX509.X509CertificateLoader.LoadCertificate(data);
#else
return new SystemX509.X509Certificate2(data);
#endif
}
/// <summary>
/// Create an System.Security.Cryptography.X509Certificate from an X509Certificate.
/// </summary>
/// <param name="x509Cert"></param>
/// <returns>A System.Security.Cryptography.X509Certificate.</returns>
// TODO[api] Change return type to X509Certificate2
#if NET5_0_OR_GREATER
[UnsupportedOSPlatform("browser")]
#endif
public static SystemX509.X509Certificate ToX509Certificate(X509Certificate x509Cert) =>
ToX509Certificate(x509Cert.CertificateStructure);
/// <summary>
/// Create a Bouncy Castle <see cref="X509Certificate"/> from a .NET <see cref="SystemX509.X509Certificate"/>.
/// </summary>
/// <param name="x509Cert">The .NET certificate.</param>
/// <returns>A Bouncy Castle <see cref="X509Certificate"/>.</returns>
public static X509Certificate FromX509Certificate(SystemX509.X509Certificate x509Cert) =>
new X509Certificate(x509Cert.GetRawCertData());
/// <summary>
/// Create a Bouncy Castle <see cref="X509Certificate"/> from a .NET <see cref="SystemX509.X509Certificate2"/>.
/// </summary>
/// <param name="x509Cert">The .NET certificate.</param>
/// <returns>A Bouncy Castle <see cref="X509Certificate"/>.</returns>
public static X509Certificate FromX509Certificate(SystemX509.X509Certificate2 x509Cert) =>
new X509Certificate(x509Cert.RawData);
/// <summary>
/// Extract the <see cref="SubjectPublicKeyInfo"/> (an X.509 ASN.1 type used for public keys) from a .NET
/// <see cref="SystemX509.X509Certificate2"/>.
/// </summary>
/// <param name="certificate">The .NET certificate.</param>
/// <returns>A <see cref="SubjectPublicKeyInfo"/> object.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="certificate"/> is null.</exception>
public static SubjectPublicKeyInfo GetSubjectPublicKeyInfo(SystemX509.X509Certificate2 certificate)
{
if (certificate == null)
throw new ArgumentNullException(nameof(certificate));
#if NET6_0_OR_GREATER
return SubjectPublicKeyInfo.GetInstance(certificate.PublicKey.ExportSubjectPublicKeyInfo());
#else
var bcCert = FromX509Certificate(certificate);
return SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(bcCert.GetPublicKey());
#endif
}
/// <summary>
/// Extract the DER-encoded <see cref="SubjectPublicKeyInfo"/> bytes from a .NET
/// <see cref="SystemX509.X509Certificate2"/>.
/// </summary>
/// <param name="certificate">The .NET certificate.</param>
/// <returns>A byte array containing the DER-encoded public key info.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="certificate"/> is null.</exception>
public static byte[] GetSubjectPublicKeyInfoDer(SystemX509.X509Certificate2 certificate)
{
if (certificate == null)
throw new ArgumentNullException(nameof(certificate));
#if NET6_0_OR_GREATER
return certificate.PublicKey.ExportSubjectPublicKeyInfo();
#else
return GetSubjectPublicKeyInfo(certificate).GetEncoded(Asn1Encodable.Der);
#endif
}
/// <summary>
/// Extract a DSA key pair from a .NET <see cref="DSA"/> object.
/// </summary>
/// <param name="dsa">The .NET DSA object.</param>
/// <returns>An <see cref="AsymmetricCipherKeyPair"/> containing the BC DSA keys.</returns>
public static AsymmetricCipherKeyPair GetDsaKeyPair(DSA dsa) => GetDsaKeyPair(dsa.ExportParameters(true));
/// <summary>
/// Extract a DSA key pair from <see cref="DSAParameters"/>.
/// </summary>
/// <param name="dp">The .NET DSA parameters.</param>
/// <returns>An <see cref="AsymmetricCipherKeyPair"/> containing the BC DSA keys.</returns>
public static AsymmetricCipherKeyPair GetDsaKeyPair(DSAParameters dp)
{
var publicKey = GetDsaPublicKey(dp);
var privateKey = new DsaPrivateKeyParameters(BigNat(dp.X), publicKey.Parameters);
return new AsymmetricCipherKeyPair(publicKey, privateKey);
}
/// <summary>
/// Extract DSA public key parameters from a .NET <see cref="DSA"/> object.
/// </summary>
/// <param name="dsa">The .NET DSA object.</param>
/// <returns>A <see cref="DsaPublicKeyParameters"/> object.</returns>
public static DsaPublicKeyParameters GetDsaPublicKey(DSA dsa) => GetDsaPublicKey(dsa.ExportParameters(false));
/// <summary>
/// Extract DSA public key parameters from <see cref="DSAParameters"/>.
/// </summary>
/// <param name="dp">The .NET DSA parameters.</param>
/// <returns>A <see cref="DsaPublicKeyParameters"/> object.</returns>
public static DsaPublicKeyParameters GetDsaPublicKey(DSAParameters dp)
{
var validationParameters = (dp.Seed != null)
? new DsaValidationParameters(dp.Seed, dp.Counter)
: null;
var parameters = new DsaParameters(BigNat(dp.P), BigNat(dp.Q), BigNat(dp.G), validationParameters);
return new DsaPublicKeyParameters(BigNat(dp.Y), parameters);
}
#if NETCOREAPP1_0_OR_GREATER || NET47_OR_GREATER || NETSTANDARD1_6_OR_GREATER
/// <summary>
/// Extract an EC key pair from a .NET <see cref="ECDsa"/> object.
/// </summary>
/// <param name="ecDsa">The .NET ECDsa object.</param>
/// <returns>An <see cref="AsymmetricCipherKeyPair"/> containing the BC EC keys.</returns>
public static AsymmetricCipherKeyPair GetECDsaKeyPair(ECDsa ecDsa) =>
GetECKeyPair("ECDSA", ecDsa.ExportParameters(true));
/// <summary>
/// Extract EC public key parameters from a .NET <see cref="ECDsa"/> object.
/// </summary>
/// <param name="ecDsa">The .NET ECDsa object.</param>
/// <returns>An <see cref="ECPublicKeyParameters"/> object.</returns>
public static ECPublicKeyParameters GetECDsaPublicKey(ECDsa ecDsa) =>
GetECPublicKey("ECDSA", ecDsa.ExportParameters(false));
/// <summary>
/// Extract an EC key pair from <see cref="ECParameters"/>.
/// </summary>
/// <param name="algorithm">The algorithm name (e.g., "ECDSA").</param>
/// <param name="ec">The .NET EC parameters.</param>
/// <returns>An <see cref="AsymmetricCipherKeyPair"/> containing the BC EC keys.</returns>
public static AsymmetricCipherKeyPair GetECKeyPair(string algorithm, ECParameters ec)
{
var publicKey = GetECPublicKey(algorithm, ec);
var privateKey = new ECPrivateKeyParameters(publicKey.AlgorithmName, BigNat(ec.D), publicKey.Parameters);
return new AsymmetricCipherKeyPair(publicKey, privateKey);
}
/// <summary>
/// Extract EC public key parameters from <see cref="ECParameters"/>.
/// </summary>
/// <param name="algorithm">The algorithm name (e.g., "ECDSA").</param>
/// <param name="ec">The .NET EC parameters.</param>
/// <returns>An <see cref="ECPublicKeyParameters"/> object.</returns>
public static ECPublicKeyParameters GetECPublicKey(string algorithm, ECParameters ec)
{
var x9 = GetX9ECParameters(ec.Curve) ?? throw new NotSupportedException("Unrecognized curve");
var q = GetECPoint(x9.Curve, ec.Q);
var parameters = ECDomainParameters.FromX9ECParameters(x9);
return new ECPublicKeyParameters(algorithm, q, parameters);
}
private static Math.EC.ECPoint GetECPoint(Math.EC.ECCurve curve, ECPoint point) =>
curve.CreatePoint(BigNat(point.X), BigNat(point.Y));
private static X9ECParameters GetX9ECParameters(ECCurve curve)
{
if (!curve.IsNamed)
throw new NotSupportedException("Only named curves are supported");
Oid oid = curve.Oid;
if (oid != null)
{
string oidValue = oid.Value;
if (oidValue != null && DerObjectIdentifier.TryFromID(oidValue, out var bcOid))
return ECUtilities.FindECCurveByOid(bcOid);
}
return null;
}
#endif
/// <summary>
/// Extract an RSA key pair from a .NET <see cref="RSA"/> object.
/// </summary>
/// <param name="rsa">The .NET RSA object.</param>
/// <returns>An <see cref="AsymmetricCipherKeyPair"/> containing the BC RSA keys.</returns>
public static AsymmetricCipherKeyPair GetRsaKeyPair(RSA rsa) => GetRsaKeyPair(rsa.ExportParameters(true));
/// <summary>
/// Extract an RSA key pair from <see cref="RSAParameters"/>.
/// </summary>
/// <param name="rp">The .NET RSA parameters.</param>
/// <returns>An <see cref="AsymmetricCipherKeyPair"/> containing the BC RSA keys.</returns>
public static AsymmetricCipherKeyPair GetRsaKeyPair(RSAParameters rp)
{
var publicKey = GetRsaPublicKey(rp);
var privateKey = new RsaPrivateCrtKeyParameters(
publicKey.Modulus,
publicKey.Exponent,
BigNat(rp.D),
BigNat(rp.P),
BigNat(rp.Q),
BigNat(rp.DP),
BigNat(rp.DQ),
BigNat(rp.InverseQ));
return new AsymmetricCipherKeyPair(publicKey, privateKey);
}
/// <summary>
/// Extract RSA public key parameters from a .NET <see cref="RSA"/> object.
/// </summary>
/// <param name="rsa">The .NET RSA object.</param>
/// <returns>An <see cref="RsaKeyParameters"/> object.</returns>
public static RsaKeyParameters GetRsaPublicKey(RSA rsa) => GetRsaPublicKey(rsa.ExportParameters(false));
/// <summary>
/// Extract RSA public key parameters from <see cref="RSAParameters"/>.
/// </summary>
/// <param name="rp">The .NET RSA parameters.</param>
/// <returns>An <see cref="RsaKeyParameters"/> object.</returns>
public static RsaKeyParameters GetRsaPublicKey(RSAParameters rp) =>
new RsaKeyParameters(false, BigNat(rp.Modulus), BigNat(rp.Exponent));
/// <summary>
/// Extract an asymmetric key pair from a .NET <see cref="AsymmetricAlgorithm"/> object.
/// </summary>
/// <param name="privateKey">The .NET private key object.</param>
/// <returns>An <see cref="AsymmetricCipherKeyPair"/> containing the BC keys.</returns>
/// <exception cref="ArgumentException">If the algorithm is not supported.</exception>
public static AsymmetricCipherKeyPair GetKeyPair(AsymmetricAlgorithm privateKey)
{
if (privateKey is DSA dsa)
return GetDsaKeyPair(dsa);
#if NETCOREAPP1_0_OR_GREATER || NET47_OR_GREATER || NETSTANDARD1_6_OR_GREATER
if (privateKey is ECDsa ecDsa)
return GetECDsaKeyPair(ecDsa);
#endif
if (privateKey is RSA rsa)
return GetRsaKeyPair(rsa);
throw new ArgumentException("Unsupported algorithm specified", nameof(privateKey));
}
/// <summary>
/// Create a .NET <see cref="RSA"/> instance from Bouncy Castle RSA public key parameters.
/// </summary>
/// <param name="rsaKey">The BC RSA public key.</param>
/// <returns>A .NET <see cref="RSA"/> instance.</returns>
// TODO This appears to not work for private keys (when no CRT info)
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
public static RSA ToRSA(RsaKeyParameters rsaKey) => CreateRSAProvider(ToRSAParameters(rsaKey));
/// <summary>
/// Create a .NET <see cref="RSA"/> instance from Bouncy Castle RSA public key parameters.
/// </summary>
/// <param name="rsaKey">The BC RSA public key.</param>
/// <param name="csp">The .NET CspParameters.</param>
/// <returns>A .NET <see cref="RSA"/> instance.</returns>
// TODO This appears to not work for private keys (when no CRT info)
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
public static RSA ToRSA(RsaKeyParameters rsaKey, CspParameters csp) =>
CreateRSAProvider(ToRSAParameters(rsaKey), csp);
/// <summary>
/// Create a .NET <see cref="RSA"/> instance from Bouncy Castle RSA private CRT parameters.
/// </summary>
/// <param name="privKey">The BC RSA private CRT keys.</param>
/// <returns>A .NET <see cref="RSA"/> instance.</returns>
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey) => CreateRSAProvider(ToRSAParameters(privKey));
/// <summary>
/// Create a .NET <see cref="RSA"/> instance from Bouncy Castle RSA private CRT parameters and CSP info.
/// </summary>
/// <param name="privKey">The BC RSA private CRT keys.</param>
/// <param name="csp">The .NET CspParameters.</param>
/// <returns>A .NET <see cref="RSA"/> instance.</returns>
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey, CspParameters csp) =>
CreateRSAProvider(ToRSAParameters(privKey), csp);
/// <summary>
/// Create a .NET <see cref="RSA"/> instance from Bouncy Castle RSA private CRT structure.
/// </summary>
/// <param name="privKey">The BC RSA private CRT keys.</param>
/// <returns>A .NET <see cref="RSA"/> instance.</returns>
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
public static RSA ToRSA(RsaPrivateKeyStructure privKey) => CreateRSAProvider(ToRSAParameters(privKey));
/// <summary>
/// Create a .NET <see cref="RSA"/> instance from Bouncy Castle RSA private CRT structure and CSP info.
/// </summary>
/// <param name="privKey">The BC RSA private CRT keys.</param>
/// <param name="csp">The .NET CspParameters.</param>
/// <returns>A .NET <see cref="RSA"/> instance.</returns>
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
public static RSA ToRSA(RsaPrivateKeyStructure privKey, CspParameters csp) =>
CreateRSAProvider(ToRSAParameters(privKey), csp);
/// <summary>
/// Convert Bouncy Castle RSA public key parameters to .NET <see cref="RSAParameters"/>.
/// </summary>
/// <param name="rsaKey">The BC RSA key.</param>
/// <returns>A .NET <see cref="RSAParameters"/> object.</returns>
public static RSAParameters ToRSAParameters(RsaKeyParameters rsaKey)
{
RSAParameters rp = new RSAParameters();
rp.Modulus = rsaKey.Modulus.ToByteArrayUnsigned();
if (rsaKey.IsPrivate)
rp.D = ConvertRSAParametersField(rsaKey.Exponent, rp.Modulus.Length);
else
rp.Exponent = rsaKey.Exponent.ToByteArrayUnsigned();
return rp;
}
/// <summary>
/// Convert Bouncy Castle RSA private CRT parameters to .NET <see cref="RSAParameters"/>.
/// </summary>
/// <param name="privKey">The BC RSA key.</param>
/// <returns>A .NET <see cref="RSAParameters"/> object.</returns>
public static RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey)
{
RSAParameters rp = new RSAParameters();
rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
rp.P = privKey.P.ToByteArrayUnsigned();
rp.Q = privKey.Q.ToByteArrayUnsigned();
rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length);
rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length);
rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length);
rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length);
return rp;
}
/// <summary>
/// Convert Bouncy Castle RSA private CRT structure to .NET <see cref="RSAParameters"/>.
/// </summary>
/// <param name="privKey">The BC RSA key.</param>
/// <returns>A .NET <see cref="RSAParameters"/> object.</returns>
public static RSAParameters ToRSAParameters(RsaPrivateKeyStructure privKey)
{
RSAParameters rp = new RSAParameters();
rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
rp.P = privKey.Prime1.ToByteArrayUnsigned();
rp.Q = privKey.Prime2.ToByteArrayUnsigned();
rp.D = ConvertRSAParametersField(privKey.PrivateExponent, rp.Modulus.Length);
rp.DP = ConvertRSAParametersField(privKey.Exponent1, rp.P.Length);
rp.DQ = ConvertRSAParametersField(privKey.Exponent2, rp.Q.Length);
rp.InverseQ = ConvertRSAParametersField(privKey.Coefficient, rp.Q.Length);
return rp;
}
private static byte[] ConvertRSAParametersField(BigInteger n, int size) =>
BigIntegers.AsUnsignedByteArray(size, n);
// TODO Why do we use CspParameters instead of just RSA.Create in methods below?
// private static RSA CreateRSA(RSAParameters rp)
// {
//#if NETCOREAPP2_0_OR_GREATER || NET472_OR_GREATER || NETSTANDARD2_1_OR_GREATER
// return RSA.Create(rp);
//#else
// var rsa = RSA.Create();
// rsa.ImportParameters(rp);
// return rsa;
//#endif
// }
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
private static RSACryptoServiceProvider CreateRSAProvider(RSAParameters rp)
{
CspParameters csp = new CspParameters();
csp.KeyContainerName = string.Format("BouncyCastle-{0}", Guid.NewGuid());
return CreateRSAProvider(rp, csp);
}
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
private static RSACryptoServiceProvider CreateRSAProvider(RSAParameters rp, CspParameters csp)
{
RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(csp);
rsaCsp.ImportParameters(rp);
return rsaCsp;
}
private static BigInteger BigNat(byte[] data) => new BigInteger(1, data);
}
}