From dc064121975ee141840d8909acf360f645137ec5 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Tue, 4 May 2021 17:50:45 +0300 Subject: [PATCH] Support keys encrypted with secret keys stored in a separate container instead of just a key derived from the password --- VipNetExtract2/Export.cs | 12 +++++----- VipNetExtract2/Program.cs | 6 +++-- VipNetExtract2/VipNetContainerEntry.cs | 31 ++++++++++++++++++++++---- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/VipNetExtract2/Export.cs b/VipNetExtract2/Export.cs index 9f00056..ab46660 100644 --- a/VipNetExtract2/Export.cs +++ b/VipNetExtract2/Export.cs @@ -15,14 +15,14 @@ namespace VipNetExtract { interface IExport { - void Export(VipNetContainer container, string pin, Stream output); + void Export(VipNetContainer container, VipNetContainer defence, string pin, Stream output); } 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()); using (var sw = new StreamWriter(output)) { 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 gostParams = Gost3410PublicKeyAlgParameters.GetInstance(entry.KeyInfo.Algorithm.Parameters); @@ -44,14 +44,14 @@ namespace VipNetExtract gostParams.DigestParamSet ) ), - new DerOctetString(new DerInteger(entry.GetPrivateKey(pin))) + new DerOctetString(new DerInteger(entry.GetPrivateKey(pin, defence))) ); } } 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; if (cert == null) diff --git a/VipNetExtract2/Program.cs b/VipNetExtract2/Program.cs index e363a6e..6834f58 100644 --- a/VipNetExtract2/Program.cs +++ b/VipNetExtract2/Program.cs @@ -19,12 +19,13 @@ namespace VipNetExtract static void Main(string[] args) { - string file = null, pin = null; + string file = null, pin = null, defenceFile = null; Mode mode = Mode.Private; bool showHelp = false; options = new OptionSet { { "f|file=", "Путь к контейнеру", f => file = f }, + { "d|defence=", "Путь к вспомогательному контейнеру секретного ключа", f => defenceFile = f }, { "private", "Извлечь закрытый ключ (по умолчанию)", p => { if (p != null) mode = Mode.Private; } }, { "cert", "Извлечь сертификат", c => { if (c != null) mode = Mode.Certificate; } }, { "p|pin=", "ПИН-код", p => pin = p }, @@ -52,7 +53,8 @@ namespace VipNetExtract try { 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) { Console.Error.WriteLine(e.Message); } diff --git a/VipNetExtract2/VipNetContainerEntry.cs b/VipNetExtract2/VipNetContainerEntry.cs index c67b6fc..9644046 100644 --- a/VipNetExtract2/VipNetContainerEntry.cs +++ b/VipNetExtract2/VipNetContainerEntry.cs @@ -48,12 +48,27 @@ namespace VipNetExtract public DerOctetString PublicKey { get; internal set; } 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 mac = KeyBlock.Skip(cek.Length).Take(4).ToArray(); var data = cek.Concat(KeyInfo.RawData).ToArray(); - var pinKey = GetDecryptionKey(pin); + var pinKey = GetDecryptionKey(pin, defence); CheckMac(pinKey, cek, data, mac); @@ -127,10 +142,18 @@ namespace VipNetExtract return output; } - private byte[] GetDecryptionKey(string pin) + private byte[] GetDecryptionKey(string pin, VipNetContainer defence) { 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 // Самое смешное, что сам десктопный ViPNet CSP не понимает такие контейнеры