diff --git a/itext.tests/itext.kernel.tests/itext/kernel/mac/MacIntegrityProtectorCreationTest.cs b/itext.tests/itext.kernel.tests/itext/kernel/mac/MacIntegrityProtectorCreationTest.cs
index 6ce5930e70..7d710c1a5f 100644
--- a/itext.tests/itext.kernel.tests/itext/kernel/mac/MacIntegrityProtectorCreationTest.cs
+++ b/itext.tests/itext.kernel.tests/itext/kernel/mac/MacIntegrityProtectorCreationTest.cs
@@ -72,8 +72,7 @@ public virtual void StandaloneMacStandardEncryptionTest() {
.HMAC_WITH_SHA_256, MacProperties.KeyWrappingAlgorithm.AES_256_NO_PADD);
WriterProperties writerProperties = new WriterProperties().SetPdfVersion(PdfVersion.PDF_2_0).SetStandardEncryption
(PASSWORD, PASSWORD, 0, EncryptionConstants.ENCRYPTION_AES_256, macProperties);
- using (PdfDocument pdfDoc = new PdfDocument(CompareTool.CreateTestPdfWriter(outputFileName, writerProperties
- ))) {
+ using (PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outputFileName, writerProperties))) {
pdfDoc.AddNewPage().AddAnnotation(new PdfTextAnnotation(new Rectangle(100, 100, 100, 100)));
}
NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outputFileName, cmpFileName, DESTINATION_FOLDER
diff --git a/itext.tests/itext.kernel.tests/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest.cs b/itext.tests/itext.kernel.tests/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest.cs
index b8b82074a3..624f24c1ae 100644
--- a/itext.tests/itext.kernel.tests/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest.cs
+++ b/itext.tests/itext.kernel.tests/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest.cs
@@ -167,6 +167,38 @@ public virtual void InvalidPublicKeyMacProtectedDocumentTest() {
NUnit.Framework.Assert.AreEqual(KernelExceptionMessageConstant.MAC_VALIDATION_FAILED, exceptionMessage);
}
+ [NUnit.Framework.Test]
+ public virtual void ReadSignedMacProtectedDocumentWithoutAttributeTest() {
+ String message = NUnit.Framework.Assert.Catch(typeof(PdfException), () => {
+ using (PdfDocument pdfDoc = new PdfDocument(new PdfReader(SOURCE_FOLDER + "signedMacProtectedDocWithoutAttribute.pdf"
+ , new ReaderProperties().SetPassword(PASSWORD)))) {
+ }
+ }
+ ).Message;
+ NUnit.Framework.Assert.AreEqual(KernelExceptionMessageConstant.MAC_ATTRIBUTE_NOT_SPECIFIED, message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void MacProtectionStrippedTest() {
+ String message = NUnit.Framework.Assert.Catch(typeof(PdfException), () => {
+ using (PdfDocument pdfDoc = new PdfDocument(new PdfReader(SOURCE_FOLDER + "macProtectionStrippedTest.pdf",
+ new ReaderProperties().SetPassword(PASSWORD)))) {
+ }
+ }
+ ).Message;
+ NUnit.Framework.Assert.AreEqual(KernelExceptionMessageConstant.MAC_PERMS_WITHOUT_MAC, message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void ReadSignedMacProtectedDocumentTest() {
+ NUnit.Framework.Assert.DoesNotThrow(() => {
+ using (PdfDocument pdfDoc = new PdfDocument(new PdfReader(SOURCE_FOLDER + "signedMacProtectedDocument.pdf"
+ , new ReaderProperties().SetPassword(PASSWORD)))) {
+ }
+ }
+ );
+ }
+
[NUnit.Framework.Test]
public virtual void ReadThirdPartyMacProtectedDocumentTest() {
NUnit.Framework.Assert.DoesNotThrow(() => {
diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest/macProtectionStrippedTest.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest/macProtectionStrippedTest.pdf
new file mode 100644
index 0000000000..f772dea961
Binary files /dev/null and b/itext.tests/itext.kernel.tests/resources/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest/macProtectionStrippedTest.pdf differ
diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest/signedMacProtectedDocWithoutAttribute.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest/signedMacProtectedDocWithoutAttribute.pdf
new file mode 100644
index 0000000000..27038d63b0
Binary files /dev/null and b/itext.tests/itext.kernel.tests/resources/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest/signedMacProtectedDocWithoutAttribute.pdf differ
diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest/signedMacProtectedDocument.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest/signedMacProtectedDocument.pdf
new file mode 100644
index 0000000000..0f8a456338
Binary files /dev/null and b/itext.tests/itext.kernel.tests/resources/itext/kernel/mac/MacIntegrityProtectorReadingAndRewritingTest/signedMacProtectedDocument.pdf differ
diff --git a/itext.tests/itext.sign.tests/itext/signatures/mac/SignedDocumentWithMacTest.cs b/itext.tests/itext.sign.tests/itext/signatures/mac/SignedDocumentWithMacTest.cs
new file mode 100644
index 0000000000..0ae547a083
--- /dev/null
+++ b/itext.tests/itext.sign.tests/itext/signatures/mac/SignedDocumentWithMacTest.cs
@@ -0,0 +1,186 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using System;
+using System.IO;
+using NUnit.Framework;
+using iText.Bouncycastleconnector;
+using iText.Commons.Bouncycastle;
+using iText.Commons.Bouncycastle.Cert;
+using iText.Commons.Bouncycastle.Crypto;
+using iText.Commons.Utils;
+using iText.Kernel.Crypto;
+using iText.Kernel.Exceptions;
+using iText.Kernel.Pdf;
+using iText.Kernel.Utils;
+using iText.Signatures;
+using iText.Signatures.Testutils;
+using iText.Test;
+
+namespace iText.Signatures.Mac {
+ [NUnit.Framework.Category("BouncyCastleIntegrationTest")]
+ public class SignedDocumentWithMacTest : ExtendedITextTest {
+ private static readonly IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory();
+
+ private static readonly String CERTS_SRC = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/";
+
+ private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/signatures/mac/SignedDocumentWithMacTest/";
+
+ private static readonly String DESTINATION_FOLDER = NUnit.Framework.TestContext.CurrentContext.TestDirectory
+ + "/test/itext/signatures/mac/SignedDocumentWithMacTest/";
+
+ private static readonly byte[] ENCRYPTION_PASSWORD = "123".GetBytes();
+
+ private static readonly char[] PRIVATE_KEY_PASSWORD = "testpassphrase".ToCharArray();
+
+ [NUnit.Framework.OneTimeSetUp]
+ public static void Before() {
+ NUnit.Framework.Assume.That("BC".Equals(FACTORY.GetProviderName()));
+ CreateOrClearDestinationFolder(DESTINATION_FOLDER);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void SignMacProtectedDocTest() {
+ String fileName = "signMacProtectedDocTest.pdf";
+ String srcFileName = SOURCE_FOLDER + "macEncryptedDoc.pdf";
+ String outputFileName = DESTINATION_FOLDER + fileName;
+ String signCertFileName = CERTS_SRC + "signCertRsa01.pem";
+ String cmpFileName = SOURCE_FOLDER + "cmp_" + fileName;
+ IX509Certificate[] signRsaChain = PemFileHelper.ReadFirstChain(signCertFileName);
+ IPrivateKey signRsaPrivateKey = PemFileHelper.ReadFirstKey(signCertFileName, PRIVATE_KEY_PASSWORD);
+ using (PdfReader reader = new PdfReader(srcFileName, new ReaderProperties().SetPassword(ENCRYPTION_PASSWORD
+ ))) {
+ using (Stream outputStream = FileUtil.GetFileOutputStream(outputFileName)) {
+ PdfSigner pdfSigner = new PdfSigner(reader, outputStream, new StampingProperties());
+ PerformSignDetached(pdfSigner, signRsaPrivateKey, signRsaChain);
+ }
+ }
+ ReaderProperties properties = new ReaderProperties().SetPassword(ENCRYPTION_PASSWORD);
+ NUnit.Framework.Assert.IsNull(SignaturesCompareTool.CompareSignatures(outputFileName, cmpFileName, properties
+ , properties));
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void SignMacProtectedDocInAppendModeTest() {
+ String fileName = "signMacProtectedDocInAppendModeTest.pdf";
+ String srcFileName = SOURCE_FOLDER + "macEncryptedDoc.pdf";
+ String outputFileName = DESTINATION_FOLDER + fileName;
+ String signCertFileName = CERTS_SRC + "signCertRsa01.pem";
+ String cmpFileName = SOURCE_FOLDER + "cmp_" + fileName;
+ IX509Certificate[] signRsaChain = PemFileHelper.ReadFirstChain(signCertFileName);
+ IPrivateKey signRsaPrivateKey = PemFileHelper.ReadFirstKey(signCertFileName, PRIVATE_KEY_PASSWORD);
+ using (PdfReader reader = new PdfReader(srcFileName, new ReaderProperties().SetPassword(ENCRYPTION_PASSWORD
+ ))) {
+ using (Stream outputStream = FileUtil.GetFileOutputStream(outputFileName)) {
+ PdfSigner pdfSigner = new PdfSigner(reader, outputStream, new StampingProperties().UseAppendMode());
+ PerformSignDetached(pdfSigner, signRsaPrivateKey, signRsaChain);
+ }
+ }
+ ReaderProperties properties = new ReaderProperties().SetPassword(ENCRYPTION_PASSWORD);
+ NUnit.Framework.Assert.IsNull(SignaturesCompareTool.CompareSignatures(outputFileName, cmpFileName, properties
+ , properties));
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void SignMacProtectedDocWithSHA3_384Test() {
+ String fileName = "signMacProtectedDocWithSHA3_384Test.pdf";
+ String srcFileName = SOURCE_FOLDER + "macEncryptedDocSHA3_384.pdf";
+ String outputFileName = DESTINATION_FOLDER + fileName;
+ String signCertFileName = CERTS_SRC + "signCertRsa01.pem";
+ String cmpFileName = SOURCE_FOLDER + "cmp_" + fileName;
+ IX509Certificate[] signRsaChain = PemFileHelper.ReadFirstChain(signCertFileName);
+ IPrivateKey signRsaPrivateKey = PemFileHelper.ReadFirstKey(signCertFileName, PRIVATE_KEY_PASSWORD);
+ using (PdfReader reader = new PdfReader(srcFileName, new ReaderProperties().SetPassword(ENCRYPTION_PASSWORD
+ ))) {
+ using (Stream outputStream = FileUtil.GetFileOutputStream(outputFileName)) {
+ PdfSigner pdfSigner = new PdfSigner(reader, outputStream, new StampingProperties());
+ PerformSignDetached(pdfSigner, signRsaPrivateKey, signRsaChain);
+ }
+ }
+ ReaderProperties properties = new ReaderProperties().SetPassword(ENCRYPTION_PASSWORD);
+ NUnit.Framework.Assert.IsNull(SignaturesCompareTool.CompareSignatures(outputFileName, cmpFileName, properties
+ , properties));
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void SignMacPublicEncryptionDocTest() {
+ String fileName = "signMacPublicEncryptionDocTest.pdf";
+ String srcFileName = SOURCE_FOLDER + "macEncryptedWithPublicHandlerDoc.pdf";
+ String outputFileName = DESTINATION_FOLDER + fileName;
+ String signCertFileName = CERTS_SRC + "signCertRsa01.pem";
+ String cmpFileName = SOURCE_FOLDER + "cmp_" + fileName;
+ IX509Certificate[] signRsaChain = PemFileHelper.ReadFirstChain(signCertFileName);
+ IPrivateKey signRsaPrivateKey = PemFileHelper.ReadFirstKey(signCertFileName, PRIVATE_KEY_PASSWORD);
+ IX509Certificate certificate = CryptoUtil.ReadPublicCertificate(FileUtil.GetInputStreamForFile(CERTS_SRC +
+ "SHA256withRSA.cer"));
+ IPrivateKey privateKey = PemFileHelper.ReadFirstKey(CERTS_SRC + "SHA256withRSA.key", PRIVATE_KEY_PASSWORD);
+ ReaderProperties properties = new ReaderProperties().SetPublicKeySecurityParams(certificate, privateKey);
+ using (PdfReader reader = new PdfReader(srcFileName, properties)) {
+ using (Stream outputStream = FileUtil.GetFileOutputStream(outputFileName)) {
+ PdfSigner pdfSigner = new PdfSigner(reader, outputStream, new StampingProperties());
+ PerformSignDetached(pdfSigner, signRsaPrivateKey, signRsaChain);
+ }
+ }
+ NUnit.Framework.Assert.IsNull(SignaturesCompareTool.CompareSignatures(outputFileName, cmpFileName, properties
+ , properties));
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void ReadSignedMacProtectedInvalidDocTest() {
+ String srcFileName = SOURCE_FOLDER + "signedMacProtectedInvalidDoc.pdf";
+ String exceptionMessage = NUnit.Framework.Assert.Catch(typeof(PdfException), () => {
+ using (PdfDocument document = new PdfDocument(new PdfReader(srcFileName, new ReaderProperties().SetPassword
+ (ENCRYPTION_PASSWORD)))) {
+ }
+ }
+ ).Message;
+ // Do nothing.
+ NUnit.Framework.Assert.AreEqual(KernelExceptionMessageConstant.MAC_VALIDATION_FAILED, exceptionMessage);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void UpdateSignedMacProtectedDocumentTest() {
+ String fileName = "updateSignedMacProtectedDocumentTest.pdf";
+ String srcFileName = SOURCE_FOLDER + "thirdPartyMacProtectedAndSignedDocument.pdf";
+ String outputFileName = DESTINATION_FOLDER + fileName;
+ String cmpFileName = SOURCE_FOLDER + "cmp_" + fileName;
+ using (PdfDocument document = new PdfDocument(new PdfReader(srcFileName, new ReaderProperties().SetPassword
+ (ENCRYPTION_PASSWORD)), new PdfWriter(FileUtil.GetFileOutputStream(outputFileName)), new StampingProperties
+ ().UseAppendMode())) {
+ }
+ // Do nothing.
+ // This call produces INFO log from AESCipher caused by exception while decrypting. The reason is that,
+ // while comparing encrypted signed documents, CompareTool needs to mark signature value as unencrypted.
+ // Instead, it tries to decrypt not encrypted value which results in exception.
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outputFileName, cmpFileName, DESTINATION_FOLDER
+ , "diff", ENCRYPTION_PASSWORD, ENCRYPTION_PASSWORD));
+ }
+
+ private static void PerformSignDetached(PdfSigner pdfSigner, IPrivateKey privateKey, IX509Certificate[] chain
+ ) {
+ pdfSigner.SignDetached(new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256), chain, null, null, null
+ , 0, PdfSigner.CryptoStandard.CADES);
+ }
+ }
+}
diff --git a/itext.tests/itext.sign.tests/itext/signatures/testutils/SignaturesCompareTool.cs b/itext.tests/itext.sign.tests/itext/signatures/testutils/SignaturesCompareTool.cs
index 57d6287592..6b0d4ba6a7 100644
--- a/itext.tests/itext.sign.tests/itext/signatures/testutils/SignaturesCompareTool.cs
+++ b/itext.tests/itext.sign.tests/itext/signatures/testutils/SignaturesCompareTool.cs
@@ -56,6 +56,8 @@ public class SignaturesCompareTool {
private const String OID_OCSP_NONCE_EXTENSION = "1.3.6.1.5.5.7.48.1.2";
+ private const String ID_ATTR_PDF_MAC_DATA = "1.0.32004.1.2";
+
private static readonly IAsn1Dump DUMP = BOUNCY_CASTLE_FACTORY.CreateASN1Dump();
private static readonly ICollection IGNORED_OIDS;
@@ -66,6 +68,7 @@ static SignaturesCompareTool() {
tempSet.Add(OID_TST_INFO);
tempSet.Add(OID_SIGNING_TIME);
tempSet.Add(OID_OCSP_NONCE_EXTENSION);
+ tempSet.Add(ID_ATTR_PDF_MAC_DATA);
IGNORED_OIDS = JavaCollectionsUtil.UnmodifiableSet(tempSet);
}
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/SHA256withRSA.cer b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/SHA256withRSA.cer
new file mode 100644
index 0000000000..fbaad2b382
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/SHA256withRSA.cer differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/SHA256withRSA.key b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/SHA256withRSA.key
new file mode 100644
index 0000000000..d7e9b5e825
--- /dev/null
+++ b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/SHA256withRSA.key
@@ -0,0 +1,51 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQdOaEUfD0sZWtUR11
+xwgn0gICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEBaNXSfwUjF5SS7n
+ce1JFoUEggTQ6eV+IsckiVP7I9VTldLkpP5OKB8brmw5t20fO11HyCnqkkhooR2b
+t2fBj4fWv0IRUe266L+fVs7AOngjkWfezEvGR6nte4pNXEFrOwt/U8A6IYZXBdA5
+dvqs6VMPHbjQ8CufVLGvksuYFQVRcGy0rk1DH2Of44GU4X0GtROlFFJnkmfZhVPS
+Hx2MXXGQ02Ko1i1eKoEGgvmSAsDcPijiX96DKlQZJ4YMtI/8rRsdvNJsJ2beyZDa
+T3aJMmSSBF92mS2dtS21DwjzEu8utquguYA0KYzjZM9onOuBEEUifam8Fjnvlui6
+beQJya4zldoA6QZPSd2PUAP6l1U/d8UXqcisjzArDZDmRu58dPxn4rs0NgTOIO8h
+fEUIvfS+wuknff1b/wdGnwXkXoeSrrjS9dhP9KVU1SJ/FWKc6BY+P+JmE5vLjAtn
+AmbyZhXY0jX7ZHFh0z0y1y1fTIXL1aj4iB+cUwhJ1ZdlGkT5HdG4ts/oTGCnpB6O
+F1GvGyhprmtjp/dspLH5ha0I+4aTn46yFpnBNyg8w9c2+xj8Jiqy9J/ppVtPdhxt
+wrE1/ThUGIWUTsbGbLW87WIrZq6IlSGtztbxAMYxXoe4solYueE3pI3eYFzgnBcq
+T6Byktr71gt9AGD/N/p+Kk5RM4JT8XpQjLjz9TlmsGpJzUoBGeG6KFLsqqLLSD+0
+c5lAGWsFhec3uCu4fCyBqxpQc0y5j2bgUiTRGYn1NOdyZg+ERO/aWGfkDOAtlL1i
+B79NGIBxIXgt508g83UeaQC6KjuG/8hPY6UHmU5mlgRT9H5jvkSX3mEtl1Gdk2y0
+M5pZTTrhbG4p66GhBi8vM5tQfiBoLUKEM/kgiGXPC6Kob42nb3ufP0rmnKklcDGC
++898hW5ge+VNmOkHpVuV5ZD9aWUSVEU4+8QNZj8pcyL0GXFyEL/HxNxUESdz3k0S
+bInuxO49mgGPjBqtx5ZvaxyWFnzOp6rmHZUHymejxxdnlnTnSnXKkJFjcm7n0sKO
+575ofHtk0OdqIK6YiPgfeF6nZkIg3C0PbReZ05kTplrW182ZWuQQyJgv+RPzF4+9
+5rCe67nJhJrt7hXFRsUScHXNj+HF9Av8WR2RnHTRbpQBJszijM+Xgl+VeYcY5ckB
+fk+AfcR9r0Jud4O9795OOWVxWqGVu/b1RGonfjMkGW+JdnZL0vkOYYcHt4iMZmzW
+M0ZowZPGO5dFBV7/ZkVzb0fexw+f+E0lUBEK1cx0gxnzjmcGJO+C9if1uIEfwpon
+3wBOTDsU7XKDx9v6ibcDMOXrZa+rcJWxgNkXt5nRpozZkddYctBkehGu+snV2g4n
+SdOwr0eIVv/L+v4IywZmeWWEVnbSAvB0p7nB89bgLMr9tV0ly7MWxPH+gPnNJ1gE
+7Mp3AgN5BxEmLfW3+ou3QLoqnOS2MCw/xcgLP0nJACSPI7/nWy95iKXKgkCkAgF9
+4Ztk7uBG4tiK14KcKq8ToCW2YNliT3g0CWjBLtVPUS6qboudMiuedxTxE8WEirpT
+A77nfDNg4MVjl4kP9jhV0Phpn9rDMJ2jw0BqFc1Vou4aNDXYandAFJea44Wce9H+
+qAowcrfsWehD01HBQ2KwWVg3sLnwwBHw0nvbATS41hdxsP2OmCnxWkc=
+-----END ENCRYPTED PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDfzCCAmegAwIBAgIEUFic9zANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJU
+UzETMBEGA1UECBMKVGVzdCBTdGF0ZTESMBAGA1UEBxMJVGVzdCBUb3duMREwDwYD
+VQQKEwhUZXN0IG9yZzEQMA4GA1UECxMHVGVzdCBPVTESMBAGA1UEAxMJVGVzdCBV
+c2VyMCAXDTE0MDkyMjA5MTExMVoYDzIxMTQwODI5MDkxMTExWjBvMQswCQYDVQQG
+EwJUUzETMBEGA1UECBMKVGVzdCBTdGF0ZTESMBAGA1UEBxMJVGVzdCBUb3duMREw
+DwYDVQQKEwhUZXN0IG9yZzEQMA4GA1UECxMHVGVzdCBPVTESMBAGA1UEAxMJVGVz
+dCBVc2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+6g5sYzXiNOA
+hR7C8wc8buxU/JgcbdHpHIR44iuXjpepBYAE7hRsWM7H4cuXrKiRxS9UMOadqkGF
+Qqb5sG6lo2UUhcj4qlN6hKDc/+AMZMIW1mvQldiygCAkqgM8iso+kw56dpVuerG/
+k1nd8f+X9rjXN6/DIMznZcMy2d9ByIFuixFKElPvOWx9q9N4aiueOd5FM5eHxp+3
+F4uCTrpM5zkS7Rmf5GVtCofc8KgaTLLp4D0Ge5VUJm7yW8fuU3eIpin4ivjk+Gye
+Q3t0BsrmNyQy3CmKGOBP/vX0+wEMvGN2xqNgAFP9dxA+AbJMiAfsmoWvxXaPktqC
+DOspTCFqbwIDAQABoyEwHzAdBgNVHQ4EFgQUILviRCmSrhuLDmF0nus4pv2uu7gw
+DQYJKoZIhvcNAQELBQADggEBAGnfGYL7nDm5taDPRxuGGMqUPwRnH2bXwef6S2Xb
+/nIEFtNheVFQFtKNn5Ikq68DTFMP06yXLnI7F40+ZiQezRBB1EPPmDL2fYKc9fL1
+SHntu6HLgP/Y5nnCVegtL8l9745gQZnnXlMtkTs2HFwffznIHW/3STO0Bcj0+KMa
+p8vebMjmvV7bZEGvrcrVXL55QPZXJwRuQMXJB3f5XhAEH1VqAhTW6DrvBUnuESwo
+9fxxA5gmblt80SQYdKr2I08OTk0qmyF8zNuffTOiSS8/V6Cf7CntuPWjSuVf1EVP
+MH6KkSjceLZ99Y7bvl7KKvQ4Kj5Bp27PwlRvtYbfCUmQEG8=
+-----END CERTIFICATE-----
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/signCertRsa01.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/signCertRsa01.pem
new file mode 100644
index 0000000000..5f2f1f25a8
--- /dev/null
+++ b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/certs/signCertRsa01.pem
@@ -0,0 +1,76 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQgLsVp4wmljjCRgEy
+kw2LWgICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEELyxbQ1T8vAMXX6t
+md0GgdwEggTQXjePuN2mrJ4C7uFP/ErLys5cwO7QSOL5z7jUUep96YkZgMjau99o
+6JolOHyJRzDbUNbTCfku8PqwDfdPrCPfMZ+e9UU/Y5uVZbEL+UtIkcH/uyFCd1k7
+AVOhAux7KZwgHlq66iUHqTQFd58Ly7KOoMKGZS07CtIxjYluFZZbx44EZrgqRwEa
+ZAbIsdR2xZ092Who4CzkuF31m2TuyzfYir7kocJsdQU6k9OdptfJRda2UpZiUXW8
+GMg/qvF7RwkAbfu7gLkcmc5FETlY+E3P8tYDutN737wnd/VqO9Bd25umOAl3CHUA
+5+YSXvWOSuAJjz7kEI2DEG/ov//oO0wohJPLKRF7jUI2WbIwCAypPZPvSS506ulO
+ofuLTY0+Jii0ZO5SyC4Uu90lh8RISvi9RzRx2G4sN+gyeCBBg1R1f5IRNeBImpEw
+aKqk4VrcUrLe623Z3el1Z8ErJBJ6G0ZV/EAFC7dmpTknrVOFzdO6sohtnHygm0Ce
+h5QdqiWvb0QKHv3GZumdtCcdh8vPMRT1PEWVvfV/cyIOc5BH6sYNtxiohosl6DLz
+I84vj3n8I+cFHrYIFCtghy9iZJ0GvvPxaiJCS5A+eDiOO3rhk3ZMkmTlVCpOBiY2
+QKdljhmRaN1Y6XfQ8SRNmr/qVfwFJ4JgYnk3VVHVvzt0EvEImNusOvTH2U4zMryP
+FRhgGNIL0f5gflo2LxSPqsnqNfJLRHCmXkYeB4Pu8/xv6biFqtwC2UBaL7FCjIgY
+NI+hi9Mt9fJpYqF4XGHcT2b6Aq7otgxX+5iiQg10bEjqI/gcZrFvBvmz/97OBe7X
+BFa5F8nYeSxnr5XQiJ04oBVBjdvAFeS7ZZ4Quq2beEgcgCj7EU/OXLLHwBCCmD8s
+8kgkhsv7+sPtNnC1YeoW6xHezt/FSDYxS1lPWsnr4AE4jpxOJgM8CNSDq0E52e83
+2uDNm3gIo133RRn9V8gNQzfIJv1BZFRUtOn9CRxrJBTptSxUJX1at+btTM7hlY2z
+MwXM5i7ZGDVQOPSpKrlbmA8bHDRHeGryXz0HNWs5RWiTVSEhCuVTrrlQG4nmbhVm
+b6xbd/DWs3NdEXwLLdg1W+fbeS2qIJtJqu0LU+PcGkhaVXeobc6MFGw06hDVtVg9
+Hm1/AZJ4OuD/+GyH9ku4Z9tPDh4NmXsVVV8CpzxRJ37gLrGPwbxXwW3v6B6NBRvz
+W8KuWQjlNDxVcFb9Si7Mj81xX2k1818hwSm3iEQIwT0Q7BNUj3oJhTsNlB/Id79J
+tl7AKam1ofDZVmU7gPcv0FPKxhEZIyjoqeAzLGuW1Hmp45HbJ94tL2uvbgPq2Mj7
+j4jllaPoN9J1QBVDHBqEzLZGeWKZYK2L9Sw4SdkZjLp96hxoQ7MYvTrsX4eSV0HH
+xMLp5hhIiM8zGIsMYlwcfOSpQh/H1AnbiQIJ0/tzO6uBZ73fq59rTReRf+PtXqqT
+5vg89nN6kNvJcrwNLyEhdC7uaqsoimTMi59BE36ZZcL0jHKMmPl9g/vbPj9CiRdu
+B0+4lQD0hAqK6FVfl6ENpj4zLdU6/EER5L7v+Syv8d5AeFtdKrqxH3oCGPXmXCol
+j2rALoIlZt0D+jjmcDo6C32qtu8w40zBD/+J8iZXVWC/U9cauP00N9E=
+-----END ENCRYPTED PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIID6TCCAtGgAwIBAgIEWOew7DANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJC
+WTEOMAwGA1UEBwwFTWluc2sxDjAMBgNVBAoMBWlUZXh0MQ0wCwYDVQQLDAR0ZXN0
+MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTE3MDQwNzE1MzM0MFoYDzIxMTcw
+NDA3MTUzMzQwWjBZMQswCQYDVQQGEwJCWTEOMAwGA1UEBwwFTWluc2sxDjAMBgNV
+BAoMBWlUZXh0MQ0wCwYDVQQLDAR0ZXN0MRswGQYDVQQDDBJpVGV4dFRlc3RSc2FD
+ZXJ0MDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfr2PnZ6DjvrSY
+g0cWtDR6drgvCowB5hQH7CHSAMTeVGZxUOBT1ylfxySioRU4ckq3uQu7jOe7Te6Y
+xEenjFhTv8bT0/ZrL2w6OGBH4iUs3Kwx5jzbRHPuZt7+gCKLBXhRH6ytDnbTHNgT
+4tjGXUmGa4sMbal1mXvKN/xaK+hGDskRW6cHZq7ZtdgfM8yBQWZzshAz7dQepmUH
+ZYWxplTwJ8cuVLqjgXMXWfTatio6yYwf+5AkWszBcCA6oxm16wN2pLUeNx5aBLiF
+Dhtj6qqHLwN/7tPb0ojqXRU3AfocC2y8a4WeVOOp2du0ja4E9P7IQNKiBBlOfNcF
+D700qPFpAgMBAAGjgbswgbgwCQYDVR0TBAIwADB/BgNVHSMEeDB2gBRdKnF1rt3Y
+vlm6ILFmkcl2NlNc7qFYpFYwVDELMAkGA1UEBhMCQlkxDjAMBgNVBAcMBU1pbnNr
+MQ4wDAYDVQQKDAVpVGV4dDENMAsGA1UECwwEdGVzdDEWMBQGA1UEAwwNaVRleHRU
+ZXN0Um9vdIIEWOeR1jAdBgNVHQ4EFgQUz41Jo8H/HAqxhMN/inuhKuLZU8gwCwYD
+VR0PBAQDAgXgMA0GCSqGSIb3DQEBCwUAA4IBAQCT597xhkVzrYP7NudRgIZCDwT3
+TDnL+Xv8H4c9p+CdHJtFMoxAQlzcQi4uEIo5nfBKMEXK0d1OnO8zEBfK98EPahUI
+++PDnNk82kh64CbZQ95Ms1Usq1XGd31bkXWWRM3LY2P7VG0nR+eLutVAClIcbTLN
+yL9ZhiL33jCk1W5PeXJy13kL3XV/Awt8zb9fcinkuXkV5LoZHxh2Ob19R//5fGxH
+hnUywReGptnIbEPiWGYyX1QCQeOqi3vmqZaGr+RZfF8+zqtSP5p4gFpryuTJxkcZ
+UOeroaBv3sHCI9rtzcwZPYsW0BroFliEhNIDq5HxJgdwdu6uDOMMrC2QxB17
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID6jCCAtKgAwIBAgIEWOeR1jANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJC
+WTEOMAwGA1UEBwwFTWluc2sxDjAMBgNVBAoMBWlUZXh0MQ0wCwYDVQQLDAR0ZXN0
+MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTE3MDQwNzEzMjAwMVoYDzIxMTcw
+NDA3MTMyMDAxWjBUMQswCQYDVQQGEwJCWTEOMAwGA1UEBwwFTWluc2sxDjAMBgNV
+BAoMBWlUZXh0MQ0wCwYDVQQLDAR0ZXN0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz/fz7iq1wzhMMYcGfmMm
+teCY/ZtdE26PB1OTTBuDSN86sVNmur5FV/mLPU9ZK2ofrs+wMrqn0agmFlRl4dTh
+f5u5WSEQ/ARwXzYOn2uEkwR/0dwwZUL3VWhrPSD5SxX5MzFo8UXTNlXW2bClLC0F
+QU2qLjIwwRFwwWDSQPR8r/Mv181RljVpEjPk6DfkDtHWWA4daGlQU0nXbuZszplv
+iPafXmyKn+2w4G9Jw/8pHIK2VhWYstLI+bUZk662ZVldNvnpMyHn12FfB0Nbf/Z6
+V2WTGviEr8EEE2cA7I+H7ZGUDzug7umNCCJn3ilC6vAt9i9OLaZRDh6jPMOjMUiz
+TwIDAQABo4HBMIG+MA8GA1UdEwEB/wQFMAMBAf8wfwYDVR0jBHgwdoAUXSpxda7d
+2L5ZuiCxZpHJdjZTXO6hWKRWMFQxCzAJBgNVBAYTAkJZMQ4wDAYDVQQHDAVNaW5z
+azEOMAwGA1UECgwFaVRleHQxDTALBgNVBAsMBHRlc3QxFjAUBgNVBAMMDWlUZXh0
+VGVzdFJvb3SCBFjnkdYwHQYDVR0OBBYEFF0qcXWu3di+WbogsWaRyXY2U1zuMAsG
+A1UdDwQEAwIB9jANBgkqhkiG9w0BAQsFAAOCAQEAdhby6EaopoUF8j7oR44Mhe/N
+3y9hzGb/zLmmgTavPd2plv6NlAPt9W+8rezKO6jQCsBRFw8JY+Lx6j3W0K6rWigB
+pPGU/B/0bXLlOIv2a4uW8nBmq6jxAe5Xbtwm8HcKOOLMzxPIChHJIJy5NWw9ArD4
+Ul+FEt/VuEW1NfPZm1U5ixMOrBfn0C8pxIX4+VSHN9I8WoFjSfYX4Y3ldRLTeqxQ
+rhZQlbhGNymp3Kcvtuq5At6vopskyB8Q1b7L4e+hRWK2prz/7p4Bdhu2TmkEfWZc
+YKpgrkVFqa/Z1uZ0q4KVBOP3cyaQmqRXTV37SfpNyHAJdol5ueF68VVVNZFRXw==
+-----END CERTIFICATE-----
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacProtectedDocInAppendModeTest.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacProtectedDocInAppendModeTest.pdf
new file mode 100644
index 0000000000..4a4402c038
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacProtectedDocInAppendModeTest.pdf differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacProtectedDocTest.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacProtectedDocTest.pdf
new file mode 100644
index 0000000000..0f8a456338
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacProtectedDocTest.pdf differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacProtectedDocWithSHA3_384Test.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacProtectedDocWithSHA3_384Test.pdf
new file mode 100644
index 0000000000..1ea822942e
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacProtectedDocWithSHA3_384Test.pdf differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacPublicEncryptionDocTest.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacPublicEncryptionDocTest.pdf
new file mode 100644
index 0000000000..46560378d6
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_signMacPublicEncryptionDocTest.pdf differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_updateSignedMacProtectedDocumentTest.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_updateSignedMacProtectedDocumentTest.pdf
new file mode 100644
index 0000000000..9fbec6183d
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/cmp_updateSignedMacProtectedDocumentTest.pdf differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/macEncryptedDoc.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/macEncryptedDoc.pdf
new file mode 100644
index 0000000000..f8bffedbdc
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/macEncryptedDoc.pdf differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/macEncryptedDocSHA3_384.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/macEncryptedDocSHA3_384.pdf
new file mode 100644
index 0000000000..e4dfce5bbe
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/macEncryptedDocSHA3_384.pdf differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/macEncryptedWithPublicHandlerDoc.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/macEncryptedWithPublicHandlerDoc.pdf
new file mode 100644
index 0000000000..e62dcb8d79
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/macEncryptedWithPublicHandlerDoc.pdf differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/signedMacProtectedInvalidDoc.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/signedMacProtectedInvalidDoc.pdf
new file mode 100644
index 0000000000..9b46b247b0
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/signedMacProtectedInvalidDoc.pdf differ
diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/thirdPartyMacProtectedAndSignedDocument.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/thirdPartyMacProtectedAndSignedDocument.pdf
new file mode 100644
index 0000000000..e0b03c2b7b
Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/mac/SignedDocumentWithMacTest/thirdPartyMacProtectedAndSignedDocument.pdf differ
diff --git a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/Asn1EncodableVectorBC.cs b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/Asn1EncodableVectorBC.cs
index 44507dbe6f..089dc19ef7 100644
--- a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/Asn1EncodableVectorBC.cs
+++ b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/Asn1EncodableVectorBC.cs
@@ -107,6 +107,11 @@ public virtual void AddOptional(IAlgorithmIdentifier element) {
}
}
+ ///
+ public virtual int Size() {
+ return encodableVector.Count;
+ }
+
/// Indicates whether some other object is "equal to" this one.
/// Indicates whether some other object is "equal to" this one. Compares wrapped objects.
public override bool Equals(Object o) {
diff --git a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/Asn1EncodableVectorBCFips.cs b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/Asn1EncodableVectorBCFips.cs
index 16eda0990d..7199072a6c 100644
--- a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/Asn1EncodableVectorBCFips.cs
+++ b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/Asn1EncodableVectorBCFips.cs
@@ -107,6 +107,11 @@ public virtual void AddOptional(IAlgorithmIdentifier element) {
}
}
+ ///
+ public virtual int Size() {
+ return encodableVector.Count;
+ }
+
/// Indicates whether some other object is "equal to" this one.
/// Indicates whether some other object is "equal to" this one. Compares wrapped objects.
public override bool Equals(Object o) {
diff --git a/itext/itext.commons/itext/commons/bouncycastle/asn1/IAsn1EncodableVector.cs b/itext/itext.commons/itext/commons/bouncycastle/asn1/IAsn1EncodableVector.cs
index 4e262a4fc5..401bcfe650 100644
--- a/itext/itext.commons/itext/commons/bouncycastle/asn1/IAsn1EncodableVector.cs
+++ b/itext/itext.commons/itext/commons/bouncycastle/asn1/IAsn1EncodableVector.cs
@@ -76,5 +76,17 @@ public interface IAsn1EncodableVector {
///
/// AlgorithmIdentifier wrapper.
void AddOptional(IAlgorithmIdentifier element);
+
+ ///
+ /// Calls actual
+ /// size
+ /// method for the wrapped ASN1EncodableVector object.
+ ///
+ ///
+ ///
+ /// int
+ /// representing current vector size
+ ///
+ int Size();
}
}
diff --git a/itext/itext.kernel/itext/kernel/exceptions/KernelExceptionMessageConstant.cs b/itext/itext.kernel/itext/kernel/exceptions/KernelExceptionMessageConstant.cs
index cfabfcac45..2f368fafee 100644
--- a/itext/itext.kernel/itext/kernel/exceptions/KernelExceptionMessageConstant.cs
+++ b/itext/itext.kernel/itext/kernel/exceptions/KernelExceptionMessageConstant.cs
@@ -356,12 +356,22 @@ public const String CONTENT_STREAM_MUST_NOT_INVOKE_OPERATORS_THAT_SPECIFY_COLORS
public const String MAC_ALGORITHM_NOT_SUPPORTED = "This MAC algorithm is not supported.";
+ public const String MAC_ATTRIBUTE_NOT_SPECIFIED = "Signature doesn't contain unsigned MAC attribute, which is required in \"attached to signature\" mode.";
+
+ public const String MAC_EXTRACTION_EXCEPTION = "Exception occurred during signature parsing. It is not possible to extract MAC.";
+
+ public const String MAC_LOCATION_NOT_SPECIFIED = "AuthCode dictionary doesn't contain MACLocation entry.";
+
+ public const String MAC_NOT_SPECIFIED = "AuthCode dictionary doesn't contain MAC entry, which is required in standalone mode.";
+
public const String MAC_FOR_ENCRYPTION_5 = "MAC integrity protection is only supported for encryption algorithms of version 5 or higher.";
public const String MAC_FOR_PDF_2 = "MAC integrity protection is only supported for PDF 2.0 or higher.";
public const String MAC_PERMS_WITHOUT_MAC = "Permissions bit 13 is set to zero, " + "which indicates that MAC integrity protection is enabled. However MAC container wasn't found.";
+ public const String MAC_VALIDATION_EXCEPTION = "Unexpected exception occurred during MAC token validation.";
+
public const String MAC_VALIDATION_FAILED = "MAC integrity protection was compromised. Document content was modified.";
public const String MISSING_REQUIRED_FIELD_IN_FONT_DICTIONARY = "Missing required field {0} in font dictionary.";
@@ -463,6 +473,8 @@ public const String CONTENT_STREAM_MUST_NOT_INVOKE_OPERATORS_THAT_SPECIFY_COLORS
public const String SHADING_TYPE_NOT_FOUND = "Shading type not found.";
+ public const String SIG_OBJ_REF_NOT_SPECIFIED = "AuthCode dictionary doesn't contain SigObjRef entry, which is required in signature mode.";
+
public const String STDCF_NOT_FOUND_ENCRYPTION = "/StdCF not found (encryption)";
public const String STREAM_SHALL_END_WITH_ENDSTREAM = "Stream shall end with endstream keyword.";
@@ -542,8 +554,6 @@ public const String CONTENT_STREAM_MUST_NOT_INVOKE_OPERATORS_THAT_SPECIFY_COLORS
public const String UNSUPPORTED_XOBJECT_TYPE = "Unsupported XObject type.";
- public const String VALIDATION_EXCEPTION = "Unexpected exception occurred during MAC token validation.";
-
public const String WHEN_ADDING_OBJECT_REFERENCE_TO_THE_TAG_TREE_IT_MUST_BE_CONNECTED_TO_NOT_FLUSHED_OBJECT
= "When adding object reference to the tag tree, it must be connected to not flushed object.";
diff --git a/itext/itext.kernel/itext/kernel/mac/MacIntegrityProtector.cs b/itext/itext.kernel/itext/kernel/mac/AbstractMacIntegrityProtector.cs
similarity index 61%
rename from itext/itext.kernel/itext/kernel/mac/MacIntegrityProtector.cs
rename to itext/itext.kernel/itext/kernel/mac/AbstractMacIntegrityProtector.cs
index 7fde5a6b77..4c1dbf0147 100644
--- a/itext/itext.kernel/itext/kernel/mac/MacIntegrityProtector.cs
+++ b/itext/itext.kernel/itext/kernel/mac/AbstractMacIntegrityProtector.cs
@@ -25,17 +25,15 @@ You should have received a copy of the GNU Affero General Public License
using iText.Bouncycastleconnector;
using iText.Commons.Bouncycastle;
using iText.Commons.Bouncycastle.Asn1;
-using iText.Commons.Bouncycastle.Security;
using iText.Commons.Digest;
using iText.Commons.Utils;
using iText.IO.Source;
-using iText.Kernel.Events;
using iText.Kernel.Exceptions;
using iText.Kernel.Pdf;
namespace iText.Kernel.Mac {
/// Class responsible for integrity protection in encrypted documents, which uses MAC container.
- public class MacIntegrityProtector {
+ public abstract class AbstractMacIntegrityProtector {
private static readonly IBouncyCastleFactory BC_FACTORY = BouncyCastleFactoryCreator.GetFactory();
private const String ID_AUTHENTICATED_DATA = "1.2.840.113549.1.9.16.1.2";
@@ -52,55 +50,57 @@ public class MacIntegrityProtector {
private const String PDF_MAC = "PDFMAC";
- private MacPdfObject macPdfObject;
+ protected internal readonly PdfDocument document;
- private readonly PdfDocument document;
+ protected internal readonly MacProperties macProperties;
- private readonly MacProperties macProperties;
+ protected internal byte[] kdfSalt = null;
- private byte[] kdfSalt = null;
+ protected internal byte[] fileEncryptionKey = new byte[0];
- private byte[] fileEncryptionKey = new byte[0];
+ private readonly MacContainerReader macContainerReader;
///
/// Creates
- ///
+ ///
/// instance from the provided
/// .
///
///
///
///
- /// , for which integrity protection is required
+ /// for which integrity protection is required
///
///
///
///
/// used to provide MAC algorithm properties
///
- public MacIntegrityProtector(PdfDocument document, MacProperties macProperties) {
+ protected internal AbstractMacIntegrityProtector(PdfDocument document, MacProperties macProperties) {
this.document = document;
+ this.macContainerReader = null;
this.macProperties = macProperties;
}
///
/// Creates
- ///
+ ///
/// instance from the Auth dictionary.
///
///
///
///
- /// , for which integrity protection is required
+ /// for which integrity protection is required
///
///
///
///
- /// , representing Auth dictionary, in which MAC container is stored
+ /// representing Auth dictionary in which MAC container is stored
///
- public MacIntegrityProtector(PdfDocument document, PdfDictionary authDictionary) {
+ protected internal AbstractMacIntegrityProtector(PdfDocument document, PdfDictionary authDictionary) {
this.document = document;
- this.macProperties = ParseMacProperties(authDictionary.GetAsString(PdfName.MAC).GetValueBytes());
+ this.macContainerReader = MacContainerReader.GetInstance(authDictionary);
+ this.macProperties = new MacProperties(GetMacDigestAlgorithm(macContainerReader.ParseDigestAlgorithm()));
}
/// Sets file encryption key to be used during MAC calculation.
@@ -143,104 +143,129 @@ public virtual void SetKdfSalt(byte[] kdfSalt) {
/// in case of any modifications,
/// introduced to the document in question, after MAC container is integrated.
///
- ///
- ///
- ///
- /// which represents AuthCode entry in the trailer and container MAC.
- ///
- public virtual void ValidateMacToken(PdfDictionary authDictionary) {
- byte[] expectedMac;
- byte[] actualMac;
- byte[] expectedMessageDigest;
- byte[] actualMessageDigest;
+ public virtual void ValidateMacToken() {
try {
- byte[] macContainer = authDictionary.GetAsString(PdfName.MAC).GetValueBytes();
- byte[] macKey = ParseMacKey(macContainer);
- PdfArray byteRange = authDictionary.GetAsArray(PdfName.ByteRange);
- byte[] dataDigest = DigestBytes(document.GetReader().GetSafeFile().CreateSourceView(), byteRange.ToLongArray
- ());
- byte[] expectedData = ParseAuthAttributes(macContainer).GetEncoded();
- expectedMac = GenerateMac(macKey, expectedData);
- expectedMessageDigest = CreateMessageDigestSequence(CreateMessageBytes(dataDigest)).GetEncoded();
- actualMessageDigest = ParseMessageDigest(macContainer).GetEncoded();
- actualMac = ParseMac(macContainer);
+ byte[] macKey = GenerateDecryptedKey(macContainerReader.ParseMacKey());
+ long[] byteRange = macContainerReader.GetByteRange();
+ byte[] dataDigest;
+ IRandomAccessSource randomAccessSource = document.GetReader().GetSafeFile().CreateSourceView();
+ using (Stream rg = new RASInputStream(new RandomAccessSourceFactory().CreateRanged(randomAccessSource, byteRange
+ ))) {
+ dataDigest = DigestBytes(rg);
+ }
+ byte[] expectedData = macContainerReader.ParseAuthAttributes().GetEncoded();
+ byte[] expectedMac = GenerateMac(macKey, expectedData);
+ byte[] signatureDigest = DigestBytes(macContainerReader.GetSignature());
+ byte[] expectedMessageDigest = CreateMessageDigestSequence(CreatePdfMacIntegrityInfo(dataDigest, signatureDigest
+ )).GetEncoded();
+ byte[] actualMessageDigest = macContainerReader.ParseMessageDigest().GetEncoded();
+ byte[] actualMac = macContainerReader.ParseMac();
+ if (!JavaUtil.ArraysEquals(expectedMac, actualMac) || !JavaUtil.ArraysEquals(expectedMessageDigest, actualMessageDigest
+ )) {
+ throw new PdfException(KernelExceptionMessageConstant.MAC_VALIDATION_FAILED);
+ }
}
- catch (Exception e) {
- throw new PdfException(KernelExceptionMessageConstant.VALIDATION_EXCEPTION, e);
+ catch (PdfException e) {
+ throw;
}
- if (!JavaUtil.ArraysEquals(expectedMac, actualMac) || !JavaUtil.ArraysEquals(expectedMessageDigest, actualMessageDigest
- )) {
- throw new PdfException(KernelExceptionMessageConstant.MAC_VALIDATION_FAILED);
+ catch (Exception e) {
+ throw new PdfException(KernelExceptionMessageConstant.MAC_VALIDATION_EXCEPTION, e);
}
}
- /// Prepare the document for MAC protection.
- public virtual void PrepareDocument() {
- document.AddEventHandler(PdfDocumentEvent.START_DOCUMENT_CLOSING, new MacIntegrityProtector.MacPdfObjectAdder
- (this));
- document.AddEventHandler(PdfDocumentEvent.END_WRITER_FLUSH, new MacIntegrityProtector.MacContainerEmbedder
- (this));
- }
-
- private int GetContainerSizeEstimate() {
- try {
- IMessageDigest digest = GetMessageDigest();
- digest.Update(new byte[0]);
- return CreateMacContainer(digest.Digest(), GenerateRandomBytes(32)).Length * 2 + 2;
- }
- catch (AbstractGeneralSecurityException e) {
- throw new PdfException(KernelExceptionMessageConstant.CONTAINER_GENERATION_EXCEPTION, e);
- }
- catch (System.IO.IOException e) {
- throw new PdfException(KernelExceptionMessageConstant.CONTAINER_GENERATION_EXCEPTION, e);
- }
+ /// Digests provided bytes based on hash algorithm, specified for this class instance.
+ ///
+ ///
+ /// byte[]
+ /// to be digested
+ ///
+ /// digested bytes.
+ protected internal virtual byte[] DigestBytes(byte[] bytes) {
+ return bytes == null ? null : DigestBytes(new MemoryStream(bytes));
}
- private void EmbedMacContainer() {
- byte[] documentBytes = GetDocumentByteArrayOutputStream().ToArray();
- long[] byteRange = macPdfObject.ComputeByteRange(documentBytes.Length);
- long byteRangePosition = macPdfObject.GetByteRangePosition();
- MemoryStream localBaos = new MemoryStream();
- PdfOutputStream os = new PdfOutputStream(localBaos);
- os.Write('[');
- foreach (long l in byteRange) {
- os.WriteLong(l).Write(' ');
- }
- os.Write(']');
- Array.Copy(localBaos.ToArray(), 0, documentBytes, (int)byteRangePosition, localBaos.Length);
- IRandomAccessSource ras = new RandomAccessSourceFactory().CreateSource(documentBytes);
- // Here we should create MAC
- byte[] mac;
- try {
- byte[] dataDigest = DigestBytes(ras, byteRange);
- mac = CreateMacContainer(dataDigest, GenerateRandomBytes(32));
+ /// Digests provided input stream based on hash algorithm, specified for this class instance.
+ ///
+ ///
+ ///
+ /// to be digested
+ ///
+ /// digested bytes.
+ protected internal virtual byte[] DigestBytes(Stream inputStream) {
+ if (inputStream == null) {
+ return null;
}
- catch (AbstractGeneralSecurityException e) {
- throw new PdfException(KernelExceptionMessageConstant.CONTAINER_GENERATION_EXCEPTION, e);
+ IMessageDigest digest = GetMessageDigest();
+ byte[] buf = new byte[8192];
+ int rd;
+ while ((rd = inputStream.JRead(buf, 0, buf.Length)) > 0) {
+ digest.Update(buf, 0, rd);
}
- PdfString macString = new PdfString(mac).SetHexWriting(true);
- // fill in the MAC
- localBaos.JReset();
- os.Write(macString);
- Array.Copy(localBaos.ToArray(), 0, documentBytes, (int)byteRange[1], localBaos.Length);
- GetDocumentByteArrayOutputStream().JReset();
- document.GetWriter().GetOutputStream().Write(documentBytes, 0, documentBytes.Length);
+ return digest.Digest();
}
- private MemoryStream GetDocumentByteArrayOutputStream() {
- return ((MemoryStream)document.GetWriter().GetOutputStream());
- }
-
- private byte[] DigestBytes(IRandomAccessSource ras, long[] byteRange) {
- IMessageDigest digest = GetMessageDigest();
- using (Stream rg = new RASInputStream(new RandomAccessSourceFactory().CreateRanged(ras, byteRange))) {
- byte[] buf = new byte[8192];
- int rd;
- while ((rd = rg.JRead(buf, 0, buf.Length)) > 0) {
- digest.Update(buf, 0, rd);
- }
- return digest.Digest();
- }
+ /// Creates MAC container as ASN1 object based on data digest, MAC key and signature parameters.
+ ///
+ /// data digest as
+ /// byte[]
+ /// to be used during MAC container creation
+ ///
+ ///
+ /// MAC key as
+ /// byte[]
+ /// to be used during MAC container creation
+ ///
+ ///
+ /// signature value as
+ /// byte[]
+ /// to be used during MAC container creation
+ ///
+ ///
+ /// MAC container as
+ /// .
+ ///
+ protected internal virtual IDerSequence CreateMacContainer(byte[] dataDigest, byte[] macKey, byte[] signature
+ ) {
+ IAsn1EncodableVector contentInfoV = BC_FACTORY.CreateASN1EncodableVector();
+ contentInfoV.Add(BC_FACTORY.CreateASN1ObjectIdentifier(ID_AUTHENTICATED_DATA));
+ // Recipient info
+ IAsn1EncodableVector recInfoV = BC_FACTORY.CreateASN1EncodableVector();
+ recInfoV.Add(BC_FACTORY.CreateASN1Integer(0));
+ // version
+ recInfoV.Add(BC_FACTORY.CreateDERTaggedObject(0, BC_FACTORY.CreateASN1ObjectIdentifier(ID_KDF_PDF_MAC_WRAP_KDF
+ )));
+ recInfoV.Add(BC_FACTORY.CreateDERSequence(BC_FACTORY.CreateASN1ObjectIdentifier(GetKeyWrappingAlgorithmOid
+ ())));
+ ////////////////////// KEK
+ byte[] macKek = BC_FACTORY.GenerateHKDF(fileEncryptionKey, kdfSalt, PDF_MAC.GetBytes(System.Text.Encoding.
+ UTF8));
+ byte[] encryptedKey = GenerateEncryptedKey(macKey, macKek);
+ recInfoV.Add(BC_FACTORY.CreateDEROctetString(encryptedKey));
+ // Digest info
+ byte[] messageBytes = CreatePdfMacIntegrityInfo(dataDigest, signature == null ? null : DigestBytes(signature
+ ));
+ // Encapsulated content info
+ IAsn1EncodableVector encapContentInfoV = BC_FACTORY.CreateASN1EncodableVector();
+ encapContentInfoV.Add(BC_FACTORY.CreateASN1ObjectIdentifier(ID_CT_PDF_MAC_INTEGRITY_INFO));
+ encapContentInfoV.Add(BC_FACTORY.CreateDERTaggedObject(0, BC_FACTORY.CreateDEROctetString(messageBytes)));
+ IDerSet authAttrs = CreateAuthAttributes(messageBytes);
+ // Create mac
+ byte[] data = authAttrs.GetEncoded();
+ byte[] mac = GenerateMac(macKey, data);
+ // Auth data
+ IAsn1EncodableVector authDataV = BC_FACTORY.CreateASN1EncodableVector();
+ authDataV.Add(BC_FACTORY.CreateASN1Integer(0));
+ // version
+ authDataV.Add(BC_FACTORY.CreateDERSet(BC_FACTORY.CreateDERTaggedObject(false, 3, BC_FACTORY.CreateDERSequence
+ (recInfoV))));
+ authDataV.Add(BC_FACTORY.CreateDERSequence(BC_FACTORY.CreateASN1ObjectIdentifier(GetMacAlgorithmOid())));
+ authDataV.Add(BC_FACTORY.CreateDERTaggedObject(false, 1, BC_FACTORY.CreateDERSequence(BC_FACTORY.CreateASN1ObjectIdentifier
+ (GetMacDigestOid()))));
+ authDataV.Add(BC_FACTORY.CreateDERSequence(encapContentInfoV));
+ authDataV.Add(BC_FACTORY.CreateDERTaggedObject(false, 2, authAttrs));
+ authDataV.Add(BC_FACTORY.CreateDEROctetString(mac));
+ contentInfoV.Add(BC_FACTORY.CreateDERTaggedObject(0, BC_FACTORY.CreateDERSequence(authDataV)));
+ return BC_FACTORY.CreateDERSequence(contentInfoV);
}
private IMessageDigest GetMessageDigest() {
@@ -331,7 +356,9 @@ private byte[] GenerateEncryptedKey(byte[] macKey, byte[] macKek) {
}
}
- private byte[] GenerateDecryptedKey(byte[] encryptedMacKey, byte[] macKek) {
+ private byte[] GenerateDecryptedKey(byte[] encryptedMacKey) {
+ byte[] macKek = BC_FACTORY.GenerateHKDF(fileEncryptionKey, kdfSalt, PDF_MAC.GetBytes(System.Text.Encoding.
+ UTF8));
switch (macProperties.GetKeyWrappingAlgorithm()) {
case MacProperties.KeyWrappingAlgorithm.AES_256_NO_PADD: {
return BC_FACTORY.GenerateDecryptedKeyWithAES256NoPad(encryptedMacKey, macKek);
@@ -367,68 +394,11 @@ private String GetKeyWrappingAlgorithmOid() {
}
}
- private byte[] ParseMacKey(byte[] macContainer) {
- IAsn1Sequence authDataSequence = GetAuthDataSequence(macContainer);
- IAsn1Sequence recInfo = BC_FACTORY.CreateASN1Sequence(BC_FACTORY.CreateASN1TaggedObject(BC_FACTORY.CreateASN1Set
- (authDataSequence.GetObjectAt(1)).GetObjectAt(0)).GetObject());
- IAsn1OctetString encryptedKey = BC_FACTORY.CreateASN1OctetString(recInfo.GetObjectAt(3));
- byte[] macKek = BC_FACTORY.GenerateHKDF(fileEncryptionKey, kdfSalt, PDF_MAC.GetBytes(System.Text.Encoding.
- UTF8));
- return GenerateDecryptedKey(encryptedKey.GetOctets(), macKek);
- }
-
- private IAsn1Sequence ParseMessageDigest(byte[] macContainer) {
- IAsn1Set authAttributes = ParseAuthAttributes(macContainer);
- return BC_FACTORY.CreateASN1Sequence(authAttributes.GetObjectAt(2));
- }
-
- private byte[] CreateMacContainer(byte[] dataDigest, byte[] macKey) {
- IAsn1EncodableVector contentInfoV = BC_FACTORY.CreateASN1EncodableVector();
- contentInfoV.Add(BC_FACTORY.CreateASN1ObjectIdentifier(ID_AUTHENTICATED_DATA));
- // Recipient info
- IAsn1EncodableVector recInfoV = BC_FACTORY.CreateASN1EncodableVector();
- recInfoV.Add(BC_FACTORY.CreateASN1Integer(0));
- // version
- recInfoV.Add(BC_FACTORY.CreateDERTaggedObject(0, BC_FACTORY.CreateASN1ObjectIdentifier(ID_KDF_PDF_MAC_WRAP_KDF
- )));
- recInfoV.Add(BC_FACTORY.CreateDERSequence(BC_FACTORY.CreateASN1ObjectIdentifier(GetKeyWrappingAlgorithmOid
- ())));
- ////////////////////// KEK
- byte[] macKek = BC_FACTORY.GenerateHKDF(fileEncryptionKey, kdfSalt, PDF_MAC.GetBytes(System.Text.Encoding.
- UTF8));
- byte[] encryptedKey = GenerateEncryptedKey(macKey, macKek);
- recInfoV.Add(BC_FACTORY.CreateDEROctetString(encryptedKey));
- // Digest info
- byte[] messageBytes = CreateMessageBytes(dataDigest);
- // Encapsulated content info
- IAsn1EncodableVector encapContentInfoV = BC_FACTORY.CreateASN1EncodableVector();
- encapContentInfoV.Add(BC_FACTORY.CreateASN1ObjectIdentifier(ID_CT_PDF_MAC_INTEGRITY_INFO));
- encapContentInfoV.Add(BC_FACTORY.CreateDERTaggedObject(0, BC_FACTORY.CreateDEROctetString(messageBytes)));
- IDerSet authAttrs = CreateAuthAttributes(messageBytes);
- // Create mac
- byte[] data = authAttrs.GetEncoded();
- byte[] mac = GenerateMac(macKey, data);
- // Auth data
- IAsn1EncodableVector authDataV = BC_FACTORY.CreateASN1EncodableVector();
- authDataV.Add(BC_FACTORY.CreateASN1Integer(0));
- // version
- authDataV.Add(BC_FACTORY.CreateDERSet(BC_FACTORY.CreateDERTaggedObject(false, 3, BC_FACTORY.CreateDERSequence
- (recInfoV))));
- authDataV.Add(BC_FACTORY.CreateDERSequence(BC_FACTORY.CreateASN1ObjectIdentifier(GetMacAlgorithmOid())));
- authDataV.Add(BC_FACTORY.CreateDERTaggedObject(false, 1, BC_FACTORY.CreateDERSequence(BC_FACTORY.CreateASN1ObjectIdentifier
- (GetMacDigestOid()))));
- authDataV.Add(BC_FACTORY.CreateDERSequence(encapContentInfoV));
- authDataV.Add(BC_FACTORY.CreateDERTaggedObject(false, 2, authAttrs));
- authDataV.Add(BC_FACTORY.CreateDEROctetString(mac));
- contentInfoV.Add(BC_FACTORY.CreateDERTaggedObject(0, BC_FACTORY.CreateDERSequence(authDataV)));
- return BC_FACTORY.CreateDERSequence(contentInfoV).GetEncoded();
- }
-
private IDerSequence CreateMessageDigestSequence(byte[] messageBytes) {
// Hash messageBytes to get messageDigest attribute
IMessageDigest digest = GetMessageDigest();
digest.Update(messageBytes);
- byte[] messageDigest = digest.Digest();
+ byte[] messageDigest = DigestBytes(messageBytes);
// Message digest
IAsn1EncodableVector messageDigestV = BC_FACTORY.CreateASN1EncodableVector();
messageDigestV.Add(BC_FACTORY.CreateASN1ObjectIdentifier(ID_MESSAGE_DIGEST));
@@ -458,44 +428,23 @@ private IDerSet CreateAuthAttributes(byte[] messageBytes) {
return BC_FACTORY.CreateDERSet(authAttrsV);
}
- private static byte[] ParseMac(byte[] macContainer) {
- IAsn1Sequence authDataSequence = GetAuthDataSequence(macContainer);
- return BC_FACTORY.CreateASN1OctetString(authDataSequence.GetObjectAt(6)).GetOctets();
- }
-
- private static IAsn1Set ParseAuthAttributes(byte[] macContainer) {
- IAsn1Sequence authDataSequence = GetAuthDataSequence(macContainer);
- return BC_FACTORY.CreateASN1Set(BC_FACTORY.CreateASN1TaggedObject(authDataSequence.GetObjectAt(5)), false);
- }
-
- private static byte[] CreateMessageBytes(byte[] dataDigest) {
+ private static byte[] CreatePdfMacIntegrityInfo(byte[] dataDigest, byte[] signatureDigest) {
IAsn1EncodableVector digestInfoV = BC_FACTORY.CreateASN1EncodableVector();
digestInfoV.Add(BC_FACTORY.CreateASN1Integer(0));
digestInfoV.Add(BC_FACTORY.CreateDEROctetString(dataDigest));
+ if (signatureDigest != null) {
+ digestInfoV.Add(BC_FACTORY.CreateDERTaggedObject(false, 0, BC_FACTORY.CreateDEROctetString(signatureDigest
+ )));
+ }
return BC_FACTORY.CreateDERSequence(digestInfoV).GetEncoded();
}
- private static byte[] GenerateRandomBytes(int length) {
+ protected internal static byte[] GenerateRandomBytes(int length) {
byte[] randomBytes = new byte[length];
BC_FACTORY.GetSecureRandom().GetBytes(randomBytes);
return randomBytes;
}
- private static MacProperties ParseMacProperties(byte[] macContainer) {
- IAsn1Sequence authDataSequence = GetAuthDataSequence(macContainer);
- IAsn1Object digestAlgorithmContainer = BC_FACTORY.CreateASN1TaggedObject(authDataSequence.GetObjectAt(3)).
- GetObject();
- IDerObjectIdentifier digestAlgorithm;
- if (BC_FACTORY.CreateASN1ObjectIdentifier(digestAlgorithmContainer) != null) {
- digestAlgorithm = BC_FACTORY.CreateASN1ObjectIdentifier(digestAlgorithmContainer);
- }
- else {
- digestAlgorithm = BC_FACTORY.CreateASN1ObjectIdentifier(BC_FACTORY.CreateASN1Sequence(digestAlgorithmContainer
- ).GetObjectAt(0));
- }
- return new MacProperties(GetMacDigestAlgorithm(digestAlgorithm.GetId()));
- }
-
private static MacProperties.MacDigestAlgorithm GetMacDigestAlgorithm(String oid) {
switch (oid) {
case "2.16.840.1.101.3.4.2.1": {
@@ -527,49 +476,5 @@ private static MacProperties.MacDigestAlgorithm GetMacDigestAlgorithm(String oid
}
}
}
-
- private static IAsn1Sequence GetAuthDataSequence(byte[] macContainer) {
- IAsn1Sequence contentInfoSequence;
- try {
- using (IAsn1InputStream din = BC_FACTORY.CreateASN1InputStream(new MemoryStream(macContainer))) {
- contentInfoSequence = BC_FACTORY.CreateASN1Sequence(din.ReadObject());
- }
- }
- catch (System.IO.IOException e) {
- throw new PdfException(KernelExceptionMessageConstant.CONTAINER_PARSING_EXCEPTION, e);
- }
- return BC_FACTORY.CreateASN1Sequence(BC_FACTORY.CreateASN1TaggedObject(contentInfoSequence.GetObjectAt(1))
- .GetObject());
- }
-
- private class MacPdfObjectAdder : iText.Kernel.Events.IEventHandler {
- public virtual void HandleEvent(Event @event) {
- this._enclosing.macPdfObject = new MacPdfObject(this._enclosing.GetContainerSizeEstimate());
- this._enclosing.document.GetTrailer().Put(PdfName.AuthCode, this._enclosing.macPdfObject.GetPdfObject());
- }
-
- internal MacPdfObjectAdder(MacIntegrityProtector _enclosing) {
- this._enclosing = _enclosing;
- }
-
- private readonly MacIntegrityProtector _enclosing;
- }
-
- private class MacContainerEmbedder : iText.Kernel.Events.IEventHandler {
- public virtual void HandleEvent(Event @event) {
- try {
- this._enclosing.EmbedMacContainer();
- }
- catch (System.IO.IOException e) {
- throw new PdfException(KernelExceptionMessageConstant.CONTAINER_EMBEDDING_EXCEPTION, e);
- }
- }
-
- internal MacContainerEmbedder(MacIntegrityProtector _enclosing) {
- this._enclosing = _enclosing;
- }
-
- private readonly MacIntegrityProtector _enclosing;
- }
}
}
diff --git a/itext/itext.kernel/itext/kernel/mac/IMacContainerLocator.cs b/itext/itext.kernel/itext/kernel/mac/IMacContainerLocator.cs
new file mode 100644
index 0000000000..784bbe665e
--- /dev/null
+++ b/itext/itext.kernel/itext/kernel/mac/IMacContainerLocator.cs
@@ -0,0 +1,97 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using iText.Kernel.Pdf;
+
+namespace iText.Kernel.Mac {
+ ///
+ /// Strategy interface, which is responsible for
+ ///
+ /// container location.
+ ///
+ ///
+ /// Strategy interface, which is responsible for
+ ///
+ /// container location.
+ /// Expected to be used in
+ /// .
+ ///
+ public interface IMacContainerLocator {
+ ///
+ /// Locates
+ ///
+ /// container.
+ ///
+ ///
+ ///
+ ///
+ /// container to be located
+ ///
+ void LocateMacContainer(AbstractMacIntegrityProtector macIntegrityProtector);
+
+ ///
+ /// Creates
+ ///
+ /// from explicitly provided MAC properties.
+ ///
+ ///
+ ///
+ ///
+ /// for which MAC container shall be created
+ ///
+ ///
+ ///
+ ///
+ /// to be used for MAC container creation
+ ///
+ ///
+ ///
+ ///
+ /// which specific implementation depends on interface implementation.
+ ///
+ AbstractMacIntegrityProtector CreateMacIntegrityProtector(PdfDocument document, MacProperties macProperties
+ );
+
+ ///
+ /// Creates
+ ///
+ /// from already existing AuthCode dictionary.
+ ///
+ ///
+ ///
+ ///
+ /// for which MAC container shall be created
+ ///
+ ///
+ /// AuthCode
+ ///
+ /// which contains MAC related information
+ ///
+ ///
+ ///
+ ///
+ /// which specific implementation depends on interface implementation.
+ ///
+ AbstractMacIntegrityProtector CreateMacIntegrityProtector(PdfDocument document, PdfDictionary authDictionary
+ );
+ }
+}
diff --git a/itext/itext.kernel/itext/kernel/mac/MacContainerReader.cs b/itext/itext.kernel/itext/kernel/mac/MacContainerReader.cs
new file mode 100644
index 0000000000..ae5cad8a2f
--- /dev/null
+++ b/itext/itext.kernel/itext/kernel/mac/MacContainerReader.cs
@@ -0,0 +1,152 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using System;
+using System.IO;
+using iText.Bouncycastleconnector;
+using iText.Commons.Bouncycastle;
+using iText.Commons.Bouncycastle.Asn1;
+using iText.Kernel.Exceptions;
+using iText.Kernel.Pdf;
+
+namespace iText.Kernel.Mac {
+//\cond DO_NOT_DOCUMENT
+ internal abstract class MacContainerReader {
+ private static readonly IBouncyCastleFactory BC_FACTORY = BouncyCastleFactoryCreator.GetFactory();
+
+ private readonly byte[] macContainer;
+
+ private readonly long[] byteRange;
+
+ private readonly byte[] signature;
+
+//\cond DO_NOT_DOCUMENT
+ internal MacContainerReader(PdfDictionary authDictionary) {
+ this.macContainer = ParseMacContainer(authDictionary);
+ this.byteRange = ParseByteRange(authDictionary);
+ this.signature = ParseSignature(authDictionary);
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal static iText.Kernel.Mac.MacContainerReader GetInstance(PdfDictionary authDictionary) {
+ PdfName macLocation = authDictionary.GetAsName(PdfName.MACLocation);
+ if (PdfName.Standalone.Equals(macLocation)) {
+ return new MacStandaloneContainerReader(authDictionary);
+ }
+ else {
+ if (PdfName.AttachedToSig.Equals(macLocation)) {
+ return new MacSignatureContainerReader(authDictionary);
+ }
+ }
+ throw new PdfException(KernelExceptionMessageConstant.MAC_LOCATION_NOT_SPECIFIED);
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal abstract byte[] ParseSignature(PdfDictionary authDictionary);
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal abstract long[] ParseByteRange(PdfDictionary authDictionary);
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal abstract byte[] ParseMacContainer(PdfDictionary authDictionary);
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual long[] GetByteRange() {
+ return byteRange;
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual byte[] GetSignature() {
+ return signature;
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual byte[] ParseMac() {
+ IAsn1Sequence authDataSequence = GetAuthDataSequence();
+ return BC_FACTORY.CreateASN1OctetString(authDataSequence.GetObjectAt(6)).GetOctets();
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual IAsn1Set ParseAuthAttributes() {
+ IAsn1Sequence authDataSequence = GetAuthDataSequence();
+ return BC_FACTORY.CreateASN1Set(BC_FACTORY.CreateASN1TaggedObject(authDataSequence.GetObjectAt(5)), false);
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual IAsn1Sequence ParseMessageDigest() {
+ IAsn1Set authAttributes = ParseAuthAttributes();
+ return BC_FACTORY.CreateASN1Sequence(authAttributes.GetObjectAt(2));
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual byte[] ParseMacKey() {
+ IAsn1Sequence authDataSequence = GetAuthDataSequence();
+ IAsn1Sequence recInfo = BC_FACTORY.CreateASN1Sequence(BC_FACTORY.CreateASN1TaggedObject(BC_FACTORY.CreateASN1Set
+ (authDataSequence.GetObjectAt(1)).GetObjectAt(0)).GetObject());
+ IAsn1OctetString encryptedKey = BC_FACTORY.CreateASN1OctetString(recInfo.GetObjectAt(3));
+ return encryptedKey.GetOctets();
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual String ParseDigestAlgorithm() {
+ IAsn1Sequence authDataSequence = GetAuthDataSequence();
+ IAsn1Object digestAlgorithmContainer = BC_FACTORY.CreateASN1TaggedObject(authDataSequence.GetObjectAt(3)).
+ GetObject();
+ IDerObjectIdentifier digestAlgorithm;
+ if (BC_FACTORY.CreateASN1ObjectIdentifier(digestAlgorithmContainer) != null) {
+ digestAlgorithm = BC_FACTORY.CreateASN1ObjectIdentifier(digestAlgorithmContainer);
+ }
+ else {
+ digestAlgorithm = BC_FACTORY.CreateASN1ObjectIdentifier(BC_FACTORY.CreateASN1Sequence(digestAlgorithmContainer
+ ).GetObjectAt(0));
+ }
+ return digestAlgorithm.GetId();
+ }
+//\endcond
+
+ private IAsn1Sequence GetAuthDataSequence() {
+ IAsn1Sequence contentInfoSequence;
+ try {
+ using (IAsn1InputStream din = BC_FACTORY.CreateASN1InputStream(new MemoryStream(macContainer))) {
+ contentInfoSequence = BC_FACTORY.CreateASN1Sequence(din.ReadObject());
+ }
+ }
+ catch (System.IO.IOException e) {
+ throw new PdfException(KernelExceptionMessageConstant.CONTAINER_PARSING_EXCEPTION, e);
+ }
+ return BC_FACTORY.CreateASN1Sequence(BC_FACTORY.CreateASN1TaggedObject(contentInfoSequence.GetObjectAt(1))
+ .GetObject());
+ }
+ }
+//\endcond
+}
diff --git a/itext/itext.kernel/itext/kernel/mac/MacSignatureContainerReader.cs b/itext/itext.kernel/itext/kernel/mac/MacSignatureContainerReader.cs
new file mode 100644
index 0000000000..c2cd9e9dd4
--- /dev/null
+++ b/itext/itext.kernel/itext/kernel/mac/MacSignatureContainerReader.cs
@@ -0,0 +1,139 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using System;
+using System.IO;
+using iText.Bouncycastleconnector;
+using iText.Commons.Bouncycastle;
+using iText.Commons.Bouncycastle.Asn1;
+using iText.Kernel.Exceptions;
+using iText.Kernel.Pdf;
+
+namespace iText.Kernel.Mac {
+//\cond DO_NOT_DOCUMENT
+ internal class MacSignatureContainerReader : MacContainerReader {
+ private static readonly IBouncyCastleFactory BC_FACTORY = BouncyCastleFactoryCreator.GetFactory();
+
+ private const String ID_ATTR_PDF_MAC_DATA = "1.0.32004.1.2";
+
+//\cond DO_NOT_DOCUMENT
+ internal MacSignatureContainerReader(PdfDictionary authDictionary)
+ : base(authDictionary) {
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal override byte[] ParseSignature(PdfDictionary authDictionary) {
+ PdfDictionary signatureDictionary = GetSignatureDictionary(authDictionary);
+ PdfString contentsString = signatureDictionary.GetAsString(PdfName.Contents);
+ contentsString.MarkAsUnencryptedObject();
+ return ParseSignatureValueFromSignatureContainer(contentsString.GetValueBytes());
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal override long[] ParseByteRange(PdfDictionary authDictionary) {
+ PdfDictionary signatureDictionary = GetSignatureDictionary(authDictionary);
+ return signatureDictionary.GetAsArray(PdfName.ByteRange).ToLongArray();
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal override byte[] ParseMacContainer(PdfDictionary authDictionary) {
+ PdfDictionary signatureDictionary = GetSignatureDictionary(authDictionary);
+ PdfString contentsString = signatureDictionary.GetAsString(PdfName.Contents);
+ contentsString.MarkAsUnencryptedObject();
+ return ParseMacContainerFromSignatureContainer(contentsString.GetValueBytes());
+ }
+//\endcond
+
+ private static byte[] ParseSignatureValueFromSignatureContainer(byte[] signature) {
+ try {
+ IAsn1Sequence signerInfoSeq = ParseSignerInfoSequence(signature);
+ int signatureValueIndex = 3;
+ IAsn1TaggedObject taggedSignedAttributes = BC_FACTORY.CreateASN1TaggedObject(signerInfoSeq.GetObjectAt(signatureValueIndex
+ ));
+ if (taggedSignedAttributes != null) {
+ ++signatureValueIndex;
+ }
+ IDerOctetString signatureDataOS = BC_FACTORY.CreateDEROctetString(signerInfoSeq.GetObjectAt(++signatureValueIndex
+ ));
+ return signatureDataOS.GetOctets();
+ }
+ catch (Exception e) {
+ throw new PdfException(KernelExceptionMessageConstant.MAC_EXTRACTION_EXCEPTION, e);
+ }
+ }
+
+ private static byte[] ParseMacContainerFromSignatureContainer(byte[] signature) {
+ try {
+ IAsn1Sequence signerInfoSeq = ParseSignerInfoSequence(signature);
+ int unsignedAttributesIndex = 3;
+ IAsn1TaggedObject taggedSignedAttributes = BC_FACTORY.CreateASN1TaggedObject(signerInfoSeq.GetObjectAt(unsignedAttributesIndex
+ ));
+ if (taggedSignedAttributes != null) {
+ ++unsignedAttributesIndex;
+ }
+ unsignedAttributesIndex += 2;
+ if (signerInfoSeq.Size() > unsignedAttributesIndex) {
+ IAsn1Set unsignedAttributes = BC_FACTORY.CreateASN1Set(BC_FACTORY.CreateASN1TaggedObject(signerInfoSeq.GetObjectAt
+ (unsignedAttributesIndex)), false);
+ for (int i = 0; i < unsignedAttributes.Size(); i++) {
+ IAsn1Sequence attrSeq = BC_FACTORY.CreateASN1Sequence(unsignedAttributes.GetObjectAt(i));
+ IDerObjectIdentifier attrType = BC_FACTORY.CreateASN1ObjectIdentifier(attrSeq.GetObjectAt(0));
+ if (ID_ATTR_PDF_MAC_DATA.Equals(attrType.GetId())) {
+ IAsn1Set macSet = BC_FACTORY.CreateASN1Set(attrSeq.GetObjectAt(1));
+ return macSet.GetObjectAt(0).ToASN1Primitive().GetEncoded();
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ throw new PdfException(KernelExceptionMessageConstant.MAC_EXTRACTION_EXCEPTION, e);
+ }
+ throw new PdfException(KernelExceptionMessageConstant.MAC_ATTRIBUTE_NOT_SPECIFIED);
+ }
+
+ private static PdfDictionary GetSignatureDictionary(PdfDictionary authDictionary) {
+ if (authDictionary.GetAsDictionary(PdfName.SigObjRef) == null) {
+ throw new PdfException(KernelExceptionMessageConstant.SIG_OBJ_REF_NOT_SPECIFIED);
+ }
+ return authDictionary.GetAsDictionary(PdfName.SigObjRef);
+ }
+
+ private static IAsn1Sequence ParseSignerInfoSequence(byte[] signature) {
+ using (IAsn1InputStream @is = BC_FACTORY.CreateASN1InputStream(new MemoryStream(signature))) {
+ IAsn1Sequence contentInfo = BC_FACTORY.CreateASN1Sequence(@is.ReadObject());
+ IAsn1Sequence signedData = BC_FACTORY.CreateASN1Sequence(BC_FACTORY.CreateASN1TaggedObject(contentInfo.GetObjectAt
+ (1)).GetObject());
+ int signerInfoIndex = 4;
+ IAsn1TaggedObject taggedObj = BC_FACTORY.CreateASN1TaggedObject(signedData.GetObjectAt(signerInfoIndex));
+ if (taggedObj != null) {
+ ++signerInfoIndex;
+ }
+ return BC_FACTORY.CreateASN1Sequence(BC_FACTORY.CreateASN1Set(signedData.GetObjectAt(signerInfoIndex)).GetObjectAt
+ (0));
+ }
+ }
+ }
+//\endcond
+}
diff --git a/itext/itext.kernel/itext/kernel/mac/MacStandaloneContainerReader.cs b/itext/itext.kernel/itext/kernel/mac/MacStandaloneContainerReader.cs
new file mode 100644
index 0000000000..eb419635eb
--- /dev/null
+++ b/itext/itext.kernel/itext/kernel/mac/MacStandaloneContainerReader.cs
@@ -0,0 +1,57 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using iText.Kernel.Exceptions;
+using iText.Kernel.Pdf;
+
+namespace iText.Kernel.Mac {
+//\cond DO_NOT_DOCUMENT
+ internal class MacStandaloneContainerReader : MacContainerReader {
+//\cond DO_NOT_DOCUMENT
+ internal MacStandaloneContainerReader(PdfDictionary authDictionary)
+ : base(authDictionary) {
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal override byte[] ParseSignature(PdfDictionary authDictionary) {
+ return null;
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal override long[] ParseByteRange(PdfDictionary authDictionary) {
+ return authDictionary.GetAsArray(PdfName.ByteRange).ToLongArray();
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal override byte[] ParseMacContainer(PdfDictionary authDictionary) {
+ if (authDictionary.GetAsString(PdfName.MAC) == null) {
+ throw new PdfException(KernelExceptionMessageConstant.MAC_NOT_SPECIFIED);
+ }
+ return authDictionary.GetAsString(PdfName.MAC).GetValueBytes();
+ }
+//\endcond
+ }
+//\endcond
+}
diff --git a/itext/itext.kernel/itext/kernel/mac/StandaloneMacContainerLocator.cs b/itext/itext.kernel/itext/kernel/mac/StandaloneMacContainerLocator.cs
new file mode 100644
index 0000000000..b9a5b62d85
--- /dev/null
+++ b/itext/itext.kernel/itext/kernel/mac/StandaloneMacContainerLocator.cs
@@ -0,0 +1,55 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using iText.Kernel.Pdf;
+
+namespace iText.Kernel.Mac {
+ ///
+ /// Default
+ ///
+ /// location strategy, which locates MAC container in document's trailer.
+ ///
+ public class StandaloneMacContainerLocator : IMacContainerLocator {
+ ///
+ /// .
+ ///
+ public virtual void LocateMacContainer(AbstractMacIntegrityProtector macIntegrityProtector) {
+ ((StandaloneMacIntegrityProtector)macIntegrityProtector).PrepareDocument();
+ }
+
+ ///
+ /// .
+ ///
+ public virtual AbstractMacIntegrityProtector CreateMacIntegrityProtector(PdfDocument document, MacProperties
+ macProperties) {
+ return new StandaloneMacIntegrityProtector(document, macProperties);
+ }
+
+ ///
+ /// .
+ ///
+ public virtual AbstractMacIntegrityProtector CreateMacIntegrityProtector(PdfDocument document, PdfDictionary
+ authDictionary) {
+ return new StandaloneMacIntegrityProtector(document, authDictionary);
+ }
+ }
+}
diff --git a/itext/itext.kernel/itext/kernel/mac/StandaloneMacIntegrityProtector.cs b/itext/itext.kernel/itext/kernel/mac/StandaloneMacIntegrityProtector.cs
new file mode 100644
index 0000000000..d9023a910d
--- /dev/null
+++ b/itext/itext.kernel/itext/kernel/mac/StandaloneMacIntegrityProtector.cs
@@ -0,0 +1,142 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using System;
+using System.IO;
+using iText.Commons.Bouncycastle.Security;
+using iText.IO.Source;
+using iText.Kernel.Events;
+using iText.Kernel.Exceptions;
+using iText.Kernel.Pdf;
+
+namespace iText.Kernel.Mac {
+//\cond DO_NOT_DOCUMENT
+ /// Class responsible for integrity protection in encrypted documents, which uses MAC container in the standalone mode.
+ ///
+ internal class StandaloneMacIntegrityProtector : AbstractMacIntegrityProtector {
+ private MacPdfObject macPdfObject;
+
+//\cond DO_NOT_DOCUMENT
+ internal StandaloneMacIntegrityProtector(PdfDocument document, MacProperties macProperties)
+ : base(document, macProperties) {
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal StandaloneMacIntegrityProtector(PdfDocument document, PdfDictionary authDictionary)
+ : base(document, authDictionary) {
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual void PrepareDocument() {
+ document.AddEventHandler(PdfDocumentEvent.START_DOCUMENT_CLOSING, new StandaloneMacIntegrityProtector.StandaloneMacPdfObjectAdder
+ (this));
+ document.AddEventHandler(PdfDocumentEvent.END_WRITER_FLUSH, new StandaloneMacIntegrityProtector.StandaloneMacContainerEmbedder
+ (this));
+ }
+//\endcond
+
+ private void EmbedMacContainerInTrailer() {
+ byte[] documentBytes = GetDocumentByteArrayOutputStream().ToArray();
+ long[] byteRange = macPdfObject.ComputeByteRange(documentBytes.Length);
+ long byteRangePosition = macPdfObject.GetByteRangePosition();
+ MemoryStream localBaos = new MemoryStream();
+ PdfOutputStream os = new PdfOutputStream(localBaos);
+ os.Write('[');
+ foreach (long l in byteRange) {
+ os.WriteLong(l).Write(' ');
+ }
+ os.Write(']');
+ Array.Copy(localBaos.ToArray(), 0, documentBytes, (int)byteRangePosition, localBaos.Length);
+ byte[] mac = CreateDocumentDigestAndMacContainer(documentBytes, byteRange);
+ PdfString macString = new PdfString(mac).SetHexWriting(true);
+ // fill in the MAC
+ localBaos.JReset();
+ os.Write(macString);
+ Array.Copy(localBaos.ToArray(), 0, documentBytes, (int)byteRange[1], localBaos.Length);
+ GetDocumentByteArrayOutputStream().JReset();
+ document.GetWriter().GetOutputStream().Write(documentBytes, 0, documentBytes.Length);
+ }
+
+ private byte[] CreateDocumentDigestAndMacContainer(byte[] documentBytes, long[] byteRange) {
+ IRandomAccessSource ras = new RandomAccessSourceFactory().CreateSource(documentBytes);
+ try {
+ using (Stream rg = new RASInputStream(new RandomAccessSourceFactory().CreateRanged(ras, byteRange))) {
+ byte[] dataDigest = DigestBytes(rg);
+ return CreateMacContainer(dataDigest, GenerateRandomBytes(32), null).GetEncoded();
+ }
+ }
+ catch (AbstractGeneralSecurityException e) {
+ throw new PdfException(KernelExceptionMessageConstant.CONTAINER_GENERATION_EXCEPTION, e);
+ }
+ }
+
+ private int GetContainerSizeEstimate() {
+ try {
+ return CreateMacContainer(DigestBytes(new byte[0]), GenerateRandomBytes(32), null).GetEncoded().Length * 2
+ + 2;
+ }
+ catch (AbstractGeneralSecurityException e) {
+ throw new PdfException(KernelExceptionMessageConstant.CONTAINER_GENERATION_EXCEPTION, e);
+ }
+ catch (System.IO.IOException e) {
+ throw new PdfException(KernelExceptionMessageConstant.CONTAINER_GENERATION_EXCEPTION, e);
+ }
+ }
+
+ private MemoryStream GetDocumentByteArrayOutputStream() {
+ return ((MemoryStream)document.GetWriter().GetOutputStream());
+ }
+
+ private sealed class StandaloneMacPdfObjectAdder : iText.Kernel.Events.IEventHandler {
+ public void HandleEvent(Event @event) {
+ this._enclosing.macPdfObject = new MacPdfObject(this._enclosing.GetContainerSizeEstimate());
+ this._enclosing.document.GetTrailer().Put(PdfName.AuthCode, this._enclosing.macPdfObject.GetPdfObject());
+ }
+
+ internal StandaloneMacPdfObjectAdder(StandaloneMacIntegrityProtector _enclosing) {
+ this._enclosing = _enclosing;
+ }
+
+ private readonly StandaloneMacIntegrityProtector _enclosing;
+ }
+
+ private sealed class StandaloneMacContainerEmbedder : iText.Kernel.Events.IEventHandler {
+ public void HandleEvent(Event @event) {
+ try {
+ this._enclosing.EmbedMacContainerInTrailer();
+ }
+ catch (System.IO.IOException e) {
+ throw new PdfException(KernelExceptionMessageConstant.CONTAINER_EMBEDDING_EXCEPTION, e);
+ }
+ }
+
+ internal StandaloneMacContainerEmbedder(StandaloneMacIntegrityProtector _enclosing) {
+ this._enclosing = _enclosing;
+ }
+
+ private readonly StandaloneMacIntegrityProtector _enclosing;
+ }
+ }
+//\endcond
+}
diff --git a/itext/itext.kernel/itext/kernel/pdf/PdfDocument.cs b/itext/itext.kernel/itext/kernel/pdf/PdfDocument.cs
index c4b0ae44d8..fa19ec34e6 100644
--- a/itext/itext.kernel/itext/kernel/pdf/PdfDocument.cs
+++ b/itext/itext.kernel/itext/kernel/pdf/PdfDocument.cs
@@ -995,7 +995,6 @@ public virtual void Close() {
}
}
catalog.GetPageTree().ClearPageRefs();
- RemoveAllHandlers();
}
catch (System.IO.IOException e) {
throw new PdfException(KernelExceptionMessageConstant.CANNOT_CLOSE_DOCUMENT, e, this);
@@ -2211,7 +2210,7 @@ protected internal virtual void Open(PdfVersion newPdfVersion) {
writer.crypto = reader.decrypt;
if (writer.crypto != null) {
writer.crypto.CheckEncryptionRequirements(this);
- writer.crypto.ConfigureEncryptionParameters(this, true);
+ writer.crypto.ConfigureEncryptionParametersFromWriter(this);
}
if (newPdfVersion != null) {
// In PDF 1.4, a PDF version can also be specified in the Version entry of the document catalog,
@@ -2246,7 +2245,7 @@ protected internal virtual void Open(PdfVersion newPdfVersion) {
encryptedEmbeddedStreamsHandler.StoreAllEmbeddedStreams();
}
writer.crypto.CheckEncryptionRequirements(this);
- writer.crypto.ConfigureEncryptionParameters(this, true);
+ writer.crypto.ConfigureEncryptionParametersFromWriter(this);
}
}
}
diff --git a/itext/itext.kernel/itext/kernel/pdf/PdfEncryption.cs b/itext/itext.kernel/itext/kernel/pdf/PdfEncryption.cs
index c0222a94fa..7c48745450 100644
--- a/itext/itext.kernel/itext/kernel/pdf/PdfEncryption.cs
+++ b/itext/itext.kernel/itext/kernel/pdf/PdfEncryption.cs
@@ -64,11 +64,7 @@ public class PdfEncryption : PdfObjectWrapper {
private SecurityHandler securityHandler;
- private readonly MacIntegrityProtector macContainer;
-
- public virtual MacIntegrityProtector GetMacContainer() {
- return macContainer;
- }
+ private AbstractMacIntegrityProtector macContainer;
/// Creates the encryption.
///
@@ -201,11 +197,11 @@ public PdfEncryption(byte[] userPassword, byte[] ownerPassword, int permissions,
///
///
///
- ///
+ ///
/// class for MAC integrity protection
///
public PdfEncryption(byte[] userPassword, byte[] ownerPassword, int permissions, int encryptionType, byte[]
- documentId, PdfVersion version, MacIntegrityProtector macContainer)
+ documentId, PdfVersion version, AbstractMacIntegrityProtector macContainer)
: base(new PdfDictionary()) {
this.macContainer = macContainer;
this.documentId = documentId;
@@ -383,11 +379,11 @@ public PdfEncryption(IX509Certificate[] certs, int[] permissions, int encryption
///
///
///
- ///
+ ///
/// class for MAC integrity protection
///
public PdfEncryption(IX509Certificate[] certs, int[] permissions, int encryptionType, PdfVersion version,
- MacIntegrityProtector macContainer)
+ AbstractMacIntegrityProtector macContainer)
: base(new PdfDictionary()) {
this.macContainer = macContainer;
for (int i = 0; i < permissions.Length; i++) {
@@ -476,12 +472,11 @@ public PdfEncryption(PdfDictionary pdfDict, byte[] password, byte[] documentId)
///
///
///
- ///
- /// , which is responsible for MAC integrity
- /// protection and validation
+ ///
+ /// class for MAC integrity protection
///
- public PdfEncryption(PdfDictionary pdfDict, byte[] password, byte[] documentId, MacIntegrityProtector macContainer
- )
+ public PdfEncryption(PdfDictionary pdfDict, byte[] password, byte[] documentId, AbstractMacIntegrityProtector
+ macContainer)
: base(pdfDict) {
SetForbidRelease();
this.macContainer = macContainer;
@@ -594,10 +589,10 @@ public PdfEncryption(PdfDictionary pdfDict, IPrivateKey certificateKey, IX509Cer
///
///
///
- ///
- /// , which is responsible for MAC integrity protection and validation
+ ///
+ /// class for MAC integrity protection
///
- public PdfEncryption(PdfDictionary pdfDict, IPrivateKey certificateKey, IX509Certificate certificate, MacIntegrityProtector
+ public PdfEncryption(PdfDictionary pdfDict, IPrivateKey certificateKey, IX509Certificate certificate, AbstractMacIntegrityProtector
macContainer)
: base(pdfDict) {
this.macContainer = macContainer;
@@ -1148,32 +1143,46 @@ internal virtual void CheckEncryptionRequirements(PdfDocument document) {
//\endcond
//\cond DO_NOT_DOCUMENT
- internal virtual void CheckEncryptionPermissions() {
- if (macContainer == null && permissions != null && (permissions & MAC_DISABLED) == 0) {
- throw new PdfException(KernelExceptionMessageConstant.MAC_PERMS_WITHOUT_MAC);
+ internal virtual void ConfigureEncryptionParametersFromWriter(PdfDocument document) {
+ if (macContainer != null) {
+ macContainer.SetFileEncryptionKey(securityHandler.GetMkey().Length == 0 ? securityHandler.GetNextObjectKey
+ () : securityHandler.GetMkey());
+ document.GetDiContainer().GetInstance().LocateMacContainer(macContainer);
+ document.GetCatalog().AddDeveloperExtension(PdfDeveloperExtension.ISO_32004);
+ PdfString kdfSalt = GetPdfObject().GetAsString(PdfName.KDFSalt);
+ if (kdfSalt == null) {
+ GetPdfObject().Put(PdfName.KDFSalt, new PdfString(macContainer.GetKdfSalt()).SetHexWriting(true));
+ }
+ }
+ if (GetCryptoMode() == EncryptionConstants.ENCRYPTION_AES_GCM) {
+ document.GetCatalog().AddDeveloperExtension(PdfDeveloperExtension.ISO_32003);
}
}
//\endcond
//\cond DO_NOT_DOCUMENT
- internal virtual void ConfigureEncryptionParameters(PdfDocument document, bool forWriting) {
- if (macContainer != null) {
+ internal virtual AbstractMacIntegrityProtector GetMacContainer() {
+ return macContainer;
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual void ConfigureEncryptionParametersFromReader(PdfDocument document, PdfDictionary trailer) {
+ if (trailer.GetAsDictionary(PdfName.AuthCode) != null) {
+ macContainer = document.GetDiContainer().GetInstance().CreateMacIntegrityProtector(document
+ , trailer.GetAsDictionary(PdfName.AuthCode));
macContainer.SetFileEncryptionKey(securityHandler.GetMkey().Length == 0 ? securityHandler.GetNextObjectKey
() : securityHandler.GetMkey());
PdfString kdfSalt = GetPdfObject().GetAsString(PdfName.KDFSalt);
if (kdfSalt != null) {
macContainer.SetKdfSalt(kdfSalt.GetValueBytes());
}
- if (forWriting) {
- macContainer.PrepareDocument();
- document.GetCatalog().AddDeveloperExtension(PdfDeveloperExtension.ISO_32004);
- if (kdfSalt == null) {
- GetPdfObject().Put(PdfName.KDFSalt, new PdfString(macContainer.GetKdfSalt()).SetHexWriting(true));
- }
- }
+ macContainer.ValidateMacToken();
}
- if (GetCryptoMode() == EncryptionConstants.ENCRYPTION_AES_GCM && forWriting) {
- document.GetCatalog().AddDeveloperExtension(PdfDeveloperExtension.ISO_32003);
+ else {
+ if (permissions != null && (permissions & MAC_DISABLED) == 0) {
+ throw new PdfException(KernelExceptionMessageConstant.MAC_PERMS_WITHOUT_MAC);
+ }
}
}
//\endcond
diff --git a/itext/itext.kernel/itext/kernel/pdf/PdfName.cs b/itext/itext.kernel/itext/kernel/pdf/PdfName.cs
index aeeacf056a..e2dde52a84 100644
--- a/itext/itext.kernel/itext/kernel/pdf/PdfName.cs
+++ b/itext/itext.kernel/itext/kernel/pdf/PdfName.cs
@@ -202,6 +202,8 @@ public class PdfName : PdfPrimitiveObject, IComparable
public static readonly iText.Kernel.Pdf.PdfName AsIs = CreateDirectName("AsIs");
+ public static readonly iText.Kernel.Pdf.PdfName AttachedToSig = CreateDirectName("AttachedToSig");
+
public static readonly iText.Kernel.Pdf.PdfName AuthEvent = CreateDirectName("AuthEvent");
public static readonly iText.Kernel.Pdf.PdfName AuthCode = CreateDirectName("AuthCode");
@@ -1466,6 +1468,8 @@ public class PdfName : PdfPrimitiveObject, IComparable
public static readonly iText.Kernel.Pdf.PdfName Signed = CreateDirectName("Signed");
+ public static readonly iText.Kernel.Pdf.PdfName SigObjRef = CreateDirectName("SigObjRef");
+
public static readonly iText.Kernel.Pdf.PdfName SigRef = CreateDirectName("SigRef");
public static readonly iText.Kernel.Pdf.PdfName Simplex = CreateDirectName("Simplex");
diff --git a/itext/itext.kernel/itext/kernel/pdf/PdfReader.cs b/itext/itext.kernel/itext/kernel/pdf/PdfReader.cs
index ea18d8624f..e2962c86ab 100644
--- a/itext/itext.kernel/itext/kernel/pdf/PdfReader.cs
+++ b/itext/itext.kernel/itext/kernel/pdf/PdfReader.cs
@@ -29,7 +29,6 @@ You should have received a copy of the GNU Affero General Public License
using iText.IO.Source;
using iText.Kernel.Crypto.Securityhandler;
using iText.Kernel.Exceptions;
-using iText.Kernel.Mac;
using iText.Kernel.Pdf.Filters;
using iText.Kernel.XMP;
@@ -1541,33 +1540,24 @@ private void ReadDecryptObj() {
return;
}
encrypted = true;
- MacIntegrityProtector mac = null;
- if (trailer.GetAsDictionary(PdfName.AuthCode) != null && trailer.GetAsDictionary(PdfName.AuthCode).GetAsString
- (PdfName.MAC) != null) {
- mac = new MacIntegrityProtector(pdfDocument, trailer.GetAsDictionary(PdfName.AuthCode));
- }
PdfName filter = enc.GetAsName(PdfName.Filter);
if (PdfName.Adobe_PubSec.Equals(filter)) {
if (properties.certificate == null) {
throw new PdfException(KernelExceptionMessageConstant.CERTIFICATE_IS_NOT_PROVIDED_DOCUMENT_IS_ENCRYPTED_WITH_PUBLIC_KEY_CERTIFICATE
);
}
- decrypt = new PdfEncryption(enc, properties.certificateKey, properties.certificate, mac);
+ decrypt = new PdfEncryption(enc, properties.certificateKey, properties.certificate);
}
else {
if (PdfName.Standard.Equals(filter)) {
- decrypt = new PdfEncryption(enc, properties.password, GetOriginalFileId(), mac);
+ decrypt = new PdfEncryption(enc, properties.password, GetOriginalFileId());
}
else {
throw new UnsupportedSecurityHandlerException(MessageFormatUtil.Format(KernelExceptionMessageConstant.UNSUPPORTED_SECURITY_HANDLER
, filter));
}
}
- decrypt.CheckEncryptionPermissions();
- if (mac != null) {
- decrypt.ConfigureEncryptionParameters(pdfDocument, false);
- mac.ValidateMacToken(trailer.GetAsDictionary(PdfName.AuthCode));
- }
+ decrypt.ConfigureEncryptionParametersFromReader(pdfDocument, trailer);
}
private PdfObject ReadObject(PdfIndirectReference reference, bool fixXref) {
diff --git a/itext/itext.kernel/itext/kernel/pdf/PdfWriter.cs b/itext/itext.kernel/itext/kernel/pdf/PdfWriter.cs
index 31c0351035..bc4ea878f4 100644
--- a/itext/itext.kernel/itext/kernel/pdf/PdfWriter.cs
+++ b/itext/itext.kernel/itext/kernel/pdf/PdfWriter.cs
@@ -204,8 +204,8 @@ public virtual iText.Kernel.Pdf.PdfWriter SetSmartMode(bool smartMode) {
///
protected internal virtual void InitCryptoIfSpecified(PdfVersion version) {
EncryptionProperties encryptProps = properties.encryptionProperties;
- MacIntegrityProtector mac = encryptProps.macProperties == null ? null : new MacIntegrityProtector(document
- , encryptProps.macProperties);
+ AbstractMacIntegrityProtector mac = encryptProps.macProperties == null ? null : document.GetDiContainer().
+ GetInstance().CreateMacIntegrityProtector(document, encryptProps.macProperties);
if (properties.IsStandardEncryptionUsed()) {
crypto = new PdfEncryption(encryptProps.userPassword, encryptProps.ownerPassword, encryptProps.standardEncryptPermissions
, encryptProps.encryptionAlgorithm, ByteUtils.GetIsoBytes(this.document.GetOriginalDocumentId().GetValue
diff --git a/itext/itext.kernel/itext/kernel/utils/RegisterDefaultDiContainer.cs b/itext/itext.kernel/itext/kernel/utils/RegisterDefaultDiContainer.cs
index 86bf4353f2..d102d45dde 100644
--- a/itext/itext.kernel/itext/kernel/utils/RegisterDefaultDiContainer.cs
+++ b/itext/itext.kernel/itext/kernel/utils/RegisterDefaultDiContainer.cs
@@ -22,6 +22,7 @@ You should have received a copy of the GNU Affero General Public License
*/
using iText.Commons.Utils;
using iText.Kernel.DI.Pagetree;
+using iText.Kernel.Mac;
namespace iText.Kernel.Utils {
/// Registers a default instance for a dependency injection container for the kernel module.
@@ -40,6 +41,7 @@ static RegisterDefaultDiContainer() {
// sharp
DIContainer.RegisterDefault(typeof(IPageTreeListFactory), () => new DefaultPageTreeListFactory(DEFAULT_PAGE_TREE_LIST_FACTORY_MAX_SAFE_ENTRIES
));
+ DIContainer.RegisterDefault(typeof(IMacContainerLocator), () => new StandaloneMacContainerLocator());
}
}
}
diff --git a/itext/itext.sign/itext/signatures/CertificateUtil.cs b/itext/itext.sign/itext/signatures/CertificateUtil.cs
index 44d7b308f6..343f508828 100644
--- a/itext/itext.sign/itext/signatures/CertificateUtil.cs
+++ b/itext/itext.sign/itext/signatures/CertificateUtil.cs
@@ -286,7 +286,7 @@ public static void RetrieveRevocationInfoFromSignedData(IAsn1TaggedObject tagged
///
public static IDerSet CreateRevocationInfoChoices(ICollection crls, ICollection ocsps, ICollection otherRevocationInfoFormats) {
- if (crls.Count == 0 && ocsps.Count == 0) {
+ if (crls.IsEmpty() && ocsps.IsEmpty()) {
return null;
}
IAsn1EncodableVector revocationInfoChoices = FACTORY.CreateASN1EncodableVector();
diff --git a/itext/itext.sign/itext/signatures/PdfPKCS7.cs b/itext/itext.sign/itext/signatures/PdfPKCS7.cs
index 71c0bc548c..f718b152d9 100644
--- a/itext/itext.sign/itext/signatures/PdfPKCS7.cs
+++ b/itext/itext.sign/itext/signatures/PdfPKCS7.cs
@@ -74,6 +74,9 @@ public class PdfPKCS7 {
///
private readonly ICollection signedDataRevocationInfo = new List();
+ private readonly IAsn1EncodableVector unsignedAttributes = BOUNCY_CASTLE_FACTORY.CreateASN1EncodableVector
+ ();
+
// Constructors for creating new signatures
/// Assembles all the elements needed to create a signature, except for the data.
/// the private key
@@ -388,6 +391,15 @@ public PdfPKCS7(byte[] contentsKey, PdfName filterSubtype) {
}
}
+ /// Get unsigned attributes associated with this PKCS7 signature container.
+ ///
+ /// unsigned attributes as
+ ///
+ ///
+ public virtual IAsn1EncodableVector GetUnsignedAttributes() {
+ return unsignedAttributes;
+ }
+
/// Set signature policy identifier to be used during signature creation.
///
///
@@ -878,13 +890,11 @@ public virtual byte[] GetEncodedPKCS7(byte[] secondDigest, PdfSigner.CryptoStand
if (tsaClient != null) {
byte[] tsImprint = tsaClient.GetMessageDigest().Digest(signatureValue);
byte[] tsToken = tsaClient.GetTimeStampToken(tsImprint);
- if (tsToken != null) {
- IAsn1EncodableVector unauthAttributes = BuildUnauthenticatedAttributes(tsToken);
- if (unauthAttributes != null) {
- signerInfo.Add(BOUNCY_CASTLE_FACTORY.CreateDERTaggedObject(false, 1, BOUNCY_CASTLE_FACTORY.CreateDERSet(unauthAttributes
- )));
- }
- }
+ AddTimestampTokenToUnsignedAttributes(tsToken);
+ }
+ if (unsignedAttributes.Size() > 0) {
+ signerInfo.Add(BOUNCY_CASTLE_FACTORY.CreateDERTaggedObject(false, 1, BOUNCY_CASTLE_FACTORY.CreateDERSet(unsignedAttributes
+ )));
}
// Finally build the body out of all the components above
IAsn1EncodableVector body = BOUNCY_CASTLE_FACTORY.CreateASN1EncodableVector();
@@ -925,15 +935,10 @@ public virtual byte[] GetEncodedPKCS7(byte[] secondDigest, PdfSigner.CryptoStand
/// handled by the (vendor supplied) TSA request/response interface).
///
/// byte[] - time stamp token, DER encoded signedData
- ///
- ///
- ///
- ///
- private IAsn1EncodableVector BuildUnauthenticatedAttributes(byte[] timeStampToken) {
+ private void AddTimestampTokenToUnsignedAttributes(byte[] timeStampToken) {
if (timeStampToken == null) {
- return null;
+ return;
}
- IAsn1EncodableVector unauthAttributes = BOUNCY_CASTLE_FACTORY.CreateASN1EncodableVector();
IAsn1EncodableVector v = BOUNCY_CASTLE_FACTORY.CreateASN1EncodableVector();
v.Add(BOUNCY_CASTLE_FACTORY.CreateASN1ObjectIdentifier(SecurityIDs.ID_AA_TIME_STAMP_TOKEN));
using (IAsn1InputStream tempstream = BOUNCY_CASTLE_FACTORY.CreateASN1InputStream(new MemoryStream(timeStampToken
@@ -941,8 +946,7 @@ private IAsn1EncodableVector BuildUnauthenticatedAttributes(byte[] timeStampToke
IAsn1Sequence seq = BOUNCY_CASTLE_FACTORY.CreateASN1Sequence(tempstream.ReadObject());
v.Add(BOUNCY_CASTLE_FACTORY.CreateDERSet(seq));
}
- unauthAttributes.Add(BOUNCY_CASTLE_FACTORY.CreateDERSequence(v));
- return unauthAttributes;
+ unsignedAttributes.Add(BOUNCY_CASTLE_FACTORY.CreateDERSequence(v));
}
// Authenticated attributes
diff --git a/itext/itext.sign/itext/signatures/PdfSigner.cs b/itext/itext.sign/itext/signatures/PdfSigner.cs
index 4fd1a115d3..7fd7f53a6d 100644
--- a/itext/itext.sign/itext/signatures/PdfSigner.cs
+++ b/itext/itext.sign/itext/signatures/PdfSigner.cs
@@ -39,6 +39,7 @@ You should have received a copy of the GNU Affero General Public License
using iText.Kernel.Exceptions;
using iText.Kernel.Font;
using iText.Kernel.Geom;
+using iText.Kernel.Mac;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Annot;
using iText.Kernel.Pdf.Tagutils;
@@ -47,10 +48,13 @@ You should have received a copy of the GNU Affero General Public License
using iText.Layout.Tagging;
using iText.Pdfa;
using iText.Signatures.Exceptions;
+using iText.Signatures.Mac;
namespace iText.Signatures {
/// Takes care of the cryptographic options and appearances that form a signature.
public class PdfSigner {
+ private const int MAXIMUM_MAC_SIZE = 788;
+
/// Enum containing the Cryptographic Standards.
/// Enum containing the Cryptographic Standards. Possible values are "CMS" and "CADES".
public enum CryptoStandard {
@@ -167,6 +171,7 @@ public PdfSigner(PdfReader reader, Stream outputStream, String path, StampingPro
///
public PdfSigner(PdfReader reader, Stream outputStream, String path, StampingProperties properties) {
StampingProperties localProps = new StampingProperties(properties).PreserveEncryption();
+ localProps.RegisterDependency(typeof(IMacContainerLocator), new SignatureMacContainerLocator());
if (path == null) {
this.temporaryOS = new MemoryStream();
this.document = InitDocument(reader, new PdfWriter(temporaryOS), localProps);
@@ -499,6 +504,10 @@ public virtual void SignDetached(IExternalDigest externalDigest, IExternalSignat
if (tsaClient != null) {
estimatedSize += tsaClient.GetTokenSizeEstimate() + 96;
}
+ if (document.GetTrailer().GetAsDictionary(PdfName.AuthCode) != null) {
+ // if AuthCode is found in trailer, we assume MAC will be embedded and allocate additional space.
+ estimatedSize += MAXIMUM_MAC_SIZE;
+ }
}
this.signerName = PdfSigner.GetSignerName((IX509Certificate)chain[0]);
if (sigtype == PdfSigner.CryptoStandard.CADES && !IsDocumentPdf2()) {
@@ -546,6 +555,8 @@ public virtual void SignDetached(IExternalDigest externalDigest, IExternalSignat
byte[] extSignature = externalSignature.Sign(sh);
sgn.SetExternalSignatureValue(extSignature, null, externalSignature.GetSignatureAlgorithmName(), externalSignature
.GetSignatureMechanismParameters());
+ document.DispatchEvent(new SignatureContainerGenerationEvent(sgn.GetUnsignedAttributes(), extSignature, GetRangeStream
+ ()));
byte[] encodedSig = sgn.GetEncodedPKCS7(hash, sigtype, tsaClient, ocspList, crlBytes);
if (estimatedSize < encodedSig.Length) {
throw new System.IO.IOException("Not enough space");
@@ -735,6 +746,8 @@ protected internal virtual void PreClose(IDictionary exclusionSiz
throw new PdfException(SignExceptionMessageConstant.NO_CRYPTO_DICTIONARY_DEFINED);
}
cryptoDictionary.GetPdfObject().MakeIndirect(document);
+ document.DispatchEvent(new SignatureDocumentClosingEvent(cryptoDictionary.GetPdfObject().GetIndirectReference
+ ()));
if (fieldExist) {
fieldLock = PopulateExistingSignatureFormField(acroForm);
}
diff --git a/itext/itext.sign/itext/signatures/mac/SignatureContainerGenerationEvent.cs b/itext/itext.sign/itext/signatures/mac/SignatureContainerGenerationEvent.cs
new file mode 100644
index 0000000000..5e13a07fff
--- /dev/null
+++ b/itext/itext.sign/itext/signatures/mac/SignatureContainerGenerationEvent.cs
@@ -0,0 +1,105 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using System;
+using System.IO;
+using iText.Commons.Bouncycastle.Asn1;
+using iText.Kernel.Events;
+
+namespace iText.Signatures.Mac {
+ /// Represents an event firing before creating signature container.
+ public class SignatureContainerGenerationEvent : Event {
+ public const String START_SIGNATURE_CONTAINER_GENERATION = "StartSignatureContainerGeneration";
+
+ private readonly IAsn1EncodableVector unsignedAttributes;
+
+ private readonly byte[] signature;
+
+ private readonly Stream documentInputStream;
+
+ /// Creates an event firing before creating the signature container.
+ ///
+ ///
+ ///
+ /// unsigned signature attributes
+ ///
+ ///
+ ///
+ /// byte[]
+ /// signature value
+ ///
+ ///
+ ///
+ ///
+ /// containing document bytes considering byte range
+ ///
+ public SignatureContainerGenerationEvent(IAsn1EncodableVector unsignedAttributes, byte[] signature, Stream
+ documentInputStream)
+ : base(START_SIGNATURE_CONTAINER_GENERATION) {
+ this.unsignedAttributes = unsignedAttributes;
+ this.signature = signature;
+ this.documentInputStream = documentInputStream;
+ }
+
+ ///
+ /// Gets
+ ///
+ /// unsigned signature attributes.
+ ///
+ ///
+ ///
+ ///
+ /// unsigned signature attributes
+ ///
+ public virtual IAsn1EncodableVector GetUnsignedAttributes() {
+ return unsignedAttributes;
+ }
+
+ ///
+ /// Gets
+ /// byte[]
+ /// signature value.
+ ///
+ ///
+ ///
+ /// byte[]
+ /// signature value
+ ///
+ public virtual byte[] GetSignature() {
+ return signature;
+ }
+
+ ///
+ /// Gets
+ ///
+ /// containing document bytes considering byte range.
+ ///
+ ///
+ ///
+ ///
+ /// containing document bytes considering byte range
+ ///
+ public virtual Stream GetDocumentInputStream() {
+ return documentInputStream;
+ }
+ }
+}
diff --git a/itext/itext.sign/itext/signatures/mac/SignatureDocumentClosingEvent.cs b/itext/itext.sign/itext/signatures/mac/SignatureDocumentClosingEvent.cs
new file mode 100644
index 0000000000..7568325914
--- /dev/null
+++ b/itext/itext.sign/itext/signatures/mac/SignatureDocumentClosingEvent.cs
@@ -0,0 +1,63 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using System;
+using iText.Kernel.Events;
+using iText.Kernel.Pdf;
+
+namespace iText.Signatures.Mac {
+ /// Represents an event firing before embedding the signature into the document.
+ public class SignatureDocumentClosingEvent : Event {
+ public const String START_SIGNATURE_PRE_CLOSE = "StartSignaturePreClose";
+
+ private readonly PdfIndirectReference signatureReference;
+
+ /// Creates an event firing before embedding the signature into the document.
+ ///
+ /// Creates an event firing before embedding the signature into the document.
+ /// It contains the reference to the signature object.
+ ///
+ ///
+ ///
+ ///
+ /// to the signature object
+ ///
+ public SignatureDocumentClosingEvent(PdfIndirectReference signatureReference)
+ : base(START_SIGNATURE_PRE_CLOSE) {
+ this.signatureReference = signatureReference;
+ }
+
+ ///
+ /// Gets
+ ///
+ /// to the signature object.
+ ///
+ ///
+ ///
+ ///
+ /// to the signature object
+ ///
+ public virtual PdfIndirectReference GetSignatureReference() {
+ return signatureReference;
+ }
+ }
+}
diff --git a/itext/itext.sign/itext/signatures/mac/SignatureMacContainerLocator.cs b/itext/itext.sign/itext/signatures/mac/SignatureMacContainerLocator.cs
new file mode 100644
index 0000000000..e726f5f1c2
--- /dev/null
+++ b/itext/itext.sign/itext/signatures/mac/SignatureMacContainerLocator.cs
@@ -0,0 +1,60 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using iText.Kernel.Mac;
+using iText.Kernel.Pdf;
+
+namespace iText.Signatures.Mac {
+ ///
+ ///
+ /// strategy, which should be used specifically in case of signature creation.
+ ///
+ ///
+ ///
+ /// strategy, which should be used specifically in case of signature creation.
+ /// This strategy locates MAC container in signature unsigned attributes.
+ ///
+ public class SignatureMacContainerLocator : IMacContainerLocator {
+ ///
+ /// .
+ ///
+ public virtual void LocateMacContainer(AbstractMacIntegrityProtector macIntegrityProtector) {
+ ((SignatureMacIntegrityProtector)macIntegrityProtector).PrepareDocument();
+ }
+
+ ///
+ /// .
+ ///
+ public virtual AbstractMacIntegrityProtector CreateMacIntegrityProtector(PdfDocument document, MacProperties
+ macProperties) {
+ return new SignatureMacIntegrityProtector(document, macProperties);
+ }
+
+ ///
+ /// .
+ ///
+ public virtual AbstractMacIntegrityProtector CreateMacIntegrityProtector(PdfDocument document, PdfDictionary
+ authDictionary) {
+ return new SignatureMacIntegrityProtector(document, authDictionary);
+ }
+ }
+}
diff --git a/itext/itext.sign/itext/signatures/mac/SignatureMacIntegrityProtector.cs b/itext/itext.sign/itext/signatures/mac/SignatureMacIntegrityProtector.cs
new file mode 100644
index 0000000000..28c00931a7
--- /dev/null
+++ b/itext/itext.sign/itext/signatures/mac/SignatureMacIntegrityProtector.cs
@@ -0,0 +1,120 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse Software.
+
+This program is offered under a commercial and under the AGPL license.
+For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+AGPL licensing:
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 .
+*/
+using System;
+using System.IO;
+using iText.Bouncycastleconnector;
+using iText.Commons.Bouncycastle;
+using iText.Commons.Bouncycastle.Asn1;
+using iText.Commons.Bouncycastle.Security;
+using iText.Kernel.Events;
+using iText.Kernel.Exceptions;
+using iText.Kernel.Mac;
+using iText.Kernel.Pdf;
+
+namespace iText.Signatures.Mac {
+//\cond DO_NOT_DOCUMENT
+ /// Class responsible for integrity protection in encrypted documents which uses MAC container in the signature mode.
+ ///
+ internal class SignatureMacIntegrityProtector : AbstractMacIntegrityProtector {
+ private static readonly IBouncyCastleFactory BC_FACTORY = BouncyCastleFactoryCreator.GetFactory();
+
+ private const String ID_ATTR_PDF_MAC_DATA = "1.0.32004.1.2";
+
+//\cond DO_NOT_DOCUMENT
+ internal SignatureMacIntegrityProtector(PdfDocument document, MacProperties macProperties)
+ : base(document, macProperties) {
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal SignatureMacIntegrityProtector(PdfDocument document, PdfDictionary authDictionary)
+ : base(document, authDictionary) {
+ }
+//\endcond
+
+//\cond DO_NOT_DOCUMENT
+ internal virtual void PrepareDocument() {
+ document.AddEventHandler(SignatureDocumentClosingEvent.START_SIGNATURE_PRE_CLOSE, new SignatureMacIntegrityProtector.SignatureMacPdfObjectAdder
+ (this));
+ document.AddEventHandler(SignatureContainerGenerationEvent.START_SIGNATURE_CONTAINER_GENERATION, new SignatureMacIntegrityProtector.SignatureMacContainerEmbedder
+ (this));
+ }
+//\endcond
+
+ private void EmbedMacContainerInUnsignedAttributes(IAsn1EncodableVector unsignedAttributes, Stream documentInputStream
+ , byte[] signature) {
+ IDerSequence mac;
+ try {
+ byte[] dataDigest = DigestBytes(documentInputStream);
+ mac = CreateMacContainer(dataDigest, GenerateRandomBytes(32), signature);
+ }
+ catch (AbstractGeneralSecurityException e) {
+ throw new PdfException(KernelExceptionMessageConstant.CONTAINER_GENERATION_EXCEPTION, e);
+ }
+ IAsn1EncodableVector macAttribute = BC_FACTORY.CreateASN1EncodableVector();
+ macAttribute.Add(BC_FACTORY.CreateASN1ObjectIdentifier(ID_ATTR_PDF_MAC_DATA));
+ macAttribute.Add(BC_FACTORY.CreateDERSet(mac));
+ unsignedAttributes.Add(BC_FACTORY.CreateDERSequence(macAttribute));
+ }
+
+ private sealed class SignatureMacPdfObjectAdder : iText.Kernel.Events.IEventHandler {
+ public void HandleEvent(Event @event) {
+ if (@event is SignatureDocumentClosingEvent) {
+ PdfDictionary signatureMacDictionary = new PdfDictionary();
+ signatureMacDictionary.Put(PdfName.MACLocation, PdfName.AttachedToSig);
+ signatureMacDictionary.Put(PdfName.SigObjRef, ((SignatureDocumentClosingEvent)@event).GetSignatureReference
+ ());
+ this._enclosing.document.GetTrailer().Put(PdfName.AuthCode, signatureMacDictionary);
+ }
+ }
+
+ internal SignatureMacPdfObjectAdder(SignatureMacIntegrityProtector _enclosing) {
+ this._enclosing = _enclosing;
+ }
+
+ private readonly SignatureMacIntegrityProtector _enclosing;
+ }
+
+ private sealed class SignatureMacContainerEmbedder : iText.Kernel.Events.IEventHandler {
+ public void HandleEvent(Event @event) {
+ if (@event is SignatureContainerGenerationEvent) {
+ SignatureContainerGenerationEvent signatureEvent = (SignatureContainerGenerationEvent)@event;
+ try {
+ this._enclosing.EmbedMacContainerInUnsignedAttributes(signatureEvent.GetUnsignedAttributes(), signatureEvent
+ .GetDocumentInputStream(), signatureEvent.GetSignature());
+ }
+ catch (System.IO.IOException e) {
+ throw new PdfException(KernelExceptionMessageConstant.CONTAINER_EMBEDDING_EXCEPTION, e);
+ }
+ }
+ }
+
+ internal SignatureMacContainerEmbedder(SignatureMacIntegrityProtector _enclosing) {
+ this._enclosing = _enclosing;
+ }
+
+ private readonly SignatureMacIntegrityProtector _enclosing;
+ }
+ }
+//\endcond
+}
diff --git a/port-hash b/port-hash
index 24b3651d33..0ab469136a 100644
--- a/port-hash
+++ b/port-hash
@@ -1 +1 @@
-b8c5e6beca1ad23f97f05a5ba32ff17d7da82d31
+372ff5bcad8a28fe396b40fbb51d89e728a33728