vipnet-extract-data/VipNetExtract2/VipNetContainerEntry.cs

224 lines
10 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.CryptoPro;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
namespace VipNetExtract
{
class VipNetContainerEntry
{
public VipNetContainerEntry(Asn1Sequence seq, byte[] keyBlock)
{
Version = (DerInteger)seq[0];
KeyInfo = VipNetKeyInfo.GetInstance(seq[1]);
DefenceKeyInfo = VipNetKeyInfo.GetInstance(seq[2]);
KeyBlock = keyBlock;
for (int i = 3; i < seq.Count; ++i) {
if (seq[i] is Asn1TaggedObject tag) {
switch (tag.TagNo) {
case 0:
Certificate = X509CertificateStructure.GetInstance(tag.GetObject());
break;
case 1:
PublicKey = (DerOctetString)tag.GetObject();
break;
}
}
}
}
public DerInteger Version { get; }
public VipNetKeyInfo KeyInfo { get; }
public VipNetKeyInfo DefenceKeyInfo { get; }
public X509CertificateStructure Certificate { get; }
public DerOctetString PublicKey { get; internal set; }
public byte[] KeyBlock { get; }
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, defence);
CheckMac(pinKey, cek, data, mac);
var iv = KeyBlock.Skip(KeyBlock.Length - 8).ToArray();
var pkeyMasked = DecryptKey(pinKey, cek, iv);
byte[] privateKey;
if (KeyInfo.KeyClass.Value.And(BigInteger.Three).Equals(BigInteger.Zero)) {
data = pkeyMasked.Take(pkeyMasked.Length / 2).ToArray();
var unwrappingKey = pkeyMasked.Skip(pkeyMasked.Length / 2).ToArray();
privateKey = DecryptKey(unwrappingKey, data);
} else {
var wrapped = pkeyMasked.Take(pkeyMasked.Length / 2).Reverse().ToArray();
var mask = pkeyMasked.Skip(pkeyMasked.Length / 2).Reverse().ToArray();
var algParams = Gost3410PublicKeyAlgParameters.GetInstance(KeyInfo.Algorithm.Parameters);
var param = new ECKeyGenerationParameters(algParams.PublicKeyParamSet, new SecureRandom());
var x = new BigInteger(1, wrapped);
var y = new BigInteger(1, mask);
var z = x.Multiply(y).Mod(param.DomainParameters.Curve.Order);
CheckPrivateKey(param, z);
privateKey = z.ToByteArrayUnsigned();
}
return new BigInteger(1, privateKey);
}
private byte[] PBKDF2(IMac hmac, byte[] password, byte[] salt, int iterations, int keyLength)
{
// HMAC(HASH, key, msg) = HASH((key ^ 0x5c...) + HASH((key ^ 0x36...) + msg))
// U(HASH, key, salt, blockNum_be_int32, 1) = HMAC(HASH, key, salt + blockNum_be_int32)
// U(..., N) = HMAC(HASH, key, U(..., N-1))
// F(..., N) = U(..., 1) ^ ... ^ U(..., N)
// PBKDF2(HASH, password, salt, N, keyLength, blockSize) =
// (F(HASH, password, salt, 1, N) + ... +
// F(HASH, password, salt, ceil(keyLength / blockSize), N)).resize(keyLength)
int blockSize = hmac.GetMacSize();
hmac.Init(new KeyParameter(password));
int numBlocks = (keyLength+blockSize-1)/blockSize;
var output = new byte[numBlocks*blockSize];
var blockNumBuf = new byte[4];
for (int blockNum = 1; blockNum <= numBlocks; blockNum++)
{
blockNumBuf[0] = (byte)(blockNum >> 24);
blockNumBuf[1] = (byte)(blockNum >> 16);
blockNumBuf[2] = (byte)(blockNum >> 8);
blockNumBuf[3] = (byte)(blockNum);
byte[] block = new byte[blockSize];
byte[] F = new byte[blockSize];
hmac.BlockUpdate(salt, 0, salt.Length);
hmac.BlockUpdate(blockNumBuf, 0, 4);
hmac.DoFinal(F, 0);
hmac.Reset();
for (int j = 0; j < F.Length; j++)
block[j] = F[j];
for (int iter = 1; iter < iterations; iter++)
{
hmac.BlockUpdate(F, 0, F.Length);
hmac.DoFinal(F, 0);
hmac.Reset();
for (int j = 0; j < F.Length; j++)
block[j] = (byte)(block[j] ^ F[j]);
}
for (int j = 0; j < block.Length; j++)
output[(blockNum-1)*blockSize + j] = block[j];
}
Array.Resize(ref output, keyLength);
return output;
}
private byte[] GetDecryptionKey(string pin, VipNetContainer defence)
{
var passwordData = Encoding.ASCII.GetBytes(pin ?? "");
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 не понимает такие контейнеры
// А мы понимаем!
var p = Pbkdf2Params.GetInstance(DefenceKeyInfo.Algorithm.Parameters);
return PBKDF2(
MacUtilities.GetMac(p.Prf.Algorithm),
passwordData,
p.GetSalt(),
p.IterationCount.IntValue,
p.KeyLength.IntValue
);
}
var digest = new Gost3411Digest();
var keyData = new byte[digest.GetDigestSize()];
var unwrappingKey = new byte[digest.GetDigestSize()];
digest.BlockUpdate(passwordData, 0, passwordData.Length);
digest.DoFinal(keyData, 0);
digest.Reset();
var secodeData = passwordData.Concat(keyData).ToArray();
digest.BlockUpdate(secodeData, 0, secodeData.Length);
digest.DoFinal(unwrappingKey, 0);
var tmp = new int[keyData.Length / 4];
for (int i = 0; i < keyData.Length; i += 4)
tmp[i / 4] = BitConverter.ToInt32(keyData, i) - BitConverter.ToInt32(unwrappingKey, i);
return tmp.SelectMany(x => BitConverter.GetBytes(x)).ToArray();
}
private static void CheckMac(byte[] key, byte[] cek, byte[] data, byte[] mac)
{
var m = new Gost28147Mac();
var keyPrm = ParameterUtilities.CreateKeyParameter("GOST", key);
m.Init(keyPrm);
var cekmac = new byte[4];
m.BlockUpdate(data, 0, data.Length);
m.DoFinal(cekmac, 0);
if (!mac.SequenceEqual(cekmac))
throw new CryptographicException("Неверный ПИН-код");
}
private static byte[] DecryptKey(byte[] key, byte[] cek, byte[] iv = null)
{
var cipher = CipherUtilities.GetCipher("GOST/CFB/NOPADDING");
ICipherParameters prms = ParameterUtilities.CreateKeyParameter("GOST", key);
prms = new ParametersWithSBox(prms, Gost28147Engine.GetSBox("E-A"));
cipher.Init(false, iv == null ? prms : new ParametersWithIV(prms, iv));
return cipher.ProcessBytes(cek);
}
private void CheckPrivateKey(ECKeyGenerationParameters param, BigInteger privateKey)
{
var point = param.DomainParameters.G.Multiply(privateKey).Normalize();
var x = point.AffineXCoord.GetEncoded().Reverse();
var y = point.AffineYCoord.GetEncoded().Reverse();
var pub = PublicKey.GetOctets();
if (!x.SequenceEqual(pub.Take(pub.Length / 2)) || !y.SequenceEqual(pub.Skip(pub.Length / 2)))
throw new CryptographicException("Закрытый ключ не соответствует открытому ключу.");
}
}
}