vipnet-extract-data/VipNetExtract2/VipNetContainerEntry.cs

224 lines
10 KiB
C#
Raw Normal View History

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("Закрытый ключ не соответствует открытому ключу.");
}
}
}