Testing out some encryption implementations

This commit is contained in:
David Sparer
2016-07-11 13:39:30 -06:00
parent b3bac32441
commit e5bed0afcb
6 changed files with 215 additions and 12 deletions

View File

@@ -1,6 +1,9 @@
using System.Security;
using System.Text;
using mRemoteNG.Security;
using NUnit.Framework;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
namespace mRemoteNGTests.Security
@@ -26,7 +29,7 @@ namespace mRemoteNGTests.Security
[Test]
public void GetBlockSizeReturnsProperValueForAes()
{
Assert.That(_aesCryptographyProvider.BlockSize, Is.EqualTo(16));
Assert.That(_aesCryptographyProvider.BlockSizeInBytes, Is.EqualTo(16));
}
[Test]
@@ -46,6 +49,16 @@ namespace mRemoteNGTests.Security
Assert.That(decryptedCipherText, Is.EqualTo(plainText));
}
[Test]
public void EncryptorTest_DecryptedTextIsEqualToOriginalPlainText()
{
var plainText = "MySecret!";
var aes = new Encryptor<AesEngine, Sha256Digest>();
var cipherText = aes.Encrypt(plainText, _encryptionKey);
var decryptedCipherText = aes.Decrypt(cipherText, _encryptionKey);
Assert.That(decryptedCipherText, Is.EqualTo(plainText));
}
[Test]
public void EncryptingTheSameValueReturnsNewCipherTextEachTime()
{
@@ -55,4 +68,4 @@ namespace mRemoteNGTests.Security
Assert.That(cipherText1, Is.Not.EqualTo(cipherText2));
}
}
}
}

View File

