Support keys encrypted with secret keys stored in a separate container instead of just a key derived from the password

master
Vitaliy Filippov 2021-05-04 17:50:45 +03:00
parent 890b30fa0d
commit dc06412197
3 changed files with 37 additions and 12 deletions

View File

@ -15,14 +15,14 @@ namespace VipNetExtract
{ {
interface IExport interface IExport
{ {
void Export(VipNetContainer container, string pin, Stream output); void Export(VipNetContainer container, VipNetContainer defence, string pin, Stream output);
} }
class PrivateKeyExport : IExport class PrivateKeyExport : IExport
{ {
public void Export(VipNetContainer container, string pin, Stream output) public void Export(VipNetContainer container, VipNetContainer defence, string pin, Stream output)
{ {
var privateKey = EncodePrivateKey(container, pin); var privateKey = EncodePrivateKey(container, defence, pin);
var pemObject = new PemObject("PRIVATE KEY", privateKey.GetDerEncoded()); var pemObject = new PemObject("PRIVATE KEY", privateKey.GetDerEncoded());
using (var sw = new StreamWriter(output)) { using (var sw = new StreamWriter(output)) {
var writer = new PemWriter(sw); var writer = new PemWriter(sw);
@ -30,7 +30,7 @@ namespace VipNetExtract
} }
} }
private static Asn1Object EncodePrivateKey(VipNetContainer container, string pin) private static Asn1Object EncodePrivateKey(VipNetContainer container, VipNetContainer defence, string pin)
{ {
var entry = container.Entries[0]; var entry = container.Entries[0];
var gostParams = Gost3410PublicKeyAlgParameters.GetInstance(entry.KeyInfo.Algorithm.Parameters); var gostParams = Gost3410PublicKeyAlgParameters.GetInstance(entry.KeyInfo.Algorithm.Parameters);
@ -44,14 +44,14 @@ namespace VipNetExtract
gostParams.DigestParamSet gostParams.DigestParamSet
) )
), ),
new DerOctetString(new DerInteger(entry.GetPrivateKey(pin))) new DerOctetString(new DerInteger(entry.GetPrivateKey(pin, defence)))
); );
} }
} }
class CertificateExport : IExport class CertificateExport : IExport
{ {
public void Export(VipNetContainer container, string pin, Stream output) public void Export(VipNetContainer container, VipNetContainer defence, string pin, Stream output)
{ {
var cert = container.Entries[0].Certificate; var cert = container.Entries[0].Certificate;
if (cert == null) if (cert == null)

View File

@ -19,12 +19,13 @@ namespace VipNetExtract
static void Main(string[] args) static void Main(string[] args)
{ {
string file = null, pin = null; string file = null, pin = null, defenceFile = null;
Mode mode = Mode.Private; Mode mode = Mode.Private;
bool showHelp = false; bool showHelp = false;
options = new OptionSet { options = new OptionSet {
{ "f|file=", "Путь к контейнеру", f => file = f }, { "f|file=", "Путь к контейнеру", f => file = f },
{ "d|defence=", "Путь к вспомогательному контейнеру секретного ключа", f => defenceFile = f },
{ "private", "Извлечь закрытый ключ (по умолчанию)", p => { if (p != null) mode = Mode.Private; } }, { "private", "Извлечь закрытый ключ (по умолчанию)", p => { if (p != null) mode = Mode.Private; } },
{ "cert", "Извлечь сертификат", c => { if (c != null) mode = Mode.Certificate; } }, { "cert", "Извлечь сертификат", c => { if (c != null) mode = Mode.Certificate; } },
{ "p|pin=", "ПИН-код", p => pin = p }, { "p|pin=", "ПИН-код", p => pin = p },
@ -52,7 +53,8 @@ namespace VipNetExtract
try { try {
var container = VipNetContainer.LoadFromFile(file); var container = VipNetContainer.LoadFromFile(file);
export.Export(container, pin, Console.OpenStandardOutput()); var defence = defenceFile != null ? VipNetContainer.LoadFromFile(defenceFile) : null;
export.Export(container, defence, pin, Console.OpenStandardOutput());
} catch (Exception e) { } catch (Exception e) {
Console.Error.WriteLine(e.Message); Console.Error.WriteLine(e.Message);
} }

View File

@ -48,12 +48,27 @@ namespace VipNetExtract
public DerOctetString PublicKey { get; internal set; } public DerOctetString PublicKey { get; internal set; }
public byte[] KeyBlock { get; } public byte[] KeyBlock { get; }
public BigInteger GetPrivateKey(string pin) public byte[] GetProtectionKey(string pin)
{
if (KeyInfo.KeyClass.Value.IntValue != 64 && KeyInfo.KeyType.Value.IntValue != 24622)
throw new CryptographicException("Вспомогательный контейнер не содержит ключа защиты");
var cek = KeyBlock.Take(KeyBlock.Length - 12).ToArray();
var mac = KeyBlock.Skip(cek.Length).Take(4).ToArray();
var data = cek.Concat(KeyInfo.RawData).ToArray();
var pinKey = GetDecryptionKey(pin, null);
CheckMac(pinKey, cek, data, mac);
var iv = KeyBlock.Skip(KeyBlock.Length - 8).ToArray();
return DecryptKey(pinKey, cek, iv);
}
public BigInteger GetPrivateKey(string pin, VipNetContainer defence)
{ {
var cek = KeyBlock.Take(KeyBlock.Length - 12).ToArray(); var cek = KeyBlock.Take(KeyBlock.Length - 12).ToArray();
var mac = KeyBlock.Skip(cek.Length).Take(4).ToArray(); var mac = KeyBlock.Skip(cek.Length).Take(4).ToArray();
var data = cek.Concat(KeyInfo.RawData).ToArray(); var data = cek.Concat(KeyInfo.RawData).ToArray();
var pinKey = GetDecryptionKey(pin); var pinKey = GetDecryptionKey(pin, defence);
CheckMac(pinKey, cek, data, mac); CheckMac(pinKey, cek, data, mac);
@ -127,10 +142,18 @@ namespace VipNetExtract
return output; return output;
} }
private byte[] GetDecryptionKey(string pin) private byte[] GetDecryptionKey(string pin, VipNetContainer defence)
{ {
var passwordData = Encoding.ASCII.GetBytes(pin ?? ""); var passwordData = Encoding.ASCII.GetBytes(pin ?? "");
if (DefenceKeyInfo.Algorithm.Algorithm.Equals(PkcsObjectIdentifiers.IdPbkdf2)) if (DefenceKeyInfo.KeyClass.Value.IntValue == 64 && DefenceKeyInfo.KeyType.Value.IntValue == 24622)
{
// Контейнер зашифрован ключом, лежащим в ещё одном контейнере
if (defence == null)
throw new CryptographicException("Закрытый ключ зашифрован секретным ключом, расположенным в отдельном вспомогательном контейнере. Используйте опцию --defence");
return defence.Entries[0].GetProtectionKey(pin);
}
if (DefenceKeyInfo.Algorithm != null &&
DefenceKeyInfo.Algorithm.Algorithm.Equals(PkcsObjectIdentifiers.IdPbkdf2))
{ {
// PBKDF2 используется в контейнерах ViPNet Jcrypto SDK // PBKDF2 используется в контейнерах ViPNet Jcrypto SDK
// Самое смешное, что сам десктопный ViPNet CSP не понимает такие контейнеры // Самое смешное, что сам десктопный ViPNet CSP не понимает такие контейнеры