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