Skip to content

Commit

Permalink
Use System.Security.Cryptography in DesCipher and TripleDesCipher
Browse files Browse the repository at this point in the history
Falls back to use BouncyCastle if BCL doesn't support
  • Loading branch information
scott-xu committed Dec 1, 2024
1 parent 11e543c commit eadc5ab
Show file tree
Hide file tree
Showing 24 changed files with 1,387 additions and 913 deletions.
15 changes: 7 additions & 8 deletions src/Renci.SshNet/ConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using Renci.SshNet.Security;
using Renci.SshNet.Security.Cryptography;
using Renci.SshNet.Security.Cryptography.Ciphers;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;

namespace Renci.SshNet
{
Expand Down Expand Up @@ -363,16 +362,16 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy

Encryptions = new Dictionary<string, CipherInfo>
{
{ "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) },
{ "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) },
{ "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) },
{ "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false)) },
{ "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false)) },
{ "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false)) },
{ "[email protected]", new CipherInfo(128, (key, iv) => new AesGcmCipher(key, iv, aadLength: 4), isAead: true) },
{ "[email protected]", new CipherInfo(256, (key, iv) => new AesGcmCipher(key, iv, aadLength: 4), isAead: true) },
{ "[email protected]", new CipherInfo(512, (key, iv) => new ChaCha20Poly1305Cipher(key, aadLength: 4), isAead: true) },
{ "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) },
{ "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) },
{ "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) },
{ "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null)) },
{ "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
{ "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
{ "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
{ "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
};

HmacAlgorithms = new Dictionary<string, HashInfo>
Expand Down
15 changes: 7 additions & 8 deletions src/Renci.SshNet/PrivateKeyFile.OpenSSH.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Renci.SshNet.Security;
using Renci.SshNet.Security.Cryptography;
using Renci.SshNet.Security.Cryptography.Ciphers;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;

namespace Renci.SshNet
{
Expand Down Expand Up @@ -91,25 +90,25 @@ public Key Parse()
{
case "3des-cbc":
ivLength = 8;
cipherInfo = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null));
cipherInfo = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
break;
case "aes128-cbc":
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
break;
case "aes192-cbc":
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
break;
case "aes256-cbc":
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
break;
case "aes128-ctr":
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false));
break;
case "aes192-ctr":
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false));
break;
case "aes256-ctr":
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false));
break;
case "[email protected]":
cipherInfo = new CipherInfo(128, (key, iv) => new AesGcmCipher(key, iv, aadLength: 0), isAead: true);
Expand Down
14 changes: 6 additions & 8 deletions src/Renci.SshNet/PrivateKeyFile.PKCS1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
using Renci.SshNet.Common;
using Renci.SshNet.Security;
using Renci.SshNet.Security.Cryptography.Ciphers;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
using Renci.SshNet.Security.Cryptography.Ciphers.Paddings;

namespace Renci.SshNet
{
Expand Down Expand Up @@ -53,22 +51,22 @@ public Key Parse()
switch (_cipherName)
{
case "DES-EDE3-CBC":
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
break;
case "DES-EDE3-CFB":
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CfbCipherMode(iv), padding: null));
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CFB, pkcs7Padding: false));
break;
case "DES-CBC":
cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));
cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
break;
case "AES-128-CBC":
cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
break;
case "AES-192-CBC":
cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
break;
case "AES-256-CBC":
cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
break;
default:
throw new SshException(string.Format(CultureInfo.InvariantCulture, "Private key cipher \"{0}\" is not supported.", _cipherName));
Expand Down
3 changes: 1 addition & 2 deletions src/Renci.SshNet/PrivateKeyFile.SSHCOM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Renci.SshNet.Common;
using Renci.SshNet.Security;
using Renci.SshNet.Security.Cryptography.Ciphers;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;

namespace Renci.SshNet
{
Expand Down Expand Up @@ -51,7 +50,7 @@ public Key Parse()
}

var key = GetCipherKey(_passPhrase, 192 / 8);
var ssh2Сipher = new TripleDesCipher(key, new CbcCipherMode(new byte[8]), padding: null);
var ssh2Сipher = new TripleDesCipher(key, new byte[8], BlockCipherMode.CBC, pkcs7Padding: false);
keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize));
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,21 +123,11 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
throw new NotImplementedException($"Invalid usage of {nameof(DecryptBlock)}.");
}

private void Dispose(bool disposing)
{
if (disposing)
{
_aes.Dispose();
_encryptor.Dispose();
_decryptor.Dispose();
}
}

public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
_aes.Dispose();
_encryptor.Dispose();
_decryptor.Dispose();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,11 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
return _decryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
}

private void Dispose(bool disposing)
{
if (disposing)
{
_aes.Dispose();
_encryptor.Dispose();
_decryptor.Dispose();
}
}

public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
_aes.Dispose();
_encryptor.Dispose();
_decryptor.Dispose();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,10 @@ private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length)
}
}

private void Dispose(bool disposing)
{
if (disposing)
{
_aes.Dispose();
_encryptor.Dispose();
}
}

