Skip to content

Commit f9908a2

Browse files
authored
Use System.Security.Cryptography for RSA (#1373)
* Use BCL for RSA * Restore benchmark (with quirks for old code) * Fixup benchmark (remove compatibility with old code) * cosmetic tweaks * Add a regression test for #1388
1 parent 749a0e0 commit f9908a2

File tree

10 files changed

+466
-349
lines changed

10 files changed

+466
-349
lines changed

src/Renci.SshNet/Common/Extensions.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,41 @@ public static BigInteger ToBigInteger2(this byte[] data)
6464
return data.ToBigInteger();
6565
}
6666

67+
public static byte[] ToByteArray(this BigInteger bigInt, bool isUnsigned = false, bool isBigEndian = false)
68+
{
69+
var data = bigInt.ToByteArray();
70+
71+
if (isUnsigned && data[data.Length - 1] == 0)
72+
{
73+
data = data.Take(data.Length - 1);
74+
}
75+
76+
if (isBigEndian)
77+
{
78+
_ = data.Reverse();
79+
}
80+
81+
return data;
82+
}
83+
84+
// See https://github.com/dotnet/runtime/blob/9b57a265c7efd3732b035bade005561a04767128/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs#L51
85+
public static byte[] ExportKeyParameter(this BigInteger value, int length)
86+
{
87+
var target = value.ToByteArray(isUnsigned: true, isBigEndian: true);
88+
89+
// The BCL crypto is expecting exactly-sized byte arrays (sized to "length").
90+
// If our byte array is smaller than required, then size it up.
91+
// Otherwise, just return as is: if it is too large, we'll let the BCL throw the error.
92+
if (target.Length < length)
93+
{
94+
var correctlySized = new byte[length];
95+
Buffer.BlockCopy(target, 0, correctlySized, length - target.Length, target.Length);
96+
return correctlySized;
97+
}
98+
99+
return target;
100+
}
101+
67102
/// <summary>
68103
/// Reverses the sequence of the elements in the entire one-dimensional <see cref="Array"/>.
69104
/// </summary>

src/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs

Lines changed: 0 additions & 94 deletions
This file was deleted.

src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs

Lines changed: 0 additions & 144 deletions
This file was deleted.
Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1-
using System;
1+
#nullable enable
2+
using System;
23
using System.Security.Cryptography;
34

4-
using Renci.SshNet.Common;
5-
using Renci.SshNet.Security.Cryptography.Ciphers;
6-
75
namespace Renci.SshNet.Security.Cryptography
86
{
97
/// <summary>
108
/// Implements RSA digital signature algorithm.
119
/// </summary>
12-
public class RsaDigitalSignature : CipherDigitalSignature, IDisposable
10+
public class RsaDigitalSignature : DigitalSignature, IDisposable
1311
{
14-
#if NET462
15-
private readonly HashAlgorithm _hash;
16-
#else
17-
private readonly IncrementalHash _hash;
18-
#endif
12+
private readonly RsaKey _key;
13+
private readonly HashAlgorithmName _hashAlgorithmName;
1914

2015
/// <summary>
2116
/// Initializes a new instance of the <see cref="RsaDigitalSignature"/> class with the SHA-1 hash algorithm.
@@ -32,36 +27,22 @@ public RsaDigitalSignature(RsaKey rsaKey)
3227
/// <param name="rsaKey">The RSA key.</param>
3328
/// <param name="hashAlgorithmName">The hash algorithm to use in the digital signature.</param>
3429
public RsaDigitalSignature(RsaKey rsaKey, HashAlgorithmName hashAlgorithmName)
35-
: base(ObjectIdentifier.FromHashAlgorithmName(hashAlgorithmName), new RsaCipher(rsaKey))
3630
{
37-
#if NET462
38-
_hash = CryptoConfig.CreateFromName(hashAlgorithmName.Name) as HashAlgorithm
39-
?? throw new ArgumentException($"Could not create {nameof(HashAlgorithm)} from `{hashAlgorithmName}`.", nameof(hashAlgorithmName));
40-
#else
41-
// CryptoConfig.CreateFromName is a somewhat legacy API and is incompatible with trimming.
42-
// Use IncrementalHash instead (which is also more modern and lighter-weight than HashAlgorithm).
43-
_hash = IncrementalHash.CreateHash(hashAlgorithmName);
44-
#endif
31+
_key = rsaKey;
32+
_hashAlgorithmName = hashAlgorithmName;
4533
}
4634

47-
/// <summary>
48-
/// Hashes the specified input.
49-
/// </summary>
50-
/// <param name="input">The input.</param>
51-
/// <returns>
52-
/// Hashed data.
53-
/// </returns>
54-
protected override byte[] Hash(byte[] input)
35+
/// <inheritdoc/>
36+
public override bool Verify(byte[] input, byte[] signature)
5537
{
56-
#if NET462
57-
return _hash.ComputeHash(input);
58-
#else
59-
_hash.AppendData(input);
60-
return _hash.GetHashAndReset();
61-
#endif
38+
return _key.RSA.VerifyData(input, signature, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
6239
}
6340

64-
#region IDisposable Members
41+
/// <inheritdoc/>
42+
public override byte[] Sign(byte[] input)
43+
{
44+
return _key.RSA.SignData(input, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
45+
}
6546

6647
/// <summary>
6748
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
@@ -78,9 +59,6 @@ public void Dispose()
7859
/// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.</param>
7960
protected virtual void Dispose(bool disposing)
8061
{
81-
_hash.Dispose();
8262
}
83-
84-
#endregion
8563
}
8664
}

0 commit comments

Comments
 (0)