Support keys encrypted with secret keys stored in a separate container instead of just a key derived from the password
parent
890b30fa0d
commit
dc06412197
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 не понимает такие контейнеры
|
||||||
|
|
Loading…
Reference in New Issue