Статус: Новичок
Группы: Участники
Зарегистрирован: 18.05.2016(UTC) Сообщений: 7
Сказал(а) «Спасибо»: 2 раз Поблагодарили: 1 раз в 1 постах
|
Доброго времени суток! Имеется несколько вопросов по подписи хеша pdf-файла. На текущий момент реализована подпись pdf-файла с помощью iTextSharp и Browser plug-in: 1) Приложение на C# создает pdf-файл; 2) С помощью iTextSharp внедряется контейнер с пустой подписью и вычисляется хеш по алгоритму GOST3411; 3) С помощью Browser plug-in выполняется вычисление подписи на основании полученного хеша, записанного в виде строки шестнадцатеричных чисел; 4) С помощью iTextSharp в контейнер с пустой подписью внедряется вычисленная подпись. Главный модуль:
Код:using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace CryptoTest
{
internal class Program
{
private static void Main()
{
byte[] sourcePdf = CreatePdf();
byte[] unsignedPdf = PdfSigner.GetUnsignedPdfWithSignatureContainer(sourcePdf, out byte[] hash);
string hashInHex = BitConverter.ToString(hash).Replace("-", string.Empty);
Console.WriteLine(hashInHex);
string signedData = string.Empty, line;
while ((line = Console.ReadLine()) != null)
{
signedData += line;
}
byte[] signedHash = Convert.FromBase64String(signedData);
byte[] signedPdf = PdfSigner.GetSignedPdf(unsignedPdf, signedHash);
File.WriteAllBytes("Z:\\Test\\signedFile.pdf", signedPdf);
}
public static byte[] CreatePdf()
{
using (MemoryStream ms = new MemoryStream())
{
Document document = new Document(PageSize.A4, 25, 25, 30, 30);
PdfWriter writer = PdfWriter.GetInstance(document, ms);
document.Open();
document.Add(new Paragraph("Hello World!"));
document.Close();
writer.Close();
return ms.ToArray();
}
}
}
}
Модуль подписи pdf с помощью iTextSharp:
Код:using System;
using System.IO;
using System.Security.Cryptography;
using com.itextpdf.text.pdf.security;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
namespace CryptoTest
{
public class PdfSigner
{
private const int BufferSize = 8192;
private const int FirstPage = 1;
private const string SignatureFieldName = "SignatureField";
public static byte[] GetUnsignedPdfWithSignatureContainer(byte[] pdfBytes, out byte[] hash)
{
using (MemoryStream output = new MemoryStream())
{
using (PdfReader pdfReader = new PdfReader(pdfBytes))
{
using (PdfStamper stamper = PdfStamper.CreateSignature(pdfReader, output, '\0'))
{
PdfSignatureAppearance sap = stamper.SignatureAppearance;
sap.SetVisibleSignature(new Rectangle(205, 700, 390, 800), FirstPage, SignatureFieldName);
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(new PdfName("CryptoPro PDF"), PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(sap, external, BufferSize);
hash = HashAlgorithm.Create("GOST3411").ComputeHash(sap.GetRangeStream());
}
}
return output.ToArray();
}
}
public static byte[] GetSignedPdf(byte[] unsignedPdfWithContainer, byte[] signedHash)
{
using (PdfReader pdfReader = new PdfReader(unsignedPdfWithContainer))
{
using (MemoryStream output = new MemoryStream())
{
IExternalSignatureContainer external = new HashSignatureContainer(signedHash);
MakeSignature.SignDeferred(pdfReader, SignatureFieldName, output, external);
return output.ToArray();
}
}
}
private class HashSignatureContainer : IExternalSignatureContainer
{
private readonly byte[] _signedHash;
public HashSignatureContainer(byte[] signedHash)
{
_signedHash = signedHash;
}
public byte[] Sign(Stream data)
{
return _signedHash;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
throw new NotImplementedException();
}
}
}
}
Модуль подписи хеша с помощью Browser plug-in:
Код:<html>
<head>
<meta charset="utf-8" />
<title>Подпись хеша</title>
<script src="jquery-1.7.1.js" type="text/javascript"></script>
<script src="cadesplugin_api.js" type="text/javascript"></script>
<script type="text/javascript">
function GetErrorMessage(err) {
var text = err.message;
return text ? err.number && (text += " (" + err.number + ")") : text = err, text;
}
function SignHashAsync(dataToSign, thumbprint) {
return new Promise(function(resolve, reject) {
cadesplugin.async_spawn(function*(arg) {
var oStore;
try {
oStore = yield cadesplugin.CreateObjectAsync("CAdESCOM.Store");
yield oStore.Open(cadesplugin.CAPICOM_CURRENT_USER_STORE, cadesplugin.CAPICOM_MY_STORE, cadesplugin.CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);
var allCertificates = yield oStore.Certificates;
var oCertificates = yield allCertificates.Find(cadesplugin.CAPICOM_CERTIFICATE_FIND_SHA1_HASH, thumbprint);
var certificatesCount = yield oCertificates.Count;
if (certificatesCount === 0) {
return reject("Сертификат не найден в хранилище.");
}
var oCertificate = yield oCertificates.Item(1);
var oHashedData = yield cadesplugin.CreateObjectAsync("CAdESCOM.HashedData");
oHashedData.propset_Algorithm(cadesplugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411);
yield oHashedData.SetHashValue(dataToSign);
var oSigner = yield cadesplugin.CreateObjectAsync("CAdESCOM.CPSigner");
oSigner.propset_Certificate(oCertificate);
var oCadesSignedData = yield cadesplugin.CreateObjectAsync("CAdESCOM.CadesSignedData");
oCadesSignedData.propset_ContentEncoding(cadesplugin.CADESCOM_BASE64_TO_BINARY);
var signatureHex = yield oCadesSignedData.SignHash(oHashedData, oSigner, cadesplugin.CADESCOM_CADES_BES);
return resolve(signatureHex);
} catch (err) {
return reject("Не удалось создать подпись. " + GetErrorMessage(err));
} finally {
if (oStore) {
yield oStore.Close();
}
}
});
});
}
function signRequest() {
var thumbprint = $("#thumbprint").val();
if (thumbprint == null) {
return false;
}
try {
var dataForSign = $("#hash").val();
var signPromise = SignHashAsync(dataForSign, thumbprint);
signPromise.then(
function (signedData) {
if (!signedData) {
alert('Не удается найти подписанные данные для отправки на сервер.');
return false;
}
$("#sign").val(signedData);
},
function (error) {
console.log(error);
alert(error);
});
} catch (error) {
alert('Не удалось подписать данные запроса: ' + error);
}
return false;
}
</script>
</head>
<body>
<table>
<tr>
<td style="font-weight: bold;">Хеш</td>
<td>
<input id="hash" style="width: 50em" type="text" />
</td>
</tr>
<tr>
<td style="font-weight: bold;">Подпись</td>
<td>
<textarea id="sign" cols="100" rows="5" style="width: 50em"></textarea>
</td>
</tr>
<tr>
<td style="font-weight: bold;">Отпечаток</td>
<td>
<input id="thumbprint" style="width: 50em" type="text" value="f1dbdb0272408727a6120573ca781c28c4465de3" />
</td>
</tr>
</table>
<button class="btn" onclick="signRequest();" style="margin-top: 10px;">Подписать</button>
</body>
</html>
Такая реализация позволяет делать подпись pdf-файла, которая проходит валидацию с помощью Adobe Reader + КриптоПро PDF. Но при реализации возникла пара вопросов: 1) Если в коде главного модуля преобразование хеша из массива байтов в строку шестнадцатиричных чисел: Код:string hashInHex = BitConverter.ToString(hash).Replace("-", string.Empty);
заменить на преобразование хеша из массива байтов в Base64-строку: Код:string hashBase64 = Convert.ToBase64String(hash);
а в коде модуля подписи хеша с помощью Browser plug-in добавить строку: Код:oHashedData.propset_DataEncoding(cadesplugin.CADESCOM_BASE64_TO_BINARY);
перед строкой: Код:yield oHashedData.SetHashValue(dataToSign);
то возникает ошибка: Цитата:The data is invalid. (0x8007000D) Почему такое происходит? На форуме были какие-то замечания о том что, Browser plug-in как-то по особенному работает с Base64, строка Base64 должна быть как-то развернута или что-то в этом роде... Пробовал по разному крутить строку, но положительного результата не получил. Где про это можно прочитать более подробно? И в чем ошибка? 2) Как можно подписать хеш pdf-файла, по аналогии с подписью Browser plug-in, но кодом на C# и/или с помощью КриптоПро .NET? Пытался сделать следующим образом:
Код:private static byte[] SignHash(byte[] hash, X509Certificate2 certificate)
{
var contentInfo = new ContentInfo(hash);
var signedCms = new SignedCms(contentInfo, false);
signedCms.ComputeSignature(new CmsSigner(certificate), false);
return signedCms.Encode();
}
Но полученная подпись не проходит валидацию с помощью Adobe Reader + КриптоПро PDF. Также попытался сделать подпись хеша через CAdESCOM:
Код:private static byte[] SignData(byte[] bytesToSign, X509Certificate2 certificate)
{
CPHashedData oHashedData = new CPHashedData();
oHashedData.Algorithm = (CAPICOM_HASH_ALGORITHM)CADESCOM_HASH_ALGORITHM.CADESCOM_HASH_ALGORITHM_CP_GOST_3411;
string hashInHex = BitConverter.ToString(bytesToSign).Replace("-", string.Empty);
oHashedData.Hash(hashInHex);
CPSigner oSigner = new CPSigner();
oSigner.Certificate = GetCertificate(certificate.Thumbprint);
CadesSignedData oCadesSignedData = new CadesSignedData();
oCadesSignedData.ContentEncoding = CADESCOM_CONTENT_ENCODING_TYPE.CADESCOM_BASE64_TO_BINARY;
var signatureHex = oCadesSignedData.SignHash((HashedData)oHashedData, oSigner, CADESCOM_CADES_TYPE.CADESCOM_CADES_BES);
return Convert.FromBase64String(signatureHex);
}
private static ICertificate GetCertificate(string thumbprint)
{
var oStore = new Store();
oStore.Open(CAPICOM_STORE_LOCATION.CAPICOM_CURRENT_USER_STORE, "MY", CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);
return oStore.Certificates.Cast<ICertificate>().FirstOrDefault(c => string.Equals(c.Thumbprint, thumbprint, StringComparison.InvariantCultureIgnoreCase));
}
Но полученная подпись также не проходит валидацию с помощью Adobe Reader + КриптоПро PDF. Может я что-то упустил? Заранее благодарю за помощь! PS: Если добавил тему не в правильную ветку форма, то прошу переместить в правильно место.
|
1 пользователь поблагодарил KAS за этот пост.
|
|