Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use System.Security.Cryptography for TripleDesCipher #1546

Merged
merged 5 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ The main types provided by this library are:
Private keys in OpenSSL traditional PEM format can be encrypted using one of the following cipher methods:
* DES-EDE3-CBC
* DES-EDE3-CFB
* DES-CBC
* AES-128-CBC
* AES-192-CBC
* AES-256-CBC
Expand Down
5 changes: 3 additions & 2 deletions src/Renci.SshNet/ConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
using Renci.SshNet.Security;
using Renci.SshNet.Security.Cryptography;
using Renci.SshNet.Security.Cryptography.Ciphers;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;

using CipherMode = System.Security.Cryptography.CipherMode;

namespace Renci.SshNet
{
Expand Down Expand Up @@ -372,7 +373,7 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
{ "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)) },
{ "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, CipherMode.CBC, pkcs7Padding: false)) },
};

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

using CipherMode = System.Security.Cryptography.CipherMode;

namespace Renci.SshNet
{
Expand Down Expand Up @@ -91,7 +92,7 @@ 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, CipherMode.CBC, pkcs7Padding: false));
break;
case "aes128-cbc":
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
Expand Down
11 changes: 4 additions & 7 deletions src/Renci.SshNet/PrivateKeyFile.PKCS1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
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;

using CipherMode = System.Security.Cryptography.CipherMode;

namespace Renci.SshNet
{
Expand Down Expand Up @@ -51,13 +51,10 @@ 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, CipherMode.CBC, pkcs7Padding: true));
break;
case "DES-EDE3-CFB":
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CfbCipherMode(iv), padding: null));
break;
case "DES-CBC":
cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, CipherMode.CFB, pkcs7Padding: false));
break;
case "AES-128-CBC":
cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
Expand Down
5 changes: 3 additions & 2 deletions src/Renci.SshNet/PrivateKeyFile.SSHCOM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
using Renci.SshNet.Common;
using Renci.SshNet.Security;
using Renci.SshNet.Security.Cryptography.Ciphers;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;

using CipherMode = System.Security.Cryptography.CipherMode;

namespace Renci.SshNet
{
Expand Down Expand Up @@ -51,7 +52,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], CipherMode.CBC, pkcs7Padding: false);
keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize));
}
else
Expand Down
3 changes: 0 additions & 3 deletions src/Renci.SshNet/PrivateKeyFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ namespace Renci.SshNet
/// <description>DES-EDE3-CFB</description>
/// </item>
/// <item>
/// <description>DES-CBC</description>
/// </item>
/// <item>
/// <description>AES-128-CBC</description>
/// </item>
/// <item>
Expand Down
10 changes: 7 additions & 3 deletions src/Renci.SshNet/Security/Cryptography/BlockCipher.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;

using Org.BouncyCastle.Crypto.Paddings;

using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography.Ciphers;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
Expand All @@ -13,7 +15,7 @@ public abstract class BlockCipher : SymmetricCipher
{
private readonly CipherMode _mode;

private readonly CipherPadding _padding;
private readonly IBlockCipherPadding _padding;

/// <summary>
/// Gets the size of the block in bytes.
Expand Down Expand Up @@ -56,7 +58,7 @@ public byte BlockSize
/// <param name="mode">Cipher mode.</param>
/// <param name="padding">Cipher padding.</param>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding padding)
protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, IBlockCipherPadding padding)
: base(key)
{
_blockSize = blockSize;
Expand All @@ -81,7 +83,9 @@ public override byte[] Encrypt(byte[] input, int offset, int length)
if (_padding is not null)
{
paddingLength = _blockSize - (length % _blockSize);
input = _padding.Pad(input, offset, length, paddingLength);
input = input.Take(offset, length);
Array.Resize(ref input, length + paddingLength);
_ = _padding.AddPadding(input, length);
length += paddingLength;
offset = 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public override byte[] Encrypt(byte[] input, int offset, int length)
{
if (_aes.Mode is System.Security.Cryptography.CipherMode.CFB or System.Security.Cryptography.CipherMode.OFB)
{
// Manually pad the input for cfb and ofb cipher mode as BCL doesn't support partial block.
// See https://github.com/dotnet/runtime/blob/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs#L20-L21
paddingLength = BlockSize - (length % BlockSize);
input = input.Take(offset, length);
length += paddingLength;
Expand All @@ -69,6 +71,7 @@ public override byte[] Encrypt(byte[] input, int offset, int length)

if (paddingLength > 0)
{
// Manually unpad the output.
Array.Resize(ref output, output.Length - paddingLength);
}

Expand All @@ -89,11 +92,12 @@ public override byte[] Decrypt(byte[] input, int offset, int length)
{
if (_aes.Mode is System.Security.Cryptography.CipherMode.CFB or System.Security.Cryptography.CipherMode.OFB)
{
// Manually pad the input for cfb and ofb cipher mode as BCL doesn't support partial block.
// See https://github.com/dotnet/runtime/blob/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs#L20-L21
paddingLength = BlockSize - (length % BlockSize);
var newInput = new byte[input.Length + paddingLength];
Buffer.BlockCopy(input, offset, newInput, 0, length);
input = newInput;
length = input.Length;
input = input.Take(offset, length);
length += paddingLength;
Array.Resize(ref input, length);
offset = 0;
}
}
Expand All @@ -107,6 +111,7 @@ public override byte[] Decrypt(byte[] input, int offset, int length)

if (paddingLength > 0)
{
// Manually unpad the output.
Array.Resize(ref output, output.Length - paddingLength);
}

Expand All @@ -123,21 +128,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
@@ -1,6 +1,8 @@
using System;
using System.Security.Cryptography;

using Org.BouncyCastle.Crypto.Paddings;

namespace Renci.SshNet.Security.Cryptography.Ciphers
{
public partial class AesCipher
Expand All @@ -11,7 +13,7 @@ private sealed class BlockImpl : BlockCipher, IDisposable
private readonly ICryptoTransform _encryptor;
private readonly ICryptoTransform _decryptor;

public BlockImpl(byte[] key, CipherMode mode, CipherPadding padding)
public BlockImpl(byte[] key, CipherMode mode, IBlockCipherPadding padding)
: base(key, 16, mode, padding)
{
var aes = Aes.Create();
Expand All @@ -33,21 +35,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
26 changes: 8 additions & 18 deletions src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System.Security.Cryptography;

using Org.BouncyCastle.Crypto.Paddings;

using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
using Renci.SshNet.Security.Cryptography.Ciphers.Paddings;

namespace Renci.SshNet.Security.Cryptography.Ciphers
{
Expand All @@ -17,8 +18,8 @@ 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>
Expand All @@ -28,13 +29,13 @@ public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding =
if (mode == AesCipherMode.OFB)
{
// OFB is not supported on modern .NET
_impl = new BlockImpl(key, new OfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
_impl = new BlockImpl(key, new OfbCipherMode(iv), pkcs7Padding ? new Pkcs7Padding() : null);
}
#if !NET6_0_OR_GREATER
else if (mode == AesCipherMode.CFB)
{
// CFB not supported on NetStandard 2.1
_impl = new BlockImpl(key, new CfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
_impl = new BlockImpl(key, new CfbCipherMode(iv), pkcs7Padding ? new Pkcs7Padding() : null);
}
#endif
else if (mode == AesCipherMode.CTR)
Expand Down Expand Up @@ -76,24 +77,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);
}
}
}
Loading