@@ -10,7 +10,7 @@ namespace mRemoteNG.Security
private AesEngine _aesEngine;
private readonly Encoding _encoding;
public int BlockSize => _aesEngine.GetBlockSize();
public int BlockSizeInBytes => _aesEngine.GetBlockSize();
public AesCryptographyProvider()
{

View File

@@ -49,19 +49,16 @@ namespace mRemoteNG.Security
{
try
{
var inputBytes = Encoding.UTF8.GetBytes(input);
var iv = BuildInitializationVector(); //for the sake of demo
//Set up
var inputStringAsByteArray = Encoding.UTF8.GetBytes(input);
var cipher = new PaddedBufferedBlockCipher(_blockCipher);
var keyParam = new KeyParameter(Encoding.Default.GetBytes(key.ConvertToUnsecureString()));
var keyParamWithIv = new ParametersWithIV(keyParam, iv, 0, 16);
var keyParamWithIv = BuildKeyParameterWithIv(key);
// Encrypt/Decrypt
cipher.Init(forEncrypt, keyParamWithIv);
var outputBytes = new byte[cipher.GetOutputSize(inputBytes.Length)];
var length = cipher.ProcessBytes(inputBytes, outputBytes, 0);
cipher.DoFinal(outputBytes, length); //Do the final block
var outputBytes = new byte[cipher.GetOutputSize(inputStringAsByteArray.Length)];
var length = cipher.ProcessBytes(inputStringAsByteArray, outputBytes, 0);
cipher.DoFinal(outputBytes, length);
return outputBytes;
}
catch (CryptoException ex)
@@ -70,6 +67,14 @@ namespace mRemoteNG.Security
}
}
private ParametersWithIV BuildKeyParameterWithIv(SecureString key)
{
var iv = BuildInitializationVector();
var keyParam = new KeyParameter(Encoding.Default.GetBytes(key.ConvertToUnsecureString()));
var keyParamWithIv = new ParametersWithIV(keyParam, iv, 0, 16);
return keyParamWithIv;
}
private byte[] BuildInitializationVector()
{
var numberOfBytes = _blockCipher.GetBlockSize();

View File

@@ -0,0 +1,184 @@
using System;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
namespace mRemoteNG.Security
{
public sealed class Encryptor<TBlockCipher, TDigest> : ICryptographyProvider
where TBlockCipher : IBlockCipher, new()
where TDigest : IDigest, new()
{
private Encoding _encoding;
private IBlockCipher _blockCipher;
private BufferedBlockCipher _cipher;
private HMac _mac;
private TDigest _digest;
public Encryptor()
{
_encoding = Encoding.UTF8;
Init(new Pkcs7Padding());
_digest = new TDigest();
}
public Encryptor(Encoding encoding)
{
_encoding = encoding;
Init(new Pkcs7Padding());
_digest = new TDigest();
}
public Encryptor(Encoding encoding, IBlockCipherPadding padding)
{
_encoding = encoding;
Init(padding);
_digest = new TDigest();
}
private void Init(IBlockCipherPadding padding)
{
_blockCipher = new CbcBlockCipher(new TBlockCipher());
_cipher = new PaddedBufferedBlockCipher(_blockCipher, padding);
}
private void InitializeMac(string message, SecureString key)
{
var macKey = BuildMacKey(message, key);
_mac = new HMac(_digest);
_mac.Init(new KeyParameter(macKey));
}
private byte[] BuildMacKey(string message, SecureString key)
{
var derivativeKey = GetDerivativeKey(key);
return derivativeKey;
}
private byte[] GetDerivativeKey(SecureString key)
{
var kdfParam = new KdfParameters(_encoding.GetBytes(key.ConvertToUnsecureString()), GenerateIv());
var kdf = new BaseKdfBytesGenerator(0, _digest);
kdf.Init(kdfParam);
var outputBytes = new byte[_digest.GetByteLength()];
kdf.GenerateBytes(outputBytes, 0, _digest.GetByteLength());
return outputBytes;
}
public string Encrypt(string plainText, SecureString encryptionKey)
{
var encryptedBytes = EncryptBytes(plainText, encryptionKey);
return Convert.ToBase64String(encryptedBytes);
}
public byte[] EncryptBytes(string plainText, SecureString encryptionKey)
{
InitializeMac(plainText, encryptionKey);
var input = _encoding.GetBytes(plainText);
var iv = GenerateIv();
var encryptionKeyAsByteArray = _encoding.GetBytes(encryptionKey.ConvertToUnsecureString());
var keyParam = new KeyParameter(encryptionKeyAsByteArray);
var keyParamWithIv = new ParametersWithIV(keyParam, iv);
var cipher = BouncyCastleCrypto(true, input, keyParamWithIv);
var message = CombineArrays(iv, cipher);
_mac.Reset();
_mac.BlockUpdate(message, 0, message.Length);
var digest = new byte[_mac.GetUnderlyingDigest().GetDigestSize()];
_mac.DoFinal(digest, 0);
var result = CombineArrays(digest, message);
return result;
}
public byte[] DecryptBytes(byte[] bytes, SecureString decryptionKey)
{
// split the digest into component parts
var digest = new byte[_mac.GetUnderlyingDigest().GetDigestSize()];
var message = new byte[bytes.Length - digest.Length];
var iv = new byte[_blockCipher.GetBlockSize()];
var cipher = new byte[message.Length - iv.Length];
Buffer.BlockCopy(bytes, 0, digest, 0, digest.Length);
Buffer.BlockCopy(bytes, digest.Length, message, 0, message.Length);
if (!IsValidHMac(digest, message))
{
throw new CryptoException();
}
Buffer.BlockCopy(message, 0, iv, 0, iv.Length);
Buffer.BlockCopy(message, iv.Length, cipher, 0, cipher.Length);
var decryptionKeyAsByteArray = _encoding.GetBytes(decryptionKey.ConvertToUnsecureString());
var keyParam = new KeyParameter(decryptionKeyAsByteArray);
var keyParamWithIv = new ParametersWithIV(keyParam, iv);
var result = BouncyCastleCrypto(false, cipher, keyParamWithIv);
return result;
}
public string Decrypt(string cipher, SecureString decryptionKey)
{
var cipherTextAsByteArray = Convert.FromBase64String(cipher);
var decryptedBytes = DecryptBytes(cipherTextAsByteArray, decryptionKey);
var decryptedBytesAsEncodedString = _encoding.GetString(decryptedBytes);
return decryptedBytesAsEncodedString;
}
private bool IsValidHMac(byte[] digest, byte[] message)
{
_mac.Reset();
_mac.BlockUpdate(message, 0, message.Length);
var computed = new byte[_mac.GetUnderlyingDigest().GetDigestSize()];
_mac.DoFinal(computed, 0);
return AreEqual(digest, computed);
}
private static bool AreEqual(byte[] digest, byte[] computed)
{
if (digest.Length != computed.Length)
return false;
var result = 0;
for (var i = 0; i < digest.Length; i++)
{
// compute equality of all bytes before returning.
// helps prevent timing attacks:
// https://codahale.com/a-lesson-in-timing-attacks/
result |= digest[i] ^ computed[i];
}
return result == 0;
}
private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, ICipherParameters parameters)
{
_cipher.Init(forEncrypt, parameters);
return _cipher.DoFinal(input);
}
private byte[] GenerateIv()
{
using (var provider = new RNGCryptoServiceProvider())
{
var result = new byte[_blockCipher.GetBlockSize()];
provider.GetBytes(result);
return result;
}
}
private static byte[] CombineArrays(byte[] source1, byte[] source2)
{
var result = new byte[source1.Length + source2.Length];
Buffer.BlockCopy(source1, 0, result, 0, source1.Length);
Buffer.BlockCopy(source2, 0, result, source1.Length, source2.Length);
return result;
}
}
}

View File

@@ -169,6 +169,8 @@
<Compile Include="Messages\MessageClassEnum.cs" />
<Compile Include="Security\AesCryptographyProvider.cs" />
<Compile Include="Security\BouncyCastleCryptographyEngine.cs" />
<Compile Include="Security\Crypt.cs" />
<Compile Include="Security\Encryptor.cs" />
<Compile Include="Security\ICryptographyProvider.cs" />
<Compile Include="Security\SecureStringExtensions.cs" />
<Compile Include="Tools\ArgumentParser.cs" />
@@ -343,7 +345,6 @@
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Include="Tree\Root\RootNodeInfo.cs" />
<Compile Include="Security\Security.Crypt.cs" />
<Compile Include="Security\Security.Impersonator.cs" />
<Compile Include="Security\Security.Save.cs" />
<Compile Include="Tools\IeBrowserEmulation.cs" />