Skip to content

Commit eadc5ab

Browse files
committed
Use System.Security.Cryptography in DesCipher and TripleDesCipher
Falls back to use BouncyCastle if BCL doesn't support
1 parent 11e543c commit eadc5ab

24 files changed

+1387
-913
lines changed

src/Renci.SshNet/ConnectionInfo.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
using Renci.SshNet.Security;
1313
using Renci.SshNet.Security.Cryptography;
1414
using Renci.SshNet.Security.Cryptography.Ciphers;
15-
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
1615

1716
namespace Renci.SshNet
1817
{
@@ -363,16 +362,16 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
363362

364363
Encryptions = new Dictionary<string, CipherInfo>
365364
{
366-
{ "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) },
367-
{ "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) },
368-
{ "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) },
365+
{ "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false)) },
366+
{ "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false)) },
367+
{ "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false)) },
369368
{ "[email protected]", new CipherInfo(128, (key, iv) => new AesGcmCipher(key, iv, aadLength: 4), isAead: true) },
370369
{ "[email protected]", new CipherInfo(256, (key, iv) => new AesGcmCipher(key, iv, aadLength: 4), isAead: true) },
371370
{ "[email protected]", new CipherInfo(512, (key, iv) => new ChaCha20Poly1305Cipher(key, aadLength: 4), isAead: true) },
372-
{ "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) },
373-
{ "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) },
374-
{ "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) },
375-
{ "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null)) },
371+
{ "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
372+
{ "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
373+
{ "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
374+
{ "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
376375
};
377376

378377
HmacAlgorithms = new Dictionary<string, HashInfo>

src/Renci.SshNet/PrivateKeyFile.OpenSSH.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Renci.SshNet.Security;
99
using Renci.SshNet.Security.Cryptography;
1010
using Renci.SshNet.Security.Cryptography.Ciphers;
11-
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
1211

1312
namespace Renci.SshNet
1413
{
@@ -91,25 +90,25 @@ public Key Parse()
9190
{
9291
case "3des-cbc":
9392
ivLength = 8;
94-
cipherInfo = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null));
93+
cipherInfo = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
9594
break;
9695
case "aes128-cbc":
97-
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
96+
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
9897
break;
9998
case "aes192-cbc":
100-
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
99+
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
101100
break;
102101
case "aes256-cbc":
103-
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
102+
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
104103
break;
105104
case "aes128-ctr":
106-
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
105+
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false));
107106
break;
108107
case "aes192-ctr":
109-
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
108+
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false));
110109
break;
111110
case "aes256-ctr":
112-
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
111+
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false));
113112
break;
114113
115114
cipherInfo = new CipherInfo(128, (key, iv) => new AesGcmCipher(key, iv, aadLength: 0), isAead: true);

src/Renci.SshNet/PrivateKeyFile.PKCS1.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
using Renci.SshNet.Common;
1010
using Renci.SshNet.Security;
1111
using Renci.SshNet.Security.Cryptography.Ciphers;
12-
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
13-
using Renci.SshNet.Security.Cryptography.Ciphers.Paddings;
1412

1513
namespace Renci.SshNet
1614
{
@@ -53,22 +51,22 @@ public Key Parse()
5351
switch (_cipherName)
5452
{
5553
case "DES-EDE3-CBC":
56-
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));
54+
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
5755
break;
5856
case "DES-EDE3-CFB":
59-
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CfbCipherMode(iv), padding: null));
57+
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CFB, pkcs7Padding: false));
6058
break;
6159
case "DES-CBC":
62-
cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));
60+
cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
6361
break;
6462
case "AES-128-CBC":
65-
cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
63+
cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
6664
break;
6765
case "AES-192-CBC":
68-
cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
66+
cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
6967
break;
7068
case "AES-256-CBC":
71-
cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
69+
cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
7270
break;
7371
default:
7472
throw new SshException(string.Format(CultureInfo.InvariantCulture, "Private key cipher \"{0}\" is not supported.", _cipherName));

src/Renci.SshNet/PrivateKeyFile.SSHCOM.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Renci.SshNet.Common;
88
using Renci.SshNet.Security;
99
using Renci.SshNet.Security.Cryptography.Ciphers;
10-
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
1110

1211
namespace Renci.SshNet
1312
{
@@ -51,7 +50,7 @@ public Key Parse()
5150
}
5251

5352
var key = GetCipherKey(_passPhrase, 192 / 8);
54-
var ssh2Сipher = new TripleDesCipher(key, new CbcCipherMode(new byte[8]), padding: null);
53+
var ssh2Сipher = new TripleDesCipher(key, new byte[8], BlockCipherMode.CBC, pkcs7Padding: false);
5554
keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize));
5655
}
5756
else