public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
_aes.Dispose();
_encryptor.Dispose();
}
}
}
Expand Down
27 changes: 8 additions & 19 deletions src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,27 @@ public sealed partial class AesCipher : BlockCipher, IDisposable
/// Initializes a new instance of the <see cref="AesCipher"/> class.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="mode">The mode.</param>
/// <param name="iv">The IV.</param>
/// <param name="mode">The mode.</param>
/// <param name="pkcs7Padding">Enable PKCS7 padding.</param>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Keysize is not valid for this algorithm.</exception>
public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = false)
public AesCipher(byte[] key, byte[] iv, BlockCipherMode mode, bool pkcs7Padding = false)
: base(key, 16, mode: null, padding: null)
{
if (mode == AesCipherMode.OFB)
if (mode == BlockCipherMode.OFB)
{
// OFB is not supported on modern .NET
_impl = new BlockImpl(key, new OfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
}
#if !NET6_0_OR_GREATER
else if (mode == AesCipherMode.CFB)
else if (mode == BlockCipherMode.CFB)
{
// CFB not supported on NetStandard 2.1
_impl = new BlockImpl(key, new CfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
}
#endif
else if (mode == AesCipherMode.CTR)
else if (mode == BlockCipherMode.CTR)
{
// CTR not supported by the BCL, use an optimized implementation
_impl = new CtrImpl(key, iv);
Expand Down Expand Up @@ -76,24 +76,13 @@ public override byte[] Decrypt(byte[] input, int offset, int length)
return _impl.Decrypt(input, offset, length);
}

/// <summary>
/// Dispose the instance.
/// </summary>
/// <param name="disposing">Set to True to dispose of resouces.</param>
public void Dispose(bool disposing)
/// <inheritdoc/>
public void Dispose()
{
if (disposing && _impl is IDisposable disposableImpl)
if (_impl is IDisposable disposableImpl)
{
disposableImpl.Dispose();
}
}

/// <inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
/// <summary>
/// Custom AES Cipher Mode, follows System.Security.Cryptography.CipherMode.
/// Custom Cipher Mode, follows System.Security.Cryptography.CipherMode.
/// </summary>
public enum AesCipherMode
public enum BlockCipherMode
{
/// <summary>Cipher Block Chain Mode.</summary>
CBC = 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.Security.Cryptography;

using Renci.SshNet.Common;

namespace Renci.SshNet.Security.Cryptography.Ciphers
{
public partial class DesCipher
{
private sealed class BclImpl : BlockCipher, IDisposable
{
private readonly DES _des;
private readonly ICryptoTransform _encryptor;
private readonly ICryptoTransform _decryptor;

public BclImpl(
byte[] key,
byte[] iv,
System.Security.Cryptography.CipherMode mode,
PaddingMode padding)
: base(key, 8, mode: null, padding: null)
{
var des = DES.Create();
des.Key = Key;
des.IV = iv.Take(8);
des.Mode = mode;
des.Padding = padding;
_des = des;
_encryptor = _des.CreateEncryptor();
_decryptor = _des.CreateDecryptor();
}

public override byte[] Encrypt(byte[] input, int offset, int length)
{
if (_des.Padding != PaddingMode.None)
{
return _encryptor.TransformFinalBlock(input, offset, length);
}

var output = new byte[length];
_ = _encryptor.TransformBlock(input, offset, length, output, 0);

return output;
}

public override byte[] Decrypt(byte[] input, int offset, int length)
{
if (_des.Padding != PaddingMode.None)
{
return _decryptor.TransformFinalBlock(input, offset, length);
}

var output = new byte[length];
_ = _decryptor.TransformBlock(input, offset, length, output, 0);

return output;
}

public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
throw new NotImplementedException($"Invalid usage of {nameof(EncryptBlock)}.");
}

public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
throw new NotImplementedException($"Invalid usage of {nameof(DecryptBlock)}.");
}

public void Dispose()
{
_des.Dispose();
_encryptor.Dispose();
_decryptor.Dispose();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;

namespace Renci.SshNet.Security.Cryptography.Ciphers
{
public partial class DesCipher
{
private sealed class BouncyCastleImpl : BlockCipher
{
private KeyParameter _parameter;
private DesEngine _encryptor;
private DesEngine _decryptor;

public BouncyCastleImpl(byte[] key, CipherMode mode, CipherPadding padding)
: base(key, 8, mode, padding)
{
}

public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
if (_encryptor == null)
{
_parameter ??= new KeyParameter(Key);
_encryptor = new DesEngine();
_encryptor.Init(forEncryption: true, _parameter);
}

return _encryptor.ProcessBlock(inputBuffer, inputOffset, outputBuffer, outputOffset);
}

public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
if (_decryptor == null)
{
_parameter ??= new KeyParameter(Key);
_decryptor = new DesEngine();
_decryptor.Init(forEncryption: false, _parameter);
}

return _decryptor.ProcessBlock(inputBuffer, inputOffset, outputBuffer, outputOffset);
}
}
}
}
Loading

0 comments on commit eadc5ab

Please sign in to comment.