Подписание PDF-файла с помощью внешнего подписанного хэша и сертификата на C#C#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Подписание PDF-файла с помощью внешнего подписанного хэша и сертификата на C#

Сообщение Anonymous »

Мне нужно подписать PDF-файл с помощью внешних сервисов. Поэтому сначала мне нужна функция типа

Код: Выделить всё

public string GetHashToSign(byte[] unsignedPdf, X509Certificate cert)
{
// generate and return a hash of the prepared pdf document
}
Затем этот хеш будет отправлен на внешний веб-сервис. После того, как хеш будет отправлен на веб-сервис, пользователь будет перенаправлен на сервис, где ему/ей необходимо войти в систему и подтвердить вывеску. Когда вывеска будет подтверждена, пользователь будет перенаправлен на другую страницу, где будет загружен подписанный хэш и создан подписанный PDF-файл. На данный момент мне нужна функция типа

Код: Выделить всё

public byte[] SignDocument(byte[] unsignedPdf, X509Certificate cert, string signedHash) {
// Put all things together and return signed pdf
}
В настоящее время я пытаюсь сделать это с помощью iText и/или Aspose. Но я не могу понять, как разделить эти два шага и выполнить их на разных страницах.
РЕДАКТИРОВАТЬ:
Вот мое решение, основанное на ответе @ Glenner003.
Может кому-нибудь поможет.

Код: Выделить всё

public class PreparedResponse
{
public byte[] PreparedFile { get; set; }
public byte[] BytesToSign { get; set; }
}

Код: Выделить всё

public class SignPdfWithIText7
{
private static IBouncyCastleFactory BcFactory = BouncyCastleFactoryCreator.GetFactory();

public PreparedResponse PreparePdfForSignage(
byte[] pdf,
string certificate,
string digestAlgorithmOid,
SignerProperties signerProperties)
{

byte[] preparedBytes;
CMSContainer cmsContainer;
using MemoryStream unsignePdfStream = new(pdf);
using MemoryStream preparedPdfStream = new();
using (PdfReader reader = new PdfReader(unsignePdfStream))
using (MemoryStream outputStream = preparedPdfStream)
{
var parser = BcFactory.CreateX509CertificateParser();
IX509Certificate[] certificateChain = [.. parser.ReadAllCerts(Convert.FromBase64String(certificate))];

string digestAlgorithm = DigestAlgorithms.GetDigest(digestAlgorithmOid);

// 1. Preparing the container to get a size estimate
cmsContainer = new CMSContainer();
cmsContainer.AddCertificates(certificateChain);
cmsContainer.GetSignerInfo()
.SetSigningCertificateAndAddToSignedAttributes(certificateChain[0], digestAlgorithmOid);
// In a later version the default algorithm is extracted from the certificate
cmsContainer.GetSignerInfo().SetSignatureAlgorithm(GetAlgorithmOidFromCertificate(certificateChain[0]));
cmsContainer.GetSignerInfo().SetDigestAlgorithm(new AlgorithmIdentifier(digestAlgorithmOid));

// Next to these required fields, we can add validation data and other signed or unsigned attributes with
// the following methods:
// cmsContainer.GetSignerInfo().SetCrlResponses();
// cmsContainer.GetSignerInfo().SetOcspResponses();
// cmsContainer.GetSignerInfo().AddSignedAttribute();
// cmsContainer.GetSignerInfo().AddUnSignedAttribute();

// Get the estimated size
long estimatedSize = cmsContainer.GetSizeEstimation();

var digest = BcFactory.CreateIDigest(digestAlgorithm);
// Add enough space for the digest
estimatedSize += digest.GetDigestLength() * 2L + 2;
// Duplicate the size for conversion to hex
estimatedSize *= 2;

PdfTwoPhaseSigner signer = new PdfTwoPhaseSigner(reader, outputStream);
signer.SetStampingProperties(new StampingProperties().UseAppendMode());

// 2. Prepare the document by adding the signature field and getting the digest in return
byte[] documentDigest = signer.PrepareDocumentForSignature(signerProperties, digestAlgorithm,
PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached, (int)estimatedSize, false);

// 3. Add the digest to the CMS container, because this will be part of the items to be signed
cmsContainer.GetSignerInfo().SetMessageDigest(documentDigest);
outputStream.Close();
preparedBytes = outputStream.ToArray();
}

// 4. This step is completely optional.  Add the CMS container to the document
// to avoid having to build it again, or storing it separately from the document
PreparedResponse response;
using (PdfReader reader = new PdfReader(new MemoryStream(preparedBytes)))
using (PdfDocument document = new PdfDocument(reader))
using (MemoryStream outputStream = new MemoryStream())
{
PdfTwoPhaseSigner.AddSignatureToPreparedDocument(document, signerProperties.GetFieldName(), outputStream,
cmsContainer.Serialize());
outputStream.Close();
response = new()
{
PreparedFile = outputStream.ToArray(),
BytesToSign = cmsContainer.GetSerializedSignedAttributes()
};
}

// 5. The serialized signed attributes is what actually needs to be signed
// sometimes we have to create a digest from it, sometimes this needs to be sent as is.
return response;
}

private AlgorithmIdentifier GetAlgorithmOidFromCertificate(IX509Certificate x509Certificate)
{
ITbsCertificateStructure tbsCert = BcFactory.CreateTBSCertificate(x509Certificate.GetTbsCertificate());
if (tbsCert.GetSubjectPublicKeyInfo().GetAlgorithm().GetParameters() != null)
{
if (tbsCert.GetSubjectPublicKeyInfo().GetAlgorithm().GetParameters().IsNull())
{
return new AlgorithmIdentifier(tbsCert.GetSubjectPublicKeyInfo().GetAlgorithm().GetAlgorithm
().GetId(), BcFactory.CreateDERNull());
}
return new AlgorithmIdentifier(tbsCert.GetSubjectPublicKeyInfo().GetAlgorithm().GetAlgorithm
().GetId(), tbsCert.GetSubjectPublicKeyInfo().GetAlgorithm().GetParameters().ToASN1Primitive());
}
return new AlgorithmIdentifier(tbsCert.GetSubjectPublicKeyInfo().GetAlgorithm().GetAlgorithm().GetId());
}

public byte[] SignPreparedPdf(byte[] preparedPdf, string fieldName, byte[] signature)
{
using (PdfReader reader = new PdfReader((new MemoryStream(preparedPdf))))
using (PdfDocument document = new PdfDocument(reader))
using (MemoryStream outputStream = new MemoryStream())
{
// 1. Read the documents CMS container
SignatureUtil su = new SignatureUtil(document);
PdfSignature sig = su.GetSignature(fieldName);
PdfString encodedCMS = sig.GetContents();
byte[] encodedCMSdata = encodedCMS.GetValueBytes();
CMSContainer cmsContainer = new CMSContainer(encodedCMSdata);

// 2. Add the signatureValue to the CMS
cmsContainer.GetSignerInfo().SetSignature(signature);

PdfTwoPhaseSigner.AddSignatureToPreparedDocument(document, fieldName, outputStream,
cmsContainer.Serialize());

outputStream.Close();
return outputStream.ToArray();
}
}
}
В целом это копия образца, предоставленного мне @Glenner003, есть лишь некоторые изменения, соответствующие моим потребностям.
Как я Я не очень часто работаю с вывесками в формате PDF, просто позвольте мне сказать, что мне пришлось хэшировать BytesToSign перед отправкой его в веб-сервис. Но это может меняться от сервиса к сервису... я не знаю.

Подробнее здесь: https://stackoverflow.com/questions/792 ... in-c-sharp
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»