2016-05-02 09:52:09 +03:00
|
|
|
|
using System;
|
2016-07-07 05:33:50 +03:00
|
|
|
|
using System.Diagnostics;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
using System.Text;
|
|
|
|
|
using Bit.App.Abstractions;
|
|
|
|
|
using Bit.App.Models;
|
2016-07-07 05:33:50 +03:00
|
|
|
|
using Org.BouncyCastle.Crypto;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
using Org.BouncyCastle.Crypto.Digests;
|
|
|
|
|
using Org.BouncyCastle.Crypto.Engines;
|
|
|
|
|
using Org.BouncyCastle.Crypto.Generators;
|
|
|
|
|
using Org.BouncyCastle.Crypto.Modes;
|
|
|
|
|
using Org.BouncyCastle.Crypto.Paddings;
|
|
|
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
|
|
|
|
|
|
|
|
namespace Bit.App.Services
|
|
|
|
|
{
|
|
|
|
|
public class CryptoService : ICryptoService
|
|
|
|
|
{
|
|
|
|
|
private const string KeyKey = "key";
|
|
|
|
|
private const int InitializationVectorSize = 16;
|
|
|
|
|
private const int KeySize = 256;
|
|
|
|
|
private const int Iterations = 5000;
|
|
|
|
|
|
2016-07-09 20:12:46 +03:00
|
|
|
|
private readonly Random _random = new Random();
|
2016-05-02 09:52:09 +03:00
|
|
|
|
private readonly ISecureStorageService _secureStorage;
|
|
|
|
|
private KeyParameter _keyParameter;
|
|
|
|
|
|
|
|
|
|
public CryptoService(ISecureStorageService secureStorage)
|
|
|
|
|
{
|
|
|
|
|
_secureStorage = secureStorage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] Key
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if(_keyParameter != null)
|
|
|
|
|
{
|
2016-05-19 06:09:17 +03:00
|
|
|
|
return _keyParameter.GetKey();
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var storedKey = _secureStorage.Retrieve(KeyKey);
|
|
|
|
|
if(storedKey == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_keyParameter = new KeyParameter(storedKey);
|
|
|
|
|
return _keyParameter.GetKey();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
2016-05-03 00:50:16 +03:00
|
|
|
|
if(value != null)
|
|
|
|
|
{
|
|
|
|
|
_secureStorage.Store(KeyKey, value);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_secureStorage.Delete(KeyKey);
|
|
|
|
|
_keyParameter = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Base64Key
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if(Key == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Convert.ToBase64String(Key);
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public CipherString Encrypt(string plaintextValue)
|
|
|
|
|
{
|
|
|
|
|
if(Key == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(Key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(plaintextValue == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(plaintextValue));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var plaintextBytes = Encoding.UTF8.GetBytes(plaintextValue);
|
|
|
|
|
|
|
|
|
|
var iv = GenerateRandomInitializationVector();
|
|
|
|
|
var keyParamWithIV = new ParametersWithIV(_keyParameter, iv, 0, InitializationVectorSize);
|
|
|
|
|
|
2016-07-12 03:14:24 +03:00
|
|
|
|
var aesBlockCipher = new CbcBlockCipher(new AesEngine());
|
|
|
|
|
var cipher = new PaddedBufferedBlockCipher(aesBlockCipher);
|
2016-07-12 02:36:39 +03:00
|
|
|
|
cipher.Init(true, keyParamWithIV);
|
|
|
|
|
var encryptedBytes = new byte[cipher.GetOutputSize(plaintextBytes.Length)];
|
|
|
|
|
var length = cipher.ProcessBytes(plaintextBytes, encryptedBytes, 0);
|
|
|
|
|
cipher.DoFinal(encryptedBytes, length);
|
2016-05-02 09:52:09 +03:00
|
|
|
|
|
|
|
|
|
return new CipherString(Convert.ToBase64String(iv), Convert.ToBase64String(encryptedBytes));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Decrypt(CipherString encyptedValue)
|
|
|
|
|
{
|
|
|
|
|
if(Key == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(Key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(encyptedValue == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(encyptedValue));
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 05:33:50 +03:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var keyParamWithIV = new ParametersWithIV(_keyParameter, encyptedValue.InitializationVectorBytes, 0, InitializationVectorSize);
|
2016-07-12 03:14:24 +03:00
|
|
|
|
var aesBlockCipher = new CbcBlockCipher(new AesEngine());
|
|
|
|
|
var cipher = new PaddedBufferedBlockCipher(aesBlockCipher);
|
2016-07-12 02:36:39 +03:00
|
|
|
|
cipher.Init(false, keyParamWithIV);
|
|
|
|
|
byte[] comparisonBytes = new byte[cipher.GetOutputSize(encyptedValue.CipherTextBytes.Length)];
|
|
|
|
|
var length = cipher.ProcessBytes(encyptedValue.CipherTextBytes, comparisonBytes, 0);
|
|
|
|
|
cipher.DoFinal(comparisonBytes, length);
|
2016-07-07 05:33:50 +03:00
|
|
|
|
return Encoding.UTF8.GetString(comparisonBytes, 0, comparisonBytes.Length).TrimEnd('\0');
|
|
|
|
|
}
|
|
|
|
|
catch(Exception e)
|
|
|
|
|
{
|
|
|
|
|
Debug.WriteLine("Could not decrypt '{0}'. {1}", encyptedValue, e.Message);
|
|
|
|
|
return "[error: cannot decrypt]";
|
|
|
|
|
}
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] MakeKeyFromPassword(string password, string salt)
|
|
|
|
|
{
|
|
|
|
|
if(password == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(password));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(salt == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(salt));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
|
|
|
|
var saltBytes = Encoding.UTF8.GetBytes(salt);
|
|
|
|
|
|
|
|
|
|
var generator = new Pkcs5S2ParametersGenerator(new Sha256Digest());
|
|
|
|
|
generator.Init(passwordBytes, saltBytes, Iterations);
|
|
|
|
|
return ((KeyParameter)generator.GenerateDerivedMacParameters(KeySize)).GetKey();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string MakeKeyFromPasswordBase64(string password, string salt)
|
|
|
|
|
{
|
|
|
|
|
var key = MakeKeyFromPassword(password, salt);
|
|
|
|
|
return Convert.ToBase64String(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] HashPassword(byte[] key, string password)
|
|
|
|
|
{
|
|
|
|
|
if(key == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(Key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(password == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(password));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
|
|
|
|
|
|
|
|
|
var generator = new Pkcs5S2ParametersGenerator(new Sha256Digest());
|
|
|
|
|
generator.Init(key, passwordBytes, 1);
|
|
|
|
|
return ((KeyParameter)generator.GenerateDerivedMacParameters(KeySize)).GetKey();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string HashPasswordBase64(byte[] key, string password)
|
|
|
|
|
{
|
|
|
|
|
var hash = HashPassword(key, password);
|
|
|
|
|
return Convert.ToBase64String(hash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private byte[] GenerateRandomInitializationVector()
|
|
|
|
|
{
|
|
|
|
|
var iv = new byte[InitializationVectorSize];
|
2016-07-09 20:12:46 +03:00
|
|
|
|
_random.NextBytes(iv);
|
2016-05-02 09:52:09 +03:00
|
|
|
|
return iv;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|