Files
TSpdf/TSpdf.tests/TSpdf.sign.tests/TSpdf/signatures/testutils/SignaturesCompareTool.cs
2026-05-27 17:09:59 +02:00

556 lines
31 KiB
C#

/*
This file is part of the TSpdf (R) project.
Copyright (c) 1987-2023 TSpdf
Authors: Bruno Lowagie, Paulo Soares, et al.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License version 3
as published by the Free Software Foundation with the addition of the
following permission added to Section 15 as permitted in Section 7(a):
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
TSpdf GROUP. TSpdf GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
OF THIRD PARTY RIGHTS
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, see http://www.gnu.org/licenses or write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA, 02110-1301 USA, or download the license from the following URL:
http://TSpdfpdf.com/terms-of-use/
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License.
In accordance with Section 7(b) of the GNU Affero General Public License,
a covered work must retain the producer line in every PDF that is created
or manipulated using TSpdf.
You can be released from the requirements of the license by purchasing
a commercial license. Buying such a license is mandatory as soon as you
develop commercial activities involving the TSpdf software without
disclosing the source code of your own applications.
These activities include: offering paid services to customers as an ASP,
serving PDFs on the fly in a web application, shipping TSpdf with a closed
source product.
For more information, please contact TSpdf Software Corp. at this
address: sales@TSpdfpdf.com
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using TSpdf.Bouncycastleconnector;
using TSpdf.Commons.Bouncycastle;
using TSpdf.Commons.Bouncycastle.Asn1;
using TSpdf.Commons.Bouncycastle.Asn1.Util;
using TSpdf.Commons.Utils;
using TSpdf.IO.Util;
using TSpdf.Kernel.Pdf;
using TSpdf.Signatures;
using TSpdf.Test;
namespace TSpdf.Signatures.Testutils {
public class SignaturesCompareTool {
private static readonly IBouncyCastleFactory BOUNCY_CASTLE_FACTORY = BouncyCastleFactoryCreator.GetFactory
();
private const String OID_MESSAGE_DIGEST = "1.2.840.113549.1.9.4";
private const String OID_SIGNED_DATA = "1.2.840.113549.1.7.2";
private const String OID_TST_INFO = "1.2.840.113549.1.9.16.1.4";
private const String OID_SIGNING_TIME = "1.2.840.113549.1.9.5";
private const String OID_SIGNATURE_TIMESTAMP_ATTRIBUTE = "1.2.840.113549.1.9.16.2.14";
private const String OID_ADBE_REVOCATION_INFO_ARCHIVAL = "1.2.840.113583.1.1.8";
private const String OID_OCSP_RESPONSE = "1.3.6.1.5.5.7.48.1.1";
private const String OID_OCSP_NONCE_EXTENSION = "1.3.6.1.5.5.7.48.1.2";
private static readonly IASN1Dump DUMP = BOUNCY_CASTLE_FACTORY.CreateASN1Dump();
private static readonly ICollection<String> IGNORED_OIDS;
static SignaturesCompareTool() {
HashSet<String> tempSet = new HashSet<String>();
tempSet.Add(OID_MESSAGE_DIGEST);
tempSet.Add(OID_TST_INFO);
tempSet.Add(OID_SIGNING_TIME);
tempSet.Add(OID_OCSP_NONCE_EXTENSION);
IGNORED_OIDS = JavaCollectionsUtil.UnmodifiableSet(tempSet);
}
public static String CompareSignatures(String dest, String cmp) {
return CompareSignatures(dest, cmp, new ReaderProperties(), new ReaderProperties());
}
public static String CompareSignatures(String dest, String cmp, ReaderProperties destProperties, ReaderProperties
cmpProperties) {
TSpdfTest.PrintOutCmpPdfNameAndDir(dest, cmp);
StringBuilder errorText = new StringBuilder();
try {
using (PdfDocument outDocument = new PdfDocument(new PdfReader(dest, destProperties))) {
using (PdfDocument cmpDocument = new PdfDocument(new PdfReader(cmp, cmpProperties))) {
SignatureUtil outSigUtil = new SignatureUtil(outDocument);
SignatureUtil cmpSigUtil = new SignatureUtil(cmpDocument);
if (!Enumerable.SequenceEqual(cmpSigUtil.GetSignatureNames(), outSigUtil.GetSignatureNames())) {
AddError(errorText, "Signatures lists are different:", outSigUtil.GetSignatureNames().ToString(), cmpSigUtil
.GetSignatureNames().ToString());
}
IList<String> signatures = cmpSigUtil.GetSignatureNames();
foreach (String sig in signatures) {
IASN1Sequence outSignedData = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(GetSignatureContent(sig, outSigUtil
));
IASN1Sequence cmpSignedData = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(GetSignatureContent(sig, cmpSigUtil
));
bool isEqual = CompareSignedData(outSignedData, cmpSignedData, errorText);
if (!isEqual) {
CreateTxtFilesFromAsn1Sequences(outSignedData, cmpSignedData, dest, sig, errorText);
}
}
CompareDssEntries(outDocument, cmpDocument, dest, errorText);
}
}
}
catch (Exception e) {
errorText.Append(e.Message);
}
return String.IsNullOrEmpty(errorText.ToString()) ? null : errorText.ToString();
}
private static void CreateTxtFilesFromAsn1Sequences(IASN1Sequence outSignedData, IASN1Sequence cmpSignedData
, String dest, String sig, StringBuilder errorText) {
String sigFileName = dest.JSubstring(0, dest.LastIndexOf("."));
String outSigFile = sigFileName + "_" + sig + "_out.txt";
String cmpSigFile = sigFileName + "_" + sig + "_cmp.txt";
WriteToFile(outSigFile, sig + "\n" + DUMP.DumpAsString(outSignedData, true) + "\n");
WriteToFile(cmpSigFile, sig + "\n" + DUMP.DumpAsString(cmpSignedData, true) + "\n");
errorText.Insert(0, "See signature output files: " + "\nout: " + UrlUtil.GetNormalizedFileUriString(outSigFile
) + "\ncmp: " + UrlUtil.GetNormalizedFileUriString(cmpSigFile) + "\n");
}
private static bool CompareDssEntries(PdfDocument outDocument, PdfDocument cmpDocument, String dest, StringBuilder
errorText) {
PdfDictionary outDss = outDocument.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.DSS);
PdfDictionary cmpDss = cmpDocument.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.DSS);
if (outDss == null || cmpDss == null) {
if (outDss == cmpDss) {
return true;
}
AddError(errorText, "DSS dictionaries are different");
return false;
}
bool ocspCertificatesEqual = CompareRevocationDataFromDss(outDss, cmpDss, PdfName.OCSPs, dest, errorText,
(outSequence, cmpSequence, errorStringBuilder) => SignaturesCompareTool.CompareAsn1Structures(outSequence
, cmpSequence, errorStringBuilder));
bool crlCertificatesEqual = CompareRevocationDataFromDss(outDss, cmpDss, PdfName.CRLs, dest, errorText, (outSequence
, cmpSequence, errorStringBuilder) => SignaturesCompareTool.CompareSequencesWithSignatureValue(outSequence
, cmpSequence, errorStringBuilder));
return ocspCertificatesEqual && crlCertificatesEqual;
}
private static bool CompareRevocationDataFromDss(PdfDictionary outDss, PdfDictionary cmpDss, PdfName entryName
, String dest, StringBuilder errorText, SignaturesCompareTool.SequenceComparator comparator) {
String errorMessage = entryName.GetValue() + " entries inside DSS dictionaries are different";
PdfArray outDssEntry = outDss.GetAsArray(entryName);
PdfArray cmpDssEntry = cmpDss.GetAsArray(entryName);
if (outDssEntry == null || cmpDssEntry == null) {
if (outDssEntry == cmpDssEntry) {
return true;
}
AddError(errorText, errorMessage);
return false;
}
if (outDssEntry.Size() != cmpDssEntry.Size()) {
AddError(errorText, errorMessage);
return false;
}
for (int i = 0; i < outDssEntry.Size(); ++i) {
PdfStream outDssEntryItem = outDssEntry.GetAsStream(i);
PdfStream cmpDssEntryItem = cmpDssEntry.GetAsStream(i);
if (outDssEntryItem == null || cmpDssEntryItem == null) {
if (outDssEntryItem == cmpDssEntryItem) {
continue;
}
AddError(errorText, errorMessage);
return false;
}
IASN1Sequence outDecodedItem = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(outDssEntryItem.GetBytes());
IASN1Sequence cmpDecodedItem = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(cmpDssEntryItem.GetBytes());
if (!comparator(outDecodedItem, cmpDecodedItem, errorText)) {
CreateTxtFilesFromAsn1Sequences(outDecodedItem, cmpDecodedItem, dest, "DSS_" + entryName.GetValue() + "_"
+ i, errorText);
return false;
}
}
return true;
}
private static bool CompareOcspResponses(IASN1Encodable[] outOcspResponse, IASN1Encodable[] cmpOcspResponse
, StringBuilder errorText) {
if (outOcspResponse.Length != 2 || cmpOcspResponse.Length != 2) {
AddError(errorText, "OCSP response has unexpected structure");
}
IASN1OctetString outResponseString = BOUNCY_CASTLE_FACTORY.CreateASN1OctetString(outOcspResponse[1]);
IASN1OctetString cmpResponseString = BOUNCY_CASTLE_FACTORY.CreateASN1OctetString(cmpOcspResponse[1]);
if (outResponseString.Equals(cmpResponseString)) {
return true;
}
IASN1Sequence parsedOutResponse = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(outResponseString.GetOctets());
IASN1Sequence parsedCmpResponse = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(cmpResponseString.GetOctets());
return CompareSequencesWithSignatureValue(parsedOutResponse, parsedCmpResponse, errorText);
}
/// <summary>SignedData is top-level CMS-object for signatures, see "5.1.</summary>
/// <remarks>
/// SignedData is top-level CMS-object for signatures, see "5.1. SignedData Type" at
/// https://datatracker.ietf.org/doc/html/rfc5652#section-5.1 .
/// </remarks>
/// <param name="outSignedData">current output signed data</param>
/// <param name="cmpSignedData">reference signed data used for comparison as a ground truth</param>
/// <param name="errorText">string builder in order to accumulate errors</param>
/// <returns>true if signed data objects are the similar, false otherwise</returns>
private static bool CompareSignedData(IASN1Sequence outSignedData, IASN1Sequence cmpSignedData, StringBuilder
errorText) {
if (outSignedData.Size() != cmpSignedData.Size() || outSignedData.Size() != 2) {
AddError(errorText, "Signature top level elements count is incorrect (should be exactly 2):", outSignedData
.Size().ToString(), cmpSignedData.Size().ToString());
return false;
}
IASN1ObjectIdentifier outObjId = BOUNCY_CASTLE_FACTORY.CreateASN1ObjectIdentifier(outSignedData.GetObjectAt
(0));
IASN1ObjectIdentifier cmpObjId = BOUNCY_CASTLE_FACTORY.CreateASN1ObjectIdentifier(cmpSignedData.GetObjectAt
(0));
if (!outObjId.Equals(cmpObjId) || !outObjId.GetId().Equals(OID_SIGNED_DATA)) {
AddError(errorText, "Signatures object identifier is incorrect (should be " + OID_SIGNED_DATA + ")", outObjId
.GetId().ToString(), cmpObjId.GetId().ToString());
return false;
}
IASN1Sequence outContent = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(BOUNCY_CASTLE_FACTORY.CreateASN1TaggedObject
(outSignedData.GetObjectAt(1)).GetObject());
IASN1Sequence cmpContent = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(BOUNCY_CASTLE_FACTORY.CreateASN1TaggedObject
(cmpSignedData.GetObjectAt(1)).GetObject());
if (outContent.Size() != cmpContent.Size()) {
AddError(errorText, "Signatures base elements counts are different", outContent.Size().ToString(), cmpContent
.Size().ToString());
return false;
}
int signerInfoIndex = GetSignerInfoIndex(cmpContent);
if (BOUNCY_CASTLE_FACTORY.CreateASN1Set(outContent.GetObjectAt(signerInfoIndex)) == null) {
AddError(errorText, "SignerInfo object indexes are different", null, null);
return false;
}
for (int i = 0; i < cmpContent.Size(); i++) {
// SignerInfo objects will be compared separately
if (i == signerInfoIndex) {
continue;
}
// Sequences and sets related to timestamp token info should be ignored.
if (OID_TST_INFO.Equals(GetASN1ObjectId(cmpContent.GetObjectAt(i).ToASN1Primitive())) && OID_TST_INFO.Equals
(GetASN1ObjectId(outContent.GetObjectAt(i).ToASN1Primitive()))) {
continue;
}
if (!cmpContent.GetObjectAt(i).Equals(outContent.GetObjectAt(i))) {
AddError(errorText, "SignedData objects are different", null, null);
return false;
}
}
IASN1Set cmpSignerInfos = BOUNCY_CASTLE_FACTORY.CreateASN1Set(cmpContent.GetObjectAt(signerInfoIndex));
IASN1Set outSignerInfos = BOUNCY_CASTLE_FACTORY.CreateASN1Set(outContent.GetObjectAt(signerInfoIndex));
// Currently, TSpdf signature validation mechanism do not support signatures,
// containing more than one SignerInfo entry. However, it is still valid signature.
if (cmpSignerInfos.Size() != outSignerInfos.Size() || cmpSignerInfos.Size() != 1) {
AddError(errorText, "Incorrect SignerInfos objects count", outSignerInfos.Size().ToString(), cmpSignerInfos
.Size().ToString());
return false;
}
IASN1Sequence outSignerInfo = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(outSignerInfos.GetObjectAt(0));
IASN1Sequence cmpSignerInfo = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(cmpSignerInfos.GetObjectAt(0));
return CompareSequencesWithSignatureValue(outSignerInfo, cmpSignerInfo, errorText);
}
private static bool CompareSequencesWithSignatureValue(IASN1Sequence outSequence, IASN1Sequence cmpSequence
, StringBuilder errorText) {
if (cmpSequence.Size() != outSequence.Size()) {
AddError(errorText, "Incorrect SignerInfo entries count", outSequence.Size().ToString(), cmpSequence.Size(
).ToString());
return false;
}
for (int i = 0; i < cmpSequence.Size(); i++) {
// Skipping comparison of encoded strings fields which are SignatureValue fields.
// They are expected to be different.
if (BOUNCY_CASTLE_FACTORY.CreateASN1OctetString(outSequence.GetObjectAt(i)) != null || BOUNCY_CASTLE_FACTORY
.CreateASN1BitString(outSequence.GetObjectAt(i)) != null) {
if (outSequence.GetObjectAt(i).GetType().Equals(cmpSequence.GetObjectAt(i).GetType())) {
continue;
}
else {
AddError(errorText, "Signature values indexes are different!", null, null);
return false;
}
}
if (!CompareAsn1Structures(outSequence.GetObjectAt(i).ToASN1Primitive(), cmpSequence.GetObjectAt(i).ToASN1Primitive
(), errorText)) {
return false;
}
}
return true;
}
private static bool CompareAsn1Structures(IASN1Primitive @out, IASN1Primitive cmp, StringBuilder errorText
) {
if (!@out.GetType().Equals(cmp.GetType())) {
AddError(errorText, "ASN1 objects types are different", @out.GetType().FullName, cmp.GetType().FullName);
return false;
}
if (BOUNCY_CASTLE_FACTORY.CreateASN1TaggedObject(cmp) != null) {
return CompareAsn1Structures(BOUNCY_CASTLE_FACTORY.CreateASN1TaggedObject(@out).GetObject(), BOUNCY_CASTLE_FACTORY
.CreateASN1TaggedObject(cmp).GetObject(), errorText);
}
else {
if (BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(cmp) != null) {
if (!CompareContainers(BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(@out).ToArray(), BOUNCY_CASTLE_FACTORY.CreateASN1Sequence
(cmp).ToArray(), errorText)) {
AddError(errorText, "ASN1Sequence objects are different");
return false;
}
}
else {
if (BOUNCY_CASTLE_FACTORY.CreateASN1Set(cmp) != null) {
if (!CompareContainers(BOUNCY_CASTLE_FACTORY.CreateASN1Set(@out).ToArray(), BOUNCY_CASTLE_FACTORY.CreateASN1Set
(cmp).ToArray(), errorText)) {
AddError(errorText, "ASN1Set objects are different");
return false;
}
}
else {
if (BOUNCY_CASTLE_FACTORY.CreateASN1GeneralizedTime(cmp) != null || BOUNCY_CASTLE_FACTORY.CreateASN1UTCTime
(cmp) != null) {
// Ignore time values since usually they shouldn't be equal
return true;
}
else {
if (!cmp.Equals(@out)) {
AddError(errorText, "ASN1 objects are different", DUMP.DumpAsString(@out, true), DUMP.DumpAsString(cmp, true
));
return false;
}
}
}
}
}
return true;
}
private static bool CompareContainers(IASN1Encodable[] outArray, IASN1Encodable[] cmpArray, StringBuilder
errorText) {
if (cmpArray.Length != outArray.Length) {
AddError(errorText, "Container lengths are different", JavaUtil.IntegerToString(outArray.Length), JavaUtil.IntegerToString
(cmpArray.Length));
return false;
}
String cmpASN1ObjectId = GetASN1ObjectId(cmpArray);
String outASN1ObjectId = GetASN1ObjectId(outArray);
if (!Object.Equals(cmpASN1ObjectId, outASN1ObjectId)) {
AddError(errorText, "Containers ids are different", outASN1ObjectId, cmpASN1ObjectId);
return false;
}
if (IGNORED_OIDS.Contains(cmpASN1ObjectId)) {
return true;
}
if (OID_SIGNATURE_TIMESTAMP_ATTRIBUTE.Equals(cmpASN1ObjectId)) {
return CompareTimestampAttributes(outArray, cmpArray, errorText);
}
if (OID_OCSP_RESPONSE.Equals(cmpASN1ObjectId)) {
return CompareOcspResponses(outArray, cmpArray, errorText);
}
if (OID_ADBE_REVOCATION_INFO_ARCHIVAL.Equals(cmpASN1ObjectId)) {
return CompareRevocationInfoArchivalAttribute(outArray, cmpArray, errorText);
}
for (int i = 0; i < cmpArray.Length; i++) {
if (!CompareAsn1Structures(outArray[i].ToASN1Primitive(), cmpArray[i].ToASN1Primitive(), errorText)) {
return false;
}
}
return true;
}
/// <summary>See ISO 32000-2, 12.8.3.3.2 "Revocation of CMS-based signatures"</summary>
/// <param name="out">out signature revocation info attribute value</param>
/// <param name="cmp">cmp signature revocation info attribute value</param>
/// <param name="errorText">string builder in order to accumulate errors</param>
/// <returns>true if signed data objects are the similar, false otherwise</returns>
private static bool CompareRevocationInfoArchivalAttribute(IASN1Encodable[] @out, IASN1Encodable[] cmp, StringBuilder
errorText) {
String structureIsInvalidError = "Signature revocation info archival attribute structure is invalid";
if (!IsExpectedRevocationInfoArchivalAttributeStructure(@out) || !IsExpectedRevocationInfoArchivalAttributeStructure
(cmp)) {
AddError(errorText, structureIsInvalidError, String.Join("", JavaUtil.ArraysToEnumerable(@out).Select((e) =>
DUMP.DumpAsString(e)).ToList()), String.Join("", JavaUtil.ArraysToEnumerable(cmp).Select((e) => DUMP.
DumpAsString(e)).ToList()));
return false;
}
IASN1Sequence outSequence = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(BOUNCY_CASTLE_FACTORY.CreateASN1Set(@out
[1]).GetObjectAt(0).ToASN1Primitive());
IASN1Sequence cmpSequence = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(BOUNCY_CASTLE_FACTORY.CreateASN1Set(cmp
[1]).GetObjectAt(0).ToASN1Primitive());
if (outSequence.Size() != cmpSequence.Size()) {
AddError(errorText, "Signature revocation info archival attributes have different sets of revocation info types "
+ "(different sizes)", outSequence.Size().ToString(), cmpSequence.Size().ToString());
return false;
}
for (int i = 0; i < outSequence.Size(); i++) {
if (BOUNCY_CASTLE_FACTORY.CreateASN1TaggedObject(outSequence.GetObjectAt(i)) == null || BOUNCY_CASTLE_FACTORY
.CreateASN1TaggedObject(cmpSequence.GetObjectAt(i)) == null) {
AddError(errorText, structureIsInvalidError, String.Join("", JavaUtil.ArraysToEnumerable(@out).Select((e) =>
DUMP.DumpAsString(e)).ToList()), String.Join("", JavaUtil.ArraysToEnumerable(cmp).Select((e) => DUMP.
DumpAsString(e)).ToList()));
return false;
}
IASN1TaggedObject outTaggedObject = BOUNCY_CASTLE_FACTORY.CreateASN1TaggedObject(outSequence.GetObjectAt(i
));
IASN1TaggedObject cmpTaggedObject = BOUNCY_CASTLE_FACTORY.CreateASN1TaggedObject(cmpSequence.GetObjectAt(i
));
if (outTaggedObject.GetTagNo() != cmpTaggedObject.GetTagNo()) {
AddError(errorText, "Signature revocation info archival attributes have different tagged objects tag numbers"
, outTaggedObject.GetTagNo().ToString(), cmpTaggedObject.GetTagNo().ToString());
return false;
}
if (BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(outTaggedObject.GetObject()) == null || BOUNCY_CASTLE_FACTORY
.CreateASN1Sequence(cmpTaggedObject.GetObject()) == null) {
AddError(errorText, structureIsInvalidError, String.Join("", JavaUtil.ArraysToEnumerable(@out).Select((e) =>
DUMP.DumpAsString(e)).ToList()), String.Join("", JavaUtil.ArraysToEnumerable(cmp).Select((e) => DUMP.
DumpAsString(e)).ToList()));
return false;
}
// revocation entries can be either CRLs or OCSPs in most cases
IASN1Sequence outRevocationEntries = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(outTaggedObject.GetObject());
IASN1Sequence cmpRevocationEntries = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(cmpTaggedObject.GetObject());
if (outRevocationEntries.Size() != cmpRevocationEntries.Size()) {
AddError(errorText, "Signature revocation info archival attributes have different number of entries", outRevocationEntries
.Size().ToString(), cmpRevocationEntries.Size().ToString());
return false;
}
if (outTaggedObject.GetTagNo() == 0) {
// CRL revocation info case
for (int j = 0; j < outRevocationEntries.Size(); j++) {
if (BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(outRevocationEntries.GetObjectAt(j)) == null || BOUNCY_CASTLE_FACTORY
.CreateASN1Sequence(outRevocationEntries.GetObjectAt(j)) == null) {
AddError(errorText, "Signature revocation info attribute has unexpected CRL entry type", outRevocationEntries
.GetObjectAt(j).GetType().FullName.ToString(), cmpRevocationEntries.GetObjectAt(j).GetType().FullName.
ToString());
return false;
}
if (!CompareSequencesWithSignatureValue(BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(outRevocationEntries.GetObjectAt
(j)), BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(cmpRevocationEntries.GetObjectAt(j)), errorText)) {
AddError(errorText, MessageFormatUtil.Format("Signature revocation info attribute CRLs at {0} are different"
, j.ToString()));
return false;
}
}
}
else {
if (!CompareAsn1Structures(outRevocationEntries, cmpRevocationEntries, errorText)) {
AddError(errorText, "Revocation info attribute entries are different");
return false;
}
}
}
return true;
}
private static bool IsExpectedRevocationInfoArchivalAttributeStructure(IASN1Encodable[] container) {
return container.Length == 2 && BOUNCY_CASTLE_FACTORY.CreateASN1Set(container[1]) != null && BOUNCY_CASTLE_FACTORY
.CreateASN1Set(container[1]).Size() == 1 && BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(BOUNCY_CASTLE_FACTORY
.CreateASN1Set(container[1]).GetObjectAt(0).ToASN1Primitive()) != null;
}
private static bool CompareTimestampAttributes(IASN1Encodable[] @out, IASN1Encodable[] cmp, StringBuilder
errorText) {
if (cmp.Length == 2) {
if (BOUNCY_CASTLE_FACTORY.CreateASN1Set(cmp[1]) != null && BOUNCY_CASTLE_FACTORY.CreateASN1Set(@out[1]) !=
null) {
IASN1Primitive outSequence = BOUNCY_CASTLE_FACTORY.CreateASN1Set(@out[1]).GetObjectAt(0).ToASN1Primitive();
IASN1Primitive cmpSequence = BOUNCY_CASTLE_FACTORY.CreateASN1Set(cmp[1]).GetObjectAt(0).ToASN1Primitive();
if (BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(outSequence) != null && BOUNCY_CASTLE_FACTORY.CreateASN1Sequence
(cmpSequence) != null) {
return CompareSignedData(BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(outSequence), BOUNCY_CASTLE_FACTORY.CreateASN1Sequence
(cmpSequence), errorText);
}
}
}
AddError(errorText, "Signature timestamp attribute structure is invalid", String.Join("", JavaUtil.ArraysToEnumerable
(@out).Select((e) => DUMP.DumpAsString(e)).ToList()), String.Join("", JavaUtil.ArraysToEnumerable(cmp)
.Select((e) => DUMP.DumpAsString(e)).ToList()));
return false;
}
private static int GetSignerInfoIndex(IASN1Sequence baseElement) {
for (int i = 3; i < baseElement.Size(); i++) {
if (BOUNCY_CASTLE_FACTORY.CreateASN1Set(baseElement.GetObjectAt(i)) != null) {
return i;
}
}
throw new InvalidOperationException("SignerInfo entry has not been found.");
}
private static String GetASN1ObjectId(IASN1Primitive primitive) {
if (BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(primitive) != null) {
return GetASN1ObjectId(BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(primitive).ToArray());
}
if (BOUNCY_CASTLE_FACTORY.CreateASN1Set(primitive) != null) {
return GetASN1ObjectId(BOUNCY_CASTLE_FACTORY.CreateASN1Set(primitive).ToArray());
}
return null;
}
private static String GetASN1ObjectId(IASN1Encodable[] primitives) {
if (primitives.Length != 0 && BOUNCY_CASTLE_FACTORY.CreateASN1ObjectIdentifier(primitives[0]) != null) {
return BOUNCY_CASTLE_FACTORY.CreateASN1ObjectIdentifier(primitives[0]).GetId();
}
return null;
}
private static IASN1Primitive GetSignatureContent(String signatureName, SignatureUtil util) {
PdfSignature signature = util.GetSignature(signatureName);
byte[] contents = signature.GetContents().GetValueBytes();
IASN1InputStream inputStream = BOUNCY_CASTLE_FACTORY.CreateASN1InputStream(new MemoryStream(contents));
return inputStream.ReadObject();
}
private static void WriteToFile(String path, String content) {
using (StreamWriter writer = new StreamWriter(path, true)) {
writer.Write(content);
}
}
private static void AddError(StringBuilder errorBuilder, String errorText) {
AddError(errorBuilder, errorText, null, null);
}
private static void AddError(StringBuilder errorBuilder, String errorText, String @out, String cmp) {
errorBuilder.Append(errorText);
if (null != @out) {
errorBuilder.Append("\nout: ").Append(@out);
}
if (null != cmp) {
errorBuilder.Append("\ncmp: ").Append(cmp);
}
errorBuilder.Append("\n\n");
}
internal delegate bool SequenceComparator(IASN1Sequence outSequence, IASN1Sequence cmpSequence, StringBuilder
errorText);
}
}