src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BclImpl.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,21 +123,11 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
123123
throw new NotImplementedException($"Invalid usage of {nameof(DecryptBlock)}.");
124124
}
125125

126-
private void Dispose(bool disposing)
127-
{
128-
if (disposing)
129-
{
130-
_aes.Dispose();
131-
_encryptor.Dispose();
132-
_decryptor.Dispose();
133-
}
134-
}
135-
136126
public void Dispose()
137127
{
138-
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
139-
Dispose(disposing: true);
140-
GC.SuppressFinalize(this);
128+
_aes.Dispose();
129+
_encryptor.Dispose();
130+
_decryptor.Dispose();
141131
}
142132
}
143133
}

src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BlockImpl.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,11 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
3333
return _decryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
3434
}
3535

36-
private void Dispose(bool disposing)
37-
{
38-
if (disposing)
39-
{
40-
_aes.Dispose();
41-
_encryptor.Dispose();
42-
_decryptor.Dispose();
43-
}
44-
}
45-
4636
public void Dispose()
4737
{
48-
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
49-
Dispose(disposing: true);
50-
GC.SuppressFinalize(this);
38+
_aes.Dispose();
39+
_encryptor.Dispose();
40+
_decryptor.Dispose();
5141
}
5242
}
5343
}

src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,20 +105,10 @@ private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length)
105105
}
106106
}
107107

