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-08-02 03:23:46 +03:00
|
|
|
|
using PCLCrypto;
|
2016-08-06 04:59:25 +03:00
|
|
|
|
using System.Linq;
|
2017-04-20 07:06:11 +03:00
|
|
|
|
using Bit.App.Enums;
|
|
|
|
|
using System.Collections.Generic;
|
2017-04-20 17:20:24 +03:00
|
|
|
|
using Newtonsoft.Json;
|
2017-04-25 23:05:13 +03:00
|
|
|
|
using Plugin.Settings.Abstractions;
|
|
|
|
|
using Bit.App.Models.Api;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
|
|
|
|
|
namespace Bit.App.Services
|
|
|
|
|
{
|
|
|
|
|
public class CryptoService : ICryptoService
|
|
|
|
|
{
|
|
|
|
|
private const string KeyKey = "key";
|
2016-08-06 04:59:25 +03:00
|
|
|
|
private const string PreviousKeyKey = "previousKey";
|
2017-04-25 23:05:13 +03:00
|
|
|
|
private const string PrivateKeyKey = "encPrivateKey";
|
|
|
|
|
private const string OrgKeysKey = "encOrgKeys";
|
2016-05-02 09:52:09 +03:00
|
|
|
|
private const int InitializationVectorSize = 16;
|
|
|
|
|
|
2017-04-25 23:05:13 +03:00
|
|
|
|
private readonly ISettings _settings;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
private readonly ISecureStorageService _secureStorage;
|
2016-08-02 03:23:46 +03:00
|
|
|
|
private readonly IKeyDerivationService _keyDerivationService;
|
2017-04-22 21:36:31 +03:00
|
|
|
|
private SymmetricCryptoKey _key;
|
|
|
|
|
private SymmetricCryptoKey _legacyEtmKey;
|
|
|
|
|
private SymmetricCryptoKey _previousKey;
|
|
|
|
|
private IDictionary<string, SymmetricCryptoKey> _orgKeys;
|
2017-04-20 07:06:11 +03:00
|
|
|
|
private byte[] _privateKey;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
|
2016-08-02 03:23:46 +03:00
|
|
|
|
public CryptoService(
|
2017-04-25 23:05:13 +03:00
|
|
|
|
ISettings settings,
|
2016-08-02 03:23:46 +03:00
|
|
|
|
ISecureStorageService secureStorage,
|
|
|
|
|
IKeyDerivationService keyDerivationService)
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
_settings = settings;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
_secureStorage = secureStorage;
|
2016-08-02 03:23:46 +03:00
|
|
|
|
_keyDerivationService = keyDerivationService;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public SymmetricCryptoKey Key
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2017-04-20 21:23:58 +03:00
|
|
|
|
if(_key == null && _secureStorage.Contains(KeyKey))
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
2017-04-20 21:23:58 +03:00
|
|
|
|
var keyBytes = _secureStorage.Retrieve(KeyKey);
|
|
|
|
|
if(keyBytes != null)
|
|
|
|
|
{
|
2017-04-22 21:36:31 +03:00
|
|
|
|
_key = new SymmetricCryptoKey(keyBytes);
|
2017-04-20 21:23:58 +03:00
|
|
|
|
}
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-02 03:23:46 +03:00
|
|
|
|
return _key;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
2016-05-03 00:50:16 +03:00
|
|
|
|
if(value != null)
|
|
|
|
|
{
|
2017-04-20 06:16:09 +03:00
|
|
|
|
_secureStorage.Store(KeyKey, value.Key);
|
2016-05-03 00:50:16 +03:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-08-06 04:59:25 +03:00
|
|
|
|
PreviousKey = _key;
|
2016-05-03 00:50:16 +03:00
|
|
|
|
_secureStorage.Delete(KeyKey);
|
2016-08-02 03:23:46 +03:00
|
|
|
|
_key = null;
|
2017-04-20 06:16:09 +03:00
|
|
|
|
_legacyEtmKey = null;
|
2016-05-03 00:50:16 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public SymmetricCryptoKey PreviousKey
|
2016-08-06 04:59:25 +03:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2017-04-20 21:23:58 +03:00
|
|
|
|
if(_previousKey == null && _secureStorage.Contains(PreviousKeyKey))
|
2016-08-06 04:59:25 +03:00
|
|
|
|
{
|
2017-04-20 21:23:58 +03:00
|
|
|
|
var keyBytes = _secureStorage.Retrieve(PreviousKeyKey);
|
|
|
|
|
if(keyBytes != null)
|
|
|
|
|
{
|
2017-04-22 21:36:31 +03:00
|
|
|
|
_previousKey = new SymmetricCryptoKey(keyBytes);
|
2017-04-20 21:23:58 +03:00
|
|
|
|
}
|
2016-08-06 04:59:25 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _previousKey;
|
|
|
|
|
}
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
if(value != null)
|
|
|
|
|
{
|
2017-04-20 06:16:09 +03:00
|
|
|
|
_secureStorage.Store(PreviousKeyKey, value.Key);
|
2016-08-06 04:59:25 +03:00
|
|
|
|
_previousKey = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-06 06:58:31 +03:00
|
|
|
|
public bool KeyChanged
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if(Key == null)
|
|
|
|
|
{
|
2017-02-10 06:06:39 +03:00
|
|
|
|
return false;
|
2016-08-06 06:58:31 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(PreviousKey == null)
|
|
|
|
|
{
|
|
|
|
|
return Key != null;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-20 06:16:09 +03:00
|
|
|
|
return !PreviousKey.Key.SequenceEqual(Key.Key);
|
2016-08-06 06:58:31 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-06 04:59:25 +03:00
|
|
|
|
|
2017-04-20 07:06:11 +03:00
|
|
|
|
public byte[] PrivateKey
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
if(_privateKey == null && _settings.Contains(PrivateKeyKey))
|
2017-04-20 07:06:11 +03:00
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
var encPrivateKey = _settings.GetValueOrDefault<string>(PrivateKeyKey);
|
|
|
|
|
var encPrivateKeyCs = new CipherString(encPrivateKey);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_privateKey = DecryptToBytes(encPrivateKeyCs);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
_privateKey = null;
|
|
|
|
|
Debug.WriteLine($"Cannot set private key. Decryption failed.");
|
|
|
|
|
}
|
2017-04-20 07:06:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _privateKey;
|
|
|
|
|
}
|
2017-04-20 17:20:24 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public IDictionary<string, SymmetricCryptoKey> OrgKeys
|
2017-04-20 17:20:24 +03:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
if((!_orgKeys?.Any() ?? true) && _settings.Contains(OrgKeysKey))
|
2017-04-20 17:20:24 +03:00
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
var orgKeysEncDictJson = _settings.GetValueOrDefault<string>(OrgKeysKey);
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(orgKeysEncDictJson))
|
2017-04-20 17:20:24 +03:00
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
_orgKeys = new Dictionary<string, SymmetricCryptoKey>();
|
|
|
|
|
var orgKeysDict = JsonConvert.DeserializeObject<IDictionary<string, string>>(orgKeysEncDictJson);
|
|
|
|
|
foreach(var item in orgKeysDict)
|
2017-04-20 17:20:24 +03:00
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var orgKeyCs = new CipherString(item.Value);
|
|
|
|
|
var decOrgKeyBytes = RsaDecryptToBytes(orgKeyCs, PrivateKey);
|
|
|
|
|
_orgKeys.Add(item.Key, new SymmetricCryptoKey(decOrgKeyBytes));
|
|
|
|
|
}
|
|
|
|
|
catch
|
2017-04-20 17:20:24 +03:00
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
Debug.WriteLine($"Cannot set org key {item.Key}. Decryption failed.");
|
2017-04-20 17:20:24 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _orgKeys;
|
|
|
|
|
}
|
2017-04-25 23:05:13 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetPrivateKey(CipherString privateKeyEnc)
|
|
|
|
|
{
|
|
|
|
|
if(privateKeyEnc != null)
|
2017-04-20 17:20:24 +03:00
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
_settings.AddOrUpdateValue(PrivateKeyKey, privateKeyEnc.EncryptedString);
|
|
|
|
|
}
|
|
|
|
|
else if(_settings.Contains(PrivateKeyKey))
|
|
|
|
|
{
|
|
|
|
|
_settings.Remove(PrivateKeyKey);
|
|
|
|
|
_privateKey = null;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_privateKey = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-20 17:20:24 +03:00
|
|
|
|
|
2017-04-25 23:05:13 +03:00
|
|
|
|
public void SetOrgKeys(ProfileResponse profile)
|
|
|
|
|
{
|
|
|
|
|
var orgKeysEncDict = new Dictionary<string, string>();
|
|
|
|
|
|
|
|
|
|
if(profile?.Organizations?.Any() ?? false)
|
|
|
|
|
{
|
|
|
|
|
foreach(var org in profile.Organizations)
|
2017-04-20 17:20:24 +03:00
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
orgKeysEncDict.Add(org.Id, org.Key);
|
2017-04-20 17:20:24 +03:00
|
|
|
|
}
|
2017-04-20 07:06:11 +03:00
|
|
|
|
}
|
2017-04-25 23:05:13 +03:00
|
|
|
|
|
|
|
|
|
SetOrgKeys(orgKeysEncDict);
|
2017-04-20 07:06:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 23:05:13 +03:00
|
|
|
|
public void SetOrgKeys(Dictionary<string, string> orgKeysEncDict)
|
2017-04-20 07:06:11 +03:00
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
if(orgKeysEncDict?.Any() ?? false)
|
|
|
|
|
{
|
|
|
|
|
var dictJson = JsonConvert.SerializeObject(orgKeysEncDict);
|
|
|
|
|
_settings.AddOrUpdateValue(OrgKeysKey, dictJson);
|
|
|
|
|
}
|
|
|
|
|
else if(_settings.Contains(OrgKeysKey))
|
|
|
|
|
{
|
|
|
|
|
_settings.Remove(OrgKeysKey);
|
|
|
|
|
_orgKeys = null;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_orgKeys = null;
|
|
|
|
|
}
|
2017-04-20 07:06:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public SymmetricCryptoKey GetOrgKey(string orgId)
|
2017-04-20 07:06:11 +03:00
|
|
|
|
{
|
2017-04-20 17:20:24 +03:00
|
|
|
|
if(OrgKeys == null || !OrgKeys.ContainsKey(orgId))
|
2017-04-20 07:06:11 +03:00
|
|
|
|
{
|
2017-04-20 17:20:24 +03:00
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OrgKeys[orgId];
|
|
|
|
|
}
|
2017-04-20 07:06:11 +03:00
|
|
|
|
|
2017-04-20 17:20:24 +03:00
|
|
|
|
public void ClearKeys()
|
|
|
|
|
{
|
2017-04-25 23:05:13 +03:00
|
|
|
|
SetOrgKeys((Dictionary<string, string>)null);
|
2017-04-20 17:20:24 +03:00
|
|
|
|
Key = null;
|
2017-04-25 23:05:13 +03:00
|
|
|
|
SetPrivateKey(null);
|
2017-04-20 07:06:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public CipherString Encrypt(string plaintextValue, SymmetricCryptoKey key = null)
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
2017-04-20 06:16:09 +03:00
|
|
|
|
if(key == null)
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
2017-04-20 06:16:09 +03:00
|
|
|
|
key = Key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(key == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(key));
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(plaintextValue == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(plaintextValue));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var plaintextBytes = Encoding.UTF8.GetBytes(plaintextValue);
|
|
|
|
|
|
2016-08-02 03:23:46 +03:00
|
|
|
|
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
|
2017-04-20 06:16:09 +03:00
|
|
|
|
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
|
2016-08-02 03:23:46 +03:00
|
|
|
|
var iv = WinRTCrypto.CryptographicBuffer.GenerateRandom(provider.BlockLength);
|
|
|
|
|
var encryptedBytes = WinRTCrypto.CryptographicEngine.Encrypt(cryptoKey, plaintextBytes, iv);
|
2017-04-27 19:14:45 +03:00
|
|
|
|
var mac = key.MacKey != null ? ComputeMacBase64(encryptedBytes, iv, key.MacKey) : null;
|
2016-12-11 06:05:52 +03:00
|
|
|
|
|
2017-04-25 23:05:13 +03:00
|
|
|
|
return new CipherString(key.EncryptionType, Convert.ToBase64String(iv),
|
2017-04-22 21:36:31 +03:00
|
|
|
|
Convert.ToBase64String(encryptedBytes), mac);
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public string Decrypt(CipherString encyptedValue, SymmetricCryptoKey key = null)
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
2016-08-06 10:10:54 +03:00
|
|
|
|
try
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
2017-04-20 07:06:11 +03:00
|
|
|
|
var bytes = DecryptToBytes(encyptedValue, key);
|
|
|
|
|
return Encoding.UTF8.GetString(bytes, 0, bytes.Length).TrimEnd('\0');
|
|
|
|
|
}
|
|
|
|
|
catch(Exception e)
|
|
|
|
|
{
|
|
|
|
|
Debug.WriteLine("Could not decrypt '{0}'. {1}", encyptedValue, e.Message);
|
|
|
|
|
return "[error: cannot decrypt]";
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-20 06:16:09 +03:00
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public byte[] DecryptToBytes(CipherString encyptedValue, SymmetricCryptoKey key = null)
|
2017-04-20 07:06:11 +03:00
|
|
|
|
{
|
|
|
|
|
if(key == null)
|
|
|
|
|
{
|
|
|
|
|
key = Key;
|
|
|
|
|
}
|
2016-08-06 10:10:54 +03:00
|
|
|
|
|
2017-04-20 07:06:11 +03:00
|
|
|
|
if(key == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(encyptedValue == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(encyptedValue));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(encyptedValue.EncryptionType == Enums.EncryptionType.AesCbc128_HmacSha256_B64 &&
|
|
|
|
|
key.EncryptionType == Enums.EncryptionType.AesCbc256_B64)
|
|
|
|
|
{
|
|
|
|
|
// Old encrypt-then-mac scheme, swap out the key
|
|
|
|
|
if(_legacyEtmKey == null)
|
2016-08-06 10:10:54 +03:00
|
|
|
|
{
|
2017-04-22 21:36:31 +03:00
|
|
|
|
_legacyEtmKey = new SymmetricCryptoKey(key.Key, Enums.EncryptionType.AesCbc128_HmacSha256_B64);
|
2016-08-06 10:10:54 +03:00
|
|
|
|
}
|
2016-05-02 09:52:09 +03:00
|
|
|
|
|
2017-04-20 07:06:11 +03:00
|
|
|
|
key = _legacyEtmKey;
|
|
|
|
|
}
|
2017-04-20 06:16:09 +03:00
|
|
|
|
|
2017-04-20 07:06:11 +03:00
|
|
|
|
if(encyptedValue.EncryptionType != key.EncryptionType)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("encType unavailable.");
|
|
|
|
|
}
|
2017-04-20 06:16:09 +03:00
|
|
|
|
|
2017-04-20 07:06:11 +03:00
|
|
|
|
if(key.MacKey != null && !string.IsNullOrWhiteSpace(encyptedValue.Mac))
|
|
|
|
|
{
|
2017-04-27 19:14:45 +03:00
|
|
|
|
var computedMacBytes = ComputeMac(encyptedValue.CipherTextBytes,
|
2017-04-20 07:06:11 +03:00
|
|
|
|
encyptedValue.InitializationVectorBytes, key.MacKey);
|
2017-04-27 19:14:45 +03:00
|
|
|
|
if(!MacsEqual(key.MacKey, computedMacBytes, encyptedValue.MacBytes))
|
2017-04-20 06:16:09 +03:00
|
|
|
|
{
|
2017-04-20 07:06:11 +03:00
|
|
|
|
throw new InvalidOperationException("MAC failed.");
|
2017-04-20 06:16:09 +03:00
|
|
|
|
}
|
2017-04-20 07:06:11 +03:00
|
|
|
|
}
|
2017-04-20 06:16:09 +03:00
|
|
|
|
|
2017-04-20 07:06:11 +03:00
|
|
|
|
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
|
|
|
|
|
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
|
|
|
|
|
var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, encyptedValue.CipherTextBytes,
|
|
|
|
|
encyptedValue.InitializationVectorBytes);
|
|
|
|
|
return decryptedBytes;
|
|
|
|
|
}
|
2016-05-02 09:52:09 +03:00
|
|
|
|
|
2017-04-20 07:06:11 +03:00
|
|
|
|
public byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey)
|
|
|
|
|
{
|
|
|
|
|
if(privateKey == null)
|
|
|
|
|
{
|
|
|
|
|
privateKey = PrivateKey;
|
2016-07-07 05:33:50 +03:00
|
|
|
|
}
|
2017-04-20 07:06:11 +03:00
|
|
|
|
|
|
|
|
|
if(privateKey == null)
|
2016-07-07 05:33:50 +03:00
|
|
|
|
{
|
2017-04-20 07:06:11 +03:00
|
|
|
|
throw new ArgumentNullException(nameof(privateKey));
|
2016-07-07 05:33:50 +03:00
|
|
|
|
}
|
2017-04-20 07:06:11 +03:00
|
|
|
|
|
2017-04-21 20:40:29 +03:00
|
|
|
|
IAsymmetricKeyAlgorithmProvider provider = null;
|
|
|
|
|
switch(encyptedValue.EncryptionType)
|
2017-04-20 07:06:11 +03:00
|
|
|
|
{
|
2017-04-21 20:40:29 +03:00
|
|
|
|
case EncryptionType.Rsa2048_OaepSha256_B64:
|
|
|
|
|
provider = WinRTCrypto.AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithm.RsaOaepSha256);
|
|
|
|
|
break;
|
|
|
|
|
case EncryptionType.Rsa2048_OaepSha1_B64:
|
|
|
|
|
provider = WinRTCrypto.AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithm.RsaOaepSha1);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentException("EncryptionType unavailable.");
|
2017-04-20 07:06:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cryptoKey = provider.ImportKeyPair(privateKey, CryptographicPrivateKeyBlobType.Pkcs8RawPrivateKeyInfo);
|
|
|
|
|
var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, encyptedValue.CipherTextBytes);
|
|
|
|
|
return decryptedBytes;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-27 19:14:45 +03:00
|
|
|
|
private string ComputeMacBase64(byte[] ctBytes, byte[] ivBytes, byte[] macKey)
|
|
|
|
|
{
|
|
|
|
|
var mac = ComputeMac(ctBytes, ivBytes, macKey);
|
|
|
|
|
return Convert.ToBase64String(mac);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private byte[] ComputeMac(byte[] ctBytes, byte[] ivBytes, byte[] macKey)
|
2016-12-11 06:05:52 +03:00
|
|
|
|
{
|
2017-04-20 06:16:09 +03:00
|
|
|
|
if(macKey == null)
|
2016-12-11 06:05:52 +03:00
|
|
|
|
{
|
2017-04-20 06:16:09 +03:00
|
|
|
|
throw new ArgumentNullException(nameof(macKey));
|
2016-12-11 06:05:52 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(ctBytes == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(ctBytes));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(ivBytes == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(ivBytes));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
|
2017-04-20 06:16:09 +03:00
|
|
|
|
var hasher = algorithm.CreateHash(macKey);
|
2016-12-11 06:05:52 +03:00
|
|
|
|
hasher.Append(ivBytes.Concat(ctBytes).ToArray());
|
|
|
|
|
var mac = hasher.GetValueAndReset();
|
2017-04-27 19:14:45 +03:00
|
|
|
|
return mac;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Safely compare two MACs in a way that protects against timing attacks (Double HMAC Verification).
|
|
|
|
|
// ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/
|
|
|
|
|
private bool MacsEqual(byte[] macKey, byte[] mac1, byte[] mac2)
|
|
|
|
|
{
|
|
|
|
|
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
|
|
|
|
|
var hasher = algorithm.CreateHash(macKey);
|
|
|
|
|
|
|
|
|
|
hasher.Append(mac1);
|
|
|
|
|
mac1 = hasher.GetValueAndReset();
|
|
|
|
|
|
|
|
|
|
hasher.Append(mac2);
|
|
|
|
|
mac2 = hasher.GetValueAndReset();
|
|
|
|
|
|
|
|
|
|
if(mac1.Length != mac2.Length)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < mac2.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if(mac1[i] != mac2[i])
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2016-12-11 06:05:52 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public SymmetricCryptoKey MakeKeyFromPassword(string password, string salt)
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
|
2017-04-20 06:16:09 +03:00
|
|
|
|
var keyBytes = _keyDerivationService.DeriveKey(passwordBytes, saltBytes, 5000);
|
2017-04-22 21:36:31 +03:00
|
|
|
|
return new SymmetricCryptoKey(keyBytes);
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string MakeKeyFromPasswordBase64(string password, string salt)
|
|
|
|
|
{
|
|
|
|
|
var key = MakeKeyFromPassword(password, salt);
|
2017-04-20 06:16:09 +03:00
|
|
|
|
return Convert.ToBase64String(key.Key);
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public byte[] HashPassword(SymmetricCryptoKey key, string password)
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
|
|
|
|
if(key == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(Key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(password == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(password));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
2017-04-20 06:16:09 +03:00
|
|
|
|
var hash = _keyDerivationService.DeriveKey(key.Key, passwordBytes, 1);
|
2016-08-02 03:23:46 +03:00
|
|
|
|
return hash;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 21:36:31 +03:00
|
|
|
|
public string HashPasswordBase64(SymmetricCryptoKey key, string password)
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
|
|
|
|
var hash = HashPassword(key, password);
|
|
|
|
|
return Convert.ToBase64String(hash);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|