You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
5.2 KiB
C#

using System;
using Renci.SshNet.Common;
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
/// <summary>
/// Implements RSA cipher algorithm.
/// </summary>
public class RsaCipher : AsymmetricCipher
{
private readonly bool _isPrivate;
private readonly RsaKey _key;
/// <summary>
/// Initializes a new instance of the <see cref="RsaCipher"/> class.
/// </summary>
/// <param name="key">The RSA key.</param>
public RsaCipher(RsaKey key)
{
if (key == null)
throw new ArgumentNullException("key");
_key = key;
_isPrivate = !_key.D.IsZero;
}
/// <summary>
/// Encrypts the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="offset">The zero-based offset in <paramref name="data"/> at which to begin encrypting.</param>
/// <param name="length">The number of bytes to encrypt from <paramref name="data"/>.</param>
/// <returns>Encrypted data.</returns>
public override byte[] Encrypt(byte[] data, int offset, int length)
{
// Calculate signature
var bitLength = _key.Modulus.BitLength;
var paddedBlock = new byte[bitLength / 8 + (bitLength % 8 > 0 ? 1 : 0) - 1];
paddedBlock[0] = 0x01;
for (var i = 1; i < paddedBlock.Length - length - 1; i++)
{
paddedBlock[i] = 0xFF;
}
Buffer.BlockCopy(data, offset, paddedBlock, paddedBlock.Length - length, length);
return Transform(paddedBlock);
}
/// <summary>
/// Decrypts the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>
/// The decrypted data.
/// </returns>
/// <exception cref="NotSupportedException">Only block type 01 or 02 are supported.</exception>
/// <exception cref="NotSupportedException">Thrown when decrypted block type is not supported.</exception>
public override byte[] Decrypt(byte[] data)
{
return Decrypt(data, 0, data.Length);
}
/// <summary>
/// Decrypts the specified input.
/// </summary>
/// <param name="data">The input.</param>
/// <param name="offset">The zero-based offset in <paramref name="data"/> at which to begin decrypting.</param>
/// <param name="length">The number of bytes to decrypt from <paramref name="data"/>.</param>
/// <returns>
/// The decrypted data.
/// </returns>
/// <exception cref="NotSupportedException">Only block type 01 or 02 are supported.</exception>
/// <exception cref="NotSupportedException">Thrown when decrypted block type is not supported.</exception>
public override byte[] Decrypt(byte[] data, int offset, int length)
{
var paddedBlock = Transform(data, offset, length);
if (paddedBlock[0] != 1 && paddedBlock[0] != 2)
throw new NotSupportedException("Only block type 01 or 02 are supported.");
var position = 1;
while (position < paddedBlock.Length && paddedBlock[position] != 0)
position++;
position++;
var result = new byte[paddedBlock.Length - position];
Buffer.BlockCopy(paddedBlock, position, result, 0, result.Length);
return result;
}
private byte[] Transform(byte[] data)
{
return Transform(data, 0, data.Length);
}
private byte[] Transform(byte[] data, int offset, int length)
{
Array.Reverse(data, offset, length);
var inputBytes = new byte[length + 1];
Buffer.BlockCopy(data, offset, inputBytes, 0, length);
var input = new BigInteger(inputBytes);
BigInteger result;
if (_isPrivate)
{
var random = BigInteger.One;
var max = _key.Modulus - 1;
var bitLength = _key.Modulus.BitLength;
if (max < BigInteger.One)
throw new SshException("Invalid RSA key.");
while (random <= BigInteger.One || random >= max)
{
random = BigInteger.Random(bitLength);
}
var blindedInput = BigInteger.PositiveMod((BigInteger.ModPow(random, _key.Exponent, _key.Modulus) * input), _key.Modulus);
// mP = ((input Mod p) ^ dP)) Mod p
var mP = BigInteger.ModPow((blindedInput % _key.P), _key.DP, _key.P);
// mQ = ((input Mod q) ^ dQ)) Mod q
var mQ = BigInteger.ModPow((blindedInput % _key.Q), _key.DQ, _key.Q);
var h = BigInteger.PositiveMod(((mP - mQ) * _key.InverseQ), _key.P);
var m = h * _key.Q + mQ;
var rInv = BigInteger.ModInverse(random, _key.Modulus);
result = BigInteger.PositiveMod((m * rInv), _key.Modulus);
}
else
{
result = BigInteger.ModPow(input, _key.Exponent, _key.Modulus);
}
return result.ToByteArray().Reverse();
}
}
}