108-
private void Dispose(bool disposing)
109-
{
110-
if (disposing)
111-
{
112-
_aes.Dispose();
113-
_encryptor.Dispose();
114-
}
115-
}
116-
117108
public void Dispose()
118109
{
119-
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
120-
Dispose(disposing: true);
121-
GC.SuppressFinalize(this);
110+
_aes.Dispose();
111+
_encryptor.Dispose();
122112
}
123113
}
124114
}

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

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,27 @@ public sealed partial class AesCipher : BlockCipher, IDisposable
1717
/// Initializes a new instance of the <see cref="AesCipher"/> class.
1818
/// </summary>
1919
/// <param name="key">The key.</param>
20-
/// <param name="mode">The mode.</param>
2120
/// <param name="iv">The IV.</param>
21+
/// <param name="mode">The mode.</param>
2222
/// <param name="pkcs7Padding">Enable PKCS7 padding.</param>
2323
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
2424
/// <exception cref="ArgumentException">Keysize is not valid for this algorithm.</exception>
25-
public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = false)
25+
public AesCipher(byte[] key, byte[] iv, BlockCipherMode mode, bool pkcs7Padding = false)
2626
: base(key, 16, mode: null, padding: null)
2727
{
28-
if (mode == AesCipherMode.OFB)
28+
if (mode == BlockCipherMode.OFB)
2929
{
3030
// OFB is not supported on modern .NET
3131
_impl = new BlockImpl(key, new OfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
3232
}
3333
#if !NET6_0_OR_GREATER
34-
else if (mode == AesCipherMode.CFB)
34+
else if (mode == BlockCipherMode.CFB)
3535
{
3636
// CFB not supported on NetStandard 2.1
3737
_impl = new BlockImpl(key, new CfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
3838
}
3939
#endif
40-
else if (mode == AesCipherMode.CTR)
40+
else if (mode == BlockCipherMode.CTR)
4141
{
4242
// CTR not supported by the BCL, use an optimized implementation
4343
_impl = new CtrImpl(key, iv);
@@ -76,24 +76,13 @@ public override byte[] Decrypt(byte[] input, int offset, int length)
7676
return _impl.Decrypt(input, offset, length);
7777
}
7878

79-
/// <summary>
80-
/// Dispose the instance.
81-
/// </summary>
82-
/// <param name="disposing">Set to True to dispose of resouces.</param>
83-
public void Dispose(bool disposing)
79+
/// <inheritdoc/>
80+
public void Dispose()
8481
{
85-
if (disposing && _impl is IDisposable disposableImpl)
82+
if (_impl is IDisposable disposableImpl)
8683
{
8784
disposableImpl.Dispose();
8885
}
8986
}
90-
91-
/// <inheritdoc/>
92-
public void Dispose()
93-
{
94-
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
95-
Dispose(disposing: true);
96-
GC.SuppressFinalize(this);
97-
}
9887
}
9988
}

src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherMode.cs renamed to src/Renci.SshNet/Security/Cryptography/Ciphers/BlockCipherMode.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
namespace Renci.SshNet.Security.Cryptography.Ciphers
22
{
33
/// <summary>
4-
/// Custom AES Cipher Mode, follows System.Security.Cryptography.CipherMode.
4+
/// Custom Cipher Mode, follows System.Security.Cryptography.CipherMode.
55
/// </summary>
6-
public enum AesCipherMode
6+
public enum BlockCipherMode
77
{
88
/// <summary>Cipher Block Chain Mode.</summary>
99
CBC = 1,
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
4+
using Renci.SshNet.Common;
5+
6+
namespace Renci.SshNet.Security.Cryptography.Ciphers
7+
{
8+
public partial class DesCipher
9+
{
10+
private sealed class BclImpl : BlockCipher, IDisposable
11+
{
12+
private readonly DES _des;
13+
private readonly ICryptoTransform _encryptor;
14+
private readonly ICryptoTransform _decryptor;
15+
16+
public BclImpl(
17+
byte[] key,
18+
byte[] iv,
19+
System.Security.Cryptography.CipherMode mode,
20+
PaddingMode padding)
21+
: base(key, 8, mode: null, padding: null)
22+
{
23+
var des = DES.Create();
24+
des.Key = Key;
25+
des.IV = iv.Take(8);
26+
des.Mode = mode;
27+
des.Padding = padding;
28+
_des = des;
29+
_encryptor = _des.CreateEncryptor();
30+
_decryptor = _des.CreateDecryptor();
31+
}
32+
33+
public override byte[] Encrypt(byte[] input, int offset, int length)
34+
{
35+
if (_des.Padding != PaddingMode.None)
36+
{
37+
return _encryptor.TransformFinalBlock(input, offset, length);
38+
}
39+
40+
var output = new byte[length];
41+
_ = _encryptor.TransformBlock(input, offset, length, output, 0);
42+
43+
return output;
44+
}
45+
46+
public override byte[] Decrypt(byte[] input, int offset, int length)
47+
{
48+
if (_des.Padding != PaddingMode.None)
49+
{
50+
return _decryptor.TransformFinalBlock(input, offset, length);
51+
}
52+
53+
var output = new byte[length];
54+
_ = _decryptor.TransformBlock(input, offset, length, output, 0);
55+
56+
return output;
57+
}
58+
59+
public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
60+
{
61+
throw new NotImplementedException($"Invalid usage of {nameof(EncryptBlock)}.");
62+
}
63+
64+
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
65+
{
66+
throw new NotImplementedException($"Invalid usage of {nameof(DecryptBlock)}.");
67+
}
68+
69+
public void Dispose()
70+
{
71+
_des.Dispose();
72+
_encryptor.Dispose();
73+
_decryptor.Dispose();
74+
}
75+
}
76+
}
77+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Org.BouncyCastle.Crypto.Engines;
2+
using Org.BouncyCastle.Crypto.Parameters;
3+
4+
namespace Renci.SshNet.Security.Cryptography.Ciphers
5+
{
6+
public partial class DesCipher
7+
{
8+
private sealed class BouncyCastleImpl : BlockCipher
9+
{
10+
private KeyParameter _parameter;
11+
private DesEngine _encryptor;
12+
private DesEngine _decryptor;
13+
14+
public BouncyCastleImpl(byte[] key, CipherMode mode, CipherPadding padding)
15+
: base(key, 8, mode, padding)
16+
{
17+
}
18+
19+
public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
20+
{
21+
if (_encryptor == null)
22+
{
23+
_parameter ??= new KeyParameter(Key);
24+
_encryptor = new DesEngine();
25+
_encryptor.Init(forEncryption: true, _parameter);
26+
}
27+
28+
return _encryptor.ProcessBlock(inputBuffer, inputOffset, outputBuffer, outputOffset);
29+
}
30+
31+
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
32+
{
33+
if (_decryptor == null)
34+
{
35+
_parameter ??= new KeyParameter(Key);
36+
_decryptor = new DesEngine();
37+
_decryptor.Init(forEncryption: false, _parameter);
38+
}
39+
40+
return _decryptor.ProcessBlock(inputBuffer, inputOffset, outputBuffer, outputOffset);
41+
}
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)