Хз актуально ли еще, но нашел свой старый модуль:
Пример использования:
Не прикладывал код sql ридера и пары хелперов, но думаю адоптировать не сложно будет
C#:
namespace NameRemoved.BrowserModule.Browser.GeckoBased.Cryptography
{
public abstract class KeyDb
{
public abstract bool IsOldFormat { get; }
public byte[] GlobalSalt;
public byte[] PasswordCheckAsn;
public byte[] Key3DesAsn;
protected abstract bool ParseKeyDb(byte[] keyDb);
public static T Read<T>(byte[] keyDbBytes) where T : KeyDb,new()
{
var keyDb = new T();
return !keyDb.ParseKeyDb(keyDbBytes) ? default(T) : keyDb;
}
}
}
C#:
using System;
using NameRemoved.Shared.Helper;
using NameRemoved.Shared.Sql.Helper;
namespace NameRemoved.BrowserModule.Browser.GeckoBased.Cryptography
{
public class Key3Db : KeyDb
{
public override bool IsOldFormat => true;
protected override bool ParseKeyDb(byte[] key3Db)
{
var pageSize = key3Db.ConvertTo32<int>(12);
var nbKey = key3Db.ConvertTo32<int>(56);
var readCount = 0;
var page = 1;
while (readCount < nbKey)
{
var address = new ushort[(nbKey - readCount) * 2];
for (var i = 0; i < address.Length; i++)
{
var offset = pageSize * page + 2 + i * 2;
address[i] = (ushort)(key3Db[offset + 1] << 8 | key3Db[offset]);
}
Array.Sort(address);
for (var i = 0; i < address.Length; i = i + 2)
{
var startValue = address[i] + pageSize * page;
var startKey = address[i + 1] + pageSize * page;
var endKey = (i + 2 >= address.Length)
? pageSize + (pageSize * page)
: address[i + 2] + pageSize * page;
if (endKey == startKey) continue;
readCount++;
if (endKey - startKey == 11 && key3Db[startKey] == 'g' && key3Db[startKey + 5] == 'l' &&
key3Db[startKey + 7] == 's' && key3Db[startKey + 10] == 't')
GlobalSalt = key3Db.ExtractByteArray(startValue, startKey - startValue);
else if (endKey - startKey == 14 && key3Db[startKey] == 'p' && key3Db[startKey + 7] == 'd' &&
key3Db[startKey + 9] == 'c' && key3Db[startKey + 13] == 'k')
PasswordCheckAsn = key3Db.ExtractByteArray(startValue, startKey - startValue);
else if (endKey - startKey == 16 && key3Db[startKey] == 0xf8 && key3Db[startKey + 6] == 0 &&
key3Db[startKey + 12] == 0 && key3Db[startKey + 15] == 0x01)
Key3DesAsn = key3Db.ExtractByteArray(startValue, startKey - startValue);
}
page++;
}
return (GlobalSalt != null && PasswordCheckAsn != null && Key3DesAsn != null);
}
}
}
C#:
using NameRemoved.Shared.Sql;
namespace NameRemoved.BrowserModule.Browser.GeckoBased.Cryptography
{
public class Key4Db : KeyDb
{
public override bool IsOldFormat => false;
protected override bool ParseKeyDb(byte[] keyDb)
{
var reader = new NameRemovedSqlReader(keyDb);
return ReadMetaDataTable(reader) && ReadNssPrivateTable(reader);
}
private bool ReadMetaDataTable(NameRemovedSqlReader reader)
{
reader.OpenTable("metaData");
var idField = reader.GetFieldId("id");
var rowsCount = reader.GetTableRowsCount();
for (var i = 0; i < rowsCount; i++)
{
if (string.CompareOrdinal(reader.GetFieldRowValue<string>(idField, i), "password") != 0) continue;
GlobalSalt = reader.GetFieldRowValue<byte[]>("item1", i);
PasswordCheckAsn = reader.GetFieldRowValue<byte[]>("item2", i);
return true;
}
return false;
}
private bool ReadNssPrivateTable(NameRemovedSqlReader reader)
{
reader.OpenTable("nssPrivate");
var a11Field = reader.GetFieldId("a11");
var a102Field = reader.GetFieldId("a102");
var rowsCount = reader.GetTableRowsCount();
for (var i = 0; i < rowsCount; i++)
{
var rowValues = reader.GetRowValue(i);
if (!(rowValues[a102Field] is byte[] keyId) || keyId.Length != 16 || keyId[0] != 0xf8 ||
keyId[15] != 1) continue;
if (!(rowValues[a11Field] is byte[] key3DesAsn) || key3DesAsn.Length < 1) continue;
Key3DesAsn = key3DesAsn;
return true;
}
return false;
}
}
}
C#:
using NameRemoved.Shared.Helper;
using System;
using System.Security.Cryptography;
using System.Text;
namespace NameRemoved.BrowserModule.Browser.GeckoBased.Cryptography
{
public class GeckoEncryptionManager
{
public static readonly byte[] EmptyPassword = new byte[0];
private static readonly Encoding Utf8 = Encoding.UTF8;
public readonly KeyDb KeyDb;
private readonly byte[] _passwordCheckSalt;
private readonly byte[] _passwordCheckEncrypted;
private byte[] _masterPassword;
private byte[] _3DesKey;
public GeckoEncryptionManager(KeyDb keyDb)
{
KeyDb = keyDb;
var passwordCheckAsn = keyDb.PasswordCheckAsn;
if (keyDb.IsOldFormat)
{
var saltLen = passwordCheckAsn[1];
_passwordCheckSalt = passwordCheckAsn.ExtractByteArray(3, saltLen);
var encLen = passwordCheckAsn.Length - passwordCheckAsn[4 + saltLen] - saltLen - 5;
_passwordCheckEncrypted = passwordCheckAsn.ExtractByteArray(passwordCheckAsn.Length - encLen, encLen);
}
else
{
var asnObj = Asn1Der.Parse(passwordCheckAsn);
_passwordCheckSalt = asnObj[0][0][1][0].Data;
_passwordCheckEncrypted = asnObj[0][1].Data;
}
}
public bool SetMasterPassword(byte[] masterPassword)
{
CalculateKeys(masterPassword, _passwordCheckSalt, out var key, out var iv);
var check = Decode3Des(key, iv, _passwordCheckEncrypted, PaddingMode.None);
if (check[0] != 'p' || check[7] != 'd' || check[8] != '-' || check[9] != 'c' || check[13] != 'k')
return false;
_masterPassword = masterPassword;
return Extract3DesKey();
}
private bool Extract3DesKey()
{
if (_masterPassword == null) return false;
_3DesKey = KeyDb.IsOldFormat ? Extract3DesKeyOldFormat() : Extract3DesKeyNewFormat();
return true;
}
private byte[] Extract3DesKeyOldFormat()
{
var key3DesAsn = KeyDb.Key3DesAsn;
var asnStart = key3DesAsn[1] + key3DesAsn[2] + 3;
key3DesAsn = key3DesAsn.ExtractByteArray(asnStart, key3DesAsn.Length - asnStart);
var asnObj = Asn1Der.Parse(key3DesAsn);
CalculateKeys(_masterPassword, asnObj[0][0][1][0].Data, out var key, out var iv);
var keyAsn = Decode3Des(key, iv, asnObj[0][1].Data, PaddingMode.PKCS7);
var keyInt = Asn1Der.Parse(Asn1Der.Parse(keyAsn)[0][2].Data)[0][3].Data;
var size = keyInt.Length > 24 ? 24 : keyInt.Length;
key = new byte[24];
Array.Copy(keyInt, keyInt.Length - size, key, 0, size);
return key;
}
private byte[] Extract3DesKeyNewFormat()
{
var asnObj = Asn1Der.Parse(KeyDb.Key3DesAsn);
CalculateKeys(_masterPassword, asnObj[0][0][1][0].Data, out var key, out var iv);
return Decode3Des(key, iv, asnObj[0][1].Data, PaddingMode.PKCS7);
}
public byte[] DecryptData(byte[] data)
{
var asnObj = Asn1Der.Parse(data);
return Decode3Des(_3DesKey, asnObj[0][1][1].Data, asnObj[0][2].Data, PaddingMode.PKCS7);
}
public string DecryptString(byte[] data)
{
return Utf8.GetString(DecryptData(data));
}
private void CalculateKeys(byte[] masterPassword, byte[] salt, out byte[] key, out byte[] iv)
{
using (var sha = new SHA1CryptoServiceProvider())
{
var hash = HashArrays(sha, KeyDb.GlobalSalt, masterPassword);
hash = HashArrays(sha, hash, salt);
var buf = new byte[20 + salt.Length];
Array.Copy(salt, 0, buf, 0, salt.Length);
Array.Copy(salt, 0, buf, 20, salt.Length);
using (var hmac = new HMACSHA1(hash))
{
hash = hmac.ComputeHash(buf);
buf = HashArrays(hmac, hmac.ComputeHash(buf, 0, 20), salt);
var fullkey = new byte[hash.Length + buf.Length];
Array.Copy(hash, 0, fullkey, 0, hash.Length);
Array.Copy(buf, 0, fullkey, hash.Length, buf.Length);
ExtractKeys(fullkey, out key, out iv);
}
}
}
private static void ExtractKeys(byte[] fullKey, out byte[] key, out byte[] iv)
{
key = new byte[24];
Array.Copy(fullKey, 0, key, 0, 24);
iv = new byte[8];
var index = iv.Length - 1;
for (var i = fullKey.Length - 1; index >= 0; i--)
{
iv[index] = fullKey[i];
index--;
}
}
private static byte[] HashArrays(HashAlgorithm algorithm, byte[] first, byte[] second)
{
var buf = new byte[first.Length + second.Length];
Array.Copy(first, 0, buf, 0, first.Length);
Array.Copy(second, 0, buf, first.Length, second.Length);
return algorithm.ComputeHash(buf);
}
private static byte[] Decode3Des(byte[] key, byte[] iv, byte[] encrypted, PaddingMode padding)
{
using (var tdsAlg = new TripleDESCryptoServiceProvider {Mode = CipherMode.CBC, Padding = padding })
{
var decryptor = tdsAlg.CreateDecryptor(key, iv);
return decryptor.TransformFinalBlock(encrypted, 0, encrypted.Length);
}
}
}
}
C#:
using System.Collections.Generic;
using NameRemoved.Shared.Helper;
namespace NameRemoved.BrowserModule.Browser.GeckoBased.Cryptography
{
public static class Asn1Der
{
public class Asn1DerObject
{
public byte Type;
public int Length;
public List<Asn1DerObject> Objects = new List<Asn1DerObject>();
public byte[] Data;
public Asn1DerObject this[int i] => Objects[i];
}
private static int CalculateLength(byte[] dataToParse, int index, out int size)
{
if (dataToParse[index + 1] < 128)
{
size = 2;
return dataToParse[index + 1];
}
var lengthBytes = dataToParse[index + 1] - 128;
size = index + 2 + lengthBytes;
var length = (int)dataToParse[index + 2];
for (var i = index + 3; i < size; i++)
length = (length << 8) | dataToParse[i];
size = 2 + lengthBytes;
return length;
}
public static Asn1DerObject Parse(byte[] dataToParse)
{
var parsedData = new Asn1DerObject();
for (var i = 0; i < dataToParse.Length; i++)
{
var length = CalculateLength(dataToParse, i, out var size);
var type = dataToParse[i];
switch (type)
{
case 0x30:
parsedData.Type = type;
parsedData.Length = length;
parsedData.Objects.Add(Parse(dataToParse.ExtractByteArray(i + size, length)));
break;
default:
parsedData.Objects.Add(CreateObject(type, dataToParse.ExtractByteArray(i + size, length)));
break;
}
i = i + size - 1 + length;
}
return parsedData;
}
private static Asn1DerObject CreateObject(byte type, byte[] data)
{
return new Asn1DerObject()
{
Type = type,
Length = data.Length,
Data = data
};
}
}
}
Пример использования:
C#:
var encryptionManager = new GeckoEncryptionManager(profile.KeyDb4 ?? profile.KeyDb3);
encryptionManager.SetMasterPassword(GeckoEncryptionManager.EmptyPassword);
encryptionManager.DecryptString(Convert.FromBase64String(password.GetValue<string>(reader))));
Не прикладывал код sql ридера и пары хелперов, но думаю адоптировать не сложно будет