logo Наши способы организации безопасного удалённого доступа к рабочим местам и корпоративным ресурсам
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline Dmitry Komarchev  
#1 Оставлено : 25 февраля 2020 г. 14:55:06(UTC)
Dmitry Komarchev

Статус: Новичок

Группы: Участники
Зарегистрирован: 20.12.2019(UTC)
Сообщений: 6

Сказал(а) «Спасибо»: 3 раз
Добрый день!

Чем отличается подпись, получаемая методами Gost3410_2012_256CryptoServiceProvider: CreateSignature, SignData и SignHash?

Offline Артём Макаров  
#2 Оставлено : 25 февраля 2020 г. 15:34:40(UTC)
Артём Макаров

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 20.02.2017(UTC)
Сообщений: 82

Сказал(а) «Спасибо»: 2 раз
Поблагодарили: 20 раз в 20 постах
Добрый день.

CreateSignature и SignHash реализуют одну и ту же функцию.

SignHash подписывает уже вычисленный до вызова функции хэш от некоторых данных. Например, полученный с использованием Gost3411_2012_256.

SignData вычисляет хэш значение для указанных данных и подписывает их. Т.е. хэширование происходит внутри данной функции.
Техническую поддержку оказываем тут
Наша база знаний
Offline Dmitry Komarchev  
#3 Оставлено : 25 февраля 2020 г. 15:52:02(UTC)
Dmitry Komarchev

Статус: Новичок

Группы: Участники
Зарегистрирован: 20.12.2019(UTC)
Сообщений: 6

Сказал(а) «Спасибо»: 3 раз
Тогда попутный вопрос:
Я не нашел возможность штатными средствами .Net (класс SignedCms) сформировать CMS с подписью уже готового хэша документа. Т.е. я не смог завернуть в ContentInfo уже готовый хэш. Если я формирую ContentInfo с самим документом, то получаю CMS с валидной подписью, которая потом проходит проверку. Если я формирую ContentInfo с уже готовым хэшем, то SignedCms его повторно хэширует и подписывает. Я попытался подписать готовый хэш с помощью вышеназванных CreateSignature и SignHash и полученную подпись вставить в Cms. Но результат не проходит проверку - CryptographicException "Неправильная подпись". Можно ли использовать CreateSignature/SignHash для формирования подписи, которая затем будет вставлена в Cms?
Offline Артём Макаров  
#4 Оставлено : 26 февраля 2020 г. 8:43:44(UTC)
Артём Макаров

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 20.02.2017(UTC)
Сообщений: 82

Сказал(а) «Спасибо»: 2 раз
Поблагодарили: 20 раз в 20 постах
При использовании Gost3410_2012CryptoServiceProvider результирующую подпись нужно будет "перевернуть" (изменить порядок следования байт), или воспользоваться Gost2012_256SignatureFormatter, который сам развернёт подпись при формировании.
Техническую поддержку оказываем тут
Наша база знаний
Offline Dmitry Komarchev  
#5 Оставлено : 27 февраля 2020 г. 10:07:03(UTC)
Dmitry Komarchev

Статус: Новичок

Группы: Участники
Зарегистрирован: 20.12.2019(UTC)
Сообщений: 6

Сказал(а) «Спасибо»: 3 раз
Все равно не работает. Я попытался сделать тестовый пример:

class Program
{
static void Main(string[] args)
{
//TestCreateSignedCms();
TestSignFromCms();
}

private static void TestSignFromCms()
{
var content = LoadData("../../TestData/document.txt");

var hash = CalculateHash(content);

var sign = LoadData("../../TestData/sign-from-cms.bin");

var thumbprint = "8F752DBD59C223AA9CD3D1760F2786EEE8B18149";

var certificate = LoadCertificate(thumbprint);

var deformatter = new Gost2012_256SignatureDeformatter(certificate.PrivateKey);

var check = deformatter.VerifySignature(hash, sign);

if (check) { }
}

private static void TestCreateSignedCms()
{
var content = LoadData("../../TestData/document.txt");

var thumbprint = "8F752DBD59C223AA9CD3D1760F2786EEE8B18149";

var certificate = LoadCertificate(thumbprint);

var cms = SignCms(content, certificate);

SaveSignedCms(cms, "../../TestData/document.txt.sig");
}

private static X509Certificate2 LoadCertificate(string thumbprint)
{
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, true);
store.Close();
var certificate = certificates.Count > 0 ? certificates[0] : default;
return certificate;
}
}

private static byte[] SignCms(byte[] content, X509Certificate2 certificate)
{
var contentInfo = new ContentInfo(content);
var signedCms = new SignedCms(contentInfo, true);
var signer = new CmsSigner(certificate);
signedCms.ComputeSignature(signer);
var data = signedCms.Encode();
return data;
}

private static void SaveSignedCms(byte[] cms, string path)
{
var base64Data = Convert.ToBase64String(cms, Base64FormattingOptions.InsertLineBreaks);

using (var output = File.OpenWrite(path))
using (var writer = new StreamWriter(output))
{
writer.Write(base64Data);
}
}

private static byte[] CalculateHash(byte[] data)
{
var provider = new Gost3411_2012_256CryptoServiceProvider();
var hash = provider.ComputeHash(data);
return hash;
}

private static byte[] LoadData(string path)
{
using (var input = File.OpenRead(path))
{
var buffer = new byte[input.Length];
input.Read(buffer, 0, buffer.Length);
return buffer;
}
}

private static void SaveData(byte[] hash, string path)
{
using (var output = File.OpenWrite(path))
{
output.Write(hash, 0, hash.Length);
}
}
}
TestData.zip (6kb) загружен 0 раз(а).

document.txt - подписываемый документ
document.txt.sig - CMS
sign-from-cms.bin - подпись из CMS

TestCryptoProSign.zip (14kb) загружен 1 раз(а).

Это весь проект
Offline Артём Макаров  
#6 Оставлено : 27 февраля 2020 г. 10:30:29(UTC)
Артём Макаров

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 20.02.2017(UTC)
Сообщений: 82

Сказал(а) «Спасибо»: 2 раз
Поблагодарили: 20 раз в 20 постах
CMS подпись - это не подпись хэша данных, это подпись некоторой ASN1 структуры её содержащую.
Существующими средствами .NET подписать существующий хэш в CMS у вас не получится, если только не влезать в ASN1, что не самая лучшая идея.

Реализовать то что вам нужно можно либо через КриптоПро ЭЦП SDK через COM или CryptoApi
https://cpdn.cryptopro.r...03ca56afabeb9c82387.html
https://cpdn.cryptopro.r...mplifiedapihashsign.html
Техническую поддержку оказываем тут
Наша база знаний
thanks 1 пользователь поблагодарил Артём Макаров за этот пост.
Dmitry Komarchev оставлено 27.02.2020(UTC)
Offline Андрей *  
#7 Оставлено : 27 февраля 2020 г. 10:56:53(UTC)
Андрей *

Статус: Сотрудник

Группы: Участники
Дата регистрации:: 26.07.2011(UTC)
Сообщений: 9,054
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1302 раз в 1017 постах
Автор: Артём Макаров Перейти к цитате
CMS подпись - это не подпись хэша данных, это подпись некоторой ASN1 структуры её содержащую.


либо отключить добавление атрибутов, параметр AddEsAttribute в реестре...

Особенности создания подписанных CMS сообщений
Техническую поддержку оказываем тут
Наша база знаний
thanks 1 пользователь поблагодарил Андрей * за этот пост.
Dmitry Komarchev оставлено 27.02.2020(UTC)
Offline Dmitry Komarchev  
#8 Оставлено : 27 февраля 2020 г. 11:40:21(UTC)
Dmitry Komarchev

Статус: Новичок

Группы: Участники
Зарегистрирован: 20.12.2019(UTC)
Сообщений: 6

Сказал(а) «Спасибо»: 3 раз
Автор: Артём Макаров Перейти к цитате
CMS подпись - это не подпись хэша данных, это подпись некоторой ASN1 структуры её содержащую.

Вы имеете в виду SignedAttributes? У меня их вроде нет.
Судя по исходникам в corefx (https://github.com/CryptoProLLC/corefx), там подписывается именно хэш данных?

internal SignerInfoAsn Sign(
ReadOnlyMemory<byte> data,
string contentTypeOid,
bool silent,
out X509Certificate2Collection chainCerts)
{
HashAlgorithmName hashAlgorithmName = PkcsHelpers.GetDigestAlgorithm(DigestAlgorithm);
IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName);

hasher.AppendData(data.Span);
byte[] dataHash = hasher.GetHashAndReset();

SignerInfoAsn newSignerInfo = new SignerInfoAsn();
newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm;

// If the user specified attributes (not null, count > 0) we need attributes.
// If the content type is null we're counter-signing, and need the message digest attr.
// If the content type is otherwise not-data we need to record it as the content-type attr.
if (SignedAttributes?.Count > 0 || contentTypeOid != Oids.Pkcs7Data)
{
List<AttributeAsn> signedAttrs = BuildAttributes(SignedAttributes);

using (var writer = new AsnWriter(AsnEncodingRules.DER))
{
writer.WriteOctetString(dataHash);
signedAttrs.Add(
new AttributeAsn
{
AttrType = new Oid(Oids.MessageDigest, Oids.MessageDigest),
AttrValues = new[] { new ReadOnlyMemory<byte>(writer.Encode()) },
});
}

if (contentTypeOid != null)
{
using (var writer = new AsnWriter(AsnEncodingRules.DER))
{
writer.WriteObjectIdentifier(contentTypeOid);
signedAttrs.Add(
new AttributeAsn
{
AttrType = new Oid(Oids.ContentType, Oids.ContentType),
AttrValues = new[] { new ReadOnlyMemory<byte>(writer.Encode()) },
});
}
}

// Use the serializer/deserializer to DER-normalize the attribute order.
SignedAttributesSet signedAttrsSet = new SignedAttributesSet();
signedAttrsSet.SignedAttributes = PkcsHelpers.NormalizeAttributeSet(
signedAttrs.ToArray(),
normalized => hasher.AppendData(normalized));

// Since this contains user data in a context where BER is permitted, use BER.
// There shouldn't be any observable difference here between BER and DER, though,
// since the top level fields were written by NormalizeSet.
using (AsnWriter attrsWriter = new AsnWriter(AsnEncodingRules.BER))
{
signedAttrsSet.Encode(attrsWriter);
newSignerInfo.SignedAttributes = attrsWriter.Encode();
}

dataHash = hasher.GetHashAndReset();
}

switch (SignerIdentifierType)
{
case SubjectIdentifierType.IssuerAndSerialNumber:
byte[] serial = Certificate.GetSerialNumber();
Array.Reverse(serial);

newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
{
Issuer = Certificate.IssuerName.RawData,
SerialNumber = serial,
};

newSignerInfo.Version = 1;
break;
case SubjectIdentifierType.SubjectKeyIdentifier:
newSignerInfo.Sid.SubjectKeyIdentifier = PkcsPal.Instance.GetSubjectKeyIdentifier(Certificate);
newSignerInfo.Version = 3;
break;
case SubjectIdentifierType.NoSignature:
newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
{
Issuer = SubjectIdentifier.DummySignerEncodedValue,
SerialNumber = new byte[1],
};
newSignerInfo.Version = 1;
break;
default:
Debug.Fail($"Unresolved SignerIdentifierType value: {SignerIdentifierType}");
throw new CryptographicException();
}

if (UnsignedAttributes != null && UnsignedAttributes.Count > 0)
{
List<AttributeAsn> attrs = BuildAttributes(UnsignedAttributes);

newSignerInfo.UnsignedAttributes = PkcsHelpers.NormalizeAttributeSet(attrs.ToArray());
}

bool signed;
Oid signatureAlgorithm;
ReadOnlyMemory<byte> signatureValue;

if (SignerIdentifierType == SubjectIdentifierType.NoSignature)
{
signatureAlgorithm = new Oid(Oids.NoSignature, null);
signatureValue = dataHash;
signed = true;
}
else
{
signed = CmsSignature.Sign(
dataHash,
hashAlgorithmName,
Certificate,
PrivateKey,
silent,
out signatureAlgorithm,
out signatureValue);
}

if (!signed)
{
throw new CryptographicException(SR.Cryptography_Cms_CannotDetermineSignatureAlgorithm);
}

newSignerInfo.SignatureValue = signatureValue;
newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm;

X509Certificate2Collection certs = new X509Certificate2Collection();
certs.AddRange(Certificates);

if (SignerIdentifierType != SubjectIdentifierType.NoSignature)
{
if (IncludeOption == X509IncludeOption.EndCertOnly)
{
certs.Add(Certificate);
}
else if (IncludeOption != X509IncludeOption.None)
{
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;

if (!chain.Build(Certificate))
{
foreach (X509ChainStatus status in chain.ChainStatus)
{
if (status.Status == X509ChainStatusFlags.PartialChain)
{
throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain);
}
}
}

X509ChainElementCollection elements = chain.ChainElements;
int count = elements.Count;
int last = count - 1;

if (last == 0)
{
// If there's always one cert treat it as EE, not root.
last = -1;
}

for (int i = 0; i < count; i++)
{
X509Certificate2 cert = elements[i].Certificate;

if (i == last &&
IncludeOption == X509IncludeOption.ExcludeRoot &&
cert.SubjectName.RawData.AsSpan().SequenceEqual(cert.IssuerName.RawData))
{
break;
}

certs.Add(cert);
}
}
}

chainCerts = certs;
return newSignerInfo;
}
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.