Код: Выделить всё
public string GetHashToSign(byte[] unsignedPdf, X509Certificate cert)
{
// generate and return a hash of the prepared pdf document
}
Код: Выделить всё
public byte[] SignDocument(byte[] unsignedPdf, X509Certificate cert, string signedHash) {
// Put all things together and return signed pdf
}
РЕДАКТИРОВАТЬ:
Вот мое решение, основанное на ответе @ 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();
}
}
}
Как я Я не очень часто работаю с вывесками в формате PDF, просто позвольте мне сказать, что мне пришлось хэшировать BytesToSign перед отправкой его в веб-сервис. Но это может меняться от сервиса к сервису... я не знаю.
Подробнее здесь: https://stackoverflow.com/questions/792 ... in-c-sharp