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
{
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)

View File

@ -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);
}

View File

@ -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 не понимает такие контейнеры