Skip to content

Commit 43b1e4c

Browse files
author
Eugene Bochilo
committed
Create and embed MAC token while signing the documents
DEVSIX-8606
1 parent b8c5e6b commit 43b1e4c

File tree

44 files changed

+1268
-323
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1268
-323
lines changed

bouncy-castle-adapter/src/main/java/com/itextpdf/bouncycastle/asn1/ASN1EncodableVectorBC.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ public void addOptional(IAlgorithmIdentifier element) {
121121
}
122122
}
123123

124+
/**
125+
* {@inheritDoc}
126+
*/
127+
@Override
128+
public int size() {
129+
return encodableVector.size();
130+
}
131+
124132
/**
125133
* Indicates whether some other object is "equal to" this one. Compares wrapped objects.
126134
*/

bouncy-castle-fips-adapter/src/main/java/com/itextpdf/bouncycastlefips/asn1/ASN1EncodableVectorBCFips.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ public void addOptional(IAlgorithmIdentifier element) {
120120
}
121121
}
122122

123+
/**
124+
* {@inheritDoc}
125+
*/
126+
@Override
127+
public int size() {
128+
return encodableVector.size();
129+
}
130+
123131
/**
124132
* Indicates whether some other object is "equal to" this one. Compares wrapped objects.
125133
*/

commons/src/main/java/com/itextpdf/commons/bouncycastle/asn1/IASN1EncodableVector.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,11 @@ public interface IASN1EncodableVector {
7171
* @param element AlgorithmIdentifier wrapper.
7272
*/
7373
void addOptional(IAlgorithmIdentifier element);
74+
75+
/**
76+
* Calls actual {@code size} method for the wrapped ASN1EncodableVector object.
77+
*
78+
* @return {@code int} representing current vector size
79+
*/
80+
int size();
7481
}

kernel/src/main/java/com/itextpdf/kernel/exceptions/KernelExceptionMessageConstant.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,19 @@ public final class KernelExceptionMessageConstant {
268268
public static final String LZW_DECODER_EXCEPTION = "LZW decoder exception.";
269269
public static final String LZW_FLAVOUR_NOT_SUPPORTED = "LZW flavour not supported.";
270270
public static final String MAC_ALGORITHM_NOT_SUPPORTED = "This MAC algorithm is not supported.";
271+
public static final String MAC_ATTRIBUTE_NOT_SPECIFIED =
272+
"Signature doesn't contain unsigned MAC attribute, which is required in \"attached to signature\" mode.";
273+
public static final String MAC_EXTRACTION_EXCEPTION =
274+
"Exception occurred during signature parsing. It is not possible to extract MAC.";
275+
public static final String MAC_LOCATION_NOT_SPECIFIED = "AuthCode dictionary doesn't contain MACLocation entry.";
276+
public static final String MAC_NOT_SPECIFIED =
277+
"AuthCode dictionary doesn't contain MAC entry, which is required in standalone mode.";
271278
public static final String MAC_FOR_ENCRYPTION_5 =
272279
"MAC integrity protection is only supported for encryption algorithms of version 5 or higher.";
273280
public static final String MAC_FOR_PDF_2 = "MAC integrity protection is only supported for PDF 2.0 or higher.";
274281
public static final String MAC_PERMS_WITHOUT_MAC = "Permissions bit 13 is set to zero, "
275282
+ "which indicates that MAC integrity protection is enabled. However MAC container wasn't found.";
283+
public static final String MAC_VALIDATION_EXCEPTION = "Unexpected exception occurred during MAC token validation.";
276284
public static final String MAC_VALIDATION_FAILED =
277285
"MAC integrity protection was compromised. Document content was modified.";
278286
public static final String MISSING_REQUIRED_FIELD_IN_FONT_DICTIONARY
@@ -337,6 +345,8 @@ public final class KernelExceptionMessageConstant {
337345
public static final String RESOURCES_DO_NOT_CONTAIN_EXTGSTATE_ENTRY_UNABLE_TO_PROCESS_THIS_OPERATOR = "Resources "
338346
+ "do not contain ExtGState entry. Unable to process operator {0}.";
339347
public static final String SHADING_TYPE_NOT_FOUND = "Shading type not found.";
348+
public static final String SIG_OBJ_REF_NOT_SPECIFIED =
349+
"AuthCode dictionary doesn't contain SigObjRef entry, which is required in signature mode.";
340350
public static final String STDCF_NOT_FOUND_ENCRYPTION = "/StdCF not found (encryption)";
341351
public static final String STREAM_SHALL_END_WITH_ENDSTREAM = "Stream shall end with endstream keyword.";
342352
public static final String STRUCT_PARENT_INDEX_NOT_FOUND_IN_TAGGED_OBJECT = "StructParent index not found in "
@@ -387,7 +397,6 @@ public final class KernelExceptionMessageConstant {
387397
"Unknown ASN1-encoding {0}. Only DER and BER encodings are supported!";
388398
public static final String UNSUPPORTED_FONT_EMBEDDING_STRATEGY = "Unsupported font embedding strategy.";
389399
public static final String UNSUPPORTED_XOBJECT_TYPE = "Unsupported XObject type.";
390-
public static final String VALIDATION_EXCEPTION = "Unexpected exception occurred during MAC token validation.";
391400
public static final String WHEN_ADDING_OBJECT_REFERENCE_TO_THE_TAG_TREE_IT_MUST_BE_CONNECTED_TO_NOT_FLUSHED_OBJECT =
392401
"When adding object reference to the tag tree, it must be connected to not flushed object.";
393402
public static final String WHITE_POINT_IS_INCORRECTLY_SPECIFIED = "White point is incorrectly specified.";

kernel/src/main/java/com/itextpdf/kernel/mac/MacIntegrityProtector.java renamed to kernel/src/main/java/com/itextpdf/kernel/mac/AbstractMacIntegrityProtector.java

Lines changed: 136 additions & 253 deletions
Large diffs are not rendered by default.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.itextpdf.kernel.mac;
2+
3+
import com.itextpdf.kernel.pdf.PdfDictionary;
4+
import com.itextpdf.kernel.pdf.PdfDocument;
5+
6+
/**
7+
* Strategy interface, which is responsible for {@link AbstractMacIntegrityProtector} container location.
8+
* Expected to be used in {@link com.itextpdf.commons.utils.DIContainer}.
9+
*/
10+
public interface IMacContainerLocator {
11+
/**
12+
* Locates {@link AbstractMacIntegrityProtector} container.
13+
*
14+
* @param macIntegrityProtector {@link AbstractMacIntegrityProtector} container to be located
15+
*/
16+
void locateMacContainer(AbstractMacIntegrityProtector macIntegrityProtector);
17+
18+
/**
19+
* Creates {@link AbstractMacIntegrityProtector} from explicitly provided MAC properties.
20+
*
21+
* @param document {@link PdfDocument} for which MAC container shall be created
22+
* @param macProperties {@link MacProperties} to be used for MAC container creation
23+
*
24+
* @return {@link AbstractMacIntegrityProtector} which specific implementation depends on interface implementation.
25+
*/
26+
AbstractMacIntegrityProtector createMacIntegrityProtector(PdfDocument document, MacProperties macProperties);
27+
28+
/**
29+
* Creates {@link AbstractMacIntegrityProtector} from already existing AuthCode dictionary.
30+
*
31+
* @param document {@link PdfDocument} for which MAC container shall be created
32+
* @param authDictionary AuthCode {@link PdfDictionary} which contains MAC related information
33+
*
34+
* @return {@link AbstractMacIntegrityProtector} which specific implementation depends on interface implementation.
35+
*/
36+
AbstractMacIntegrityProtector createMacIntegrityProtector(PdfDocument document, PdfDictionary authDictionary);
37+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package com.itextpdf.kernel.mac;
2+
3+
import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
4+
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
5+
import com.itextpdf.commons.bouncycastle.asn1.IASN1InputStream;
6+
import com.itextpdf.commons.bouncycastle.asn1.IASN1ObjectIdentifier;
7+
import com.itextpdf.commons.bouncycastle.asn1.IASN1OctetString;
8+
import com.itextpdf.commons.bouncycastle.asn1.IASN1Primitive;
9+
import com.itextpdf.commons.bouncycastle.asn1.IASN1Sequence;
10+
import com.itextpdf.commons.bouncycastle.asn1.IASN1Set;
11+
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
12+
import com.itextpdf.kernel.exceptions.PdfException;
13+
import com.itextpdf.kernel.pdf.PdfDictionary;
14+
import com.itextpdf.kernel.pdf.PdfName;
15+
16+
import java.io.ByteArrayInputStream;
17+
import java.io.IOException;
18+
19+
abstract class MacContainerReader {
20+
private static final IBouncyCastleFactory BC_FACTORY = BouncyCastleFactoryCreator.getFactory();
21+
22+
private final byte[] macContainer;
23+
private final long[] byteRange;
24+
private final byte[] signature;
25+
26+
MacContainerReader(PdfDictionary authDictionary) {
27+
this.macContainer = parseMacContainer(authDictionary);
28+
this.byteRange = parseByteRange(authDictionary);
29+
this.signature = parseSignature(authDictionary);
30+
}
31+
32+
static MacContainerReader getInstance(PdfDictionary authDictionary) {
33+
PdfName macLocation = authDictionary.getAsName(PdfName.MACLocation);
34+
if (PdfName.Standalone.equals(macLocation)) {
35+
return new MacStandaloneContainerReader(authDictionary);
36+
} else if (PdfName.AttachedToSig.equals(macLocation)) {
37+
return new MacSignatureContainerReader(authDictionary);
38+
}
39+
throw new PdfException(KernelExceptionMessageConstant.MAC_LOCATION_NOT_SPECIFIED);
40+
}
41+
42+
abstract byte[] parseSignature(PdfDictionary authDictionary);
43+
44+
abstract long[] parseByteRange(PdfDictionary authDictionary);
45+
46+
abstract byte[] parseMacContainer(PdfDictionary authDictionary);
47+
48+
long[] getByteRange() {
49+
return byteRange;
50+
}
51+
52+
byte[] getSignature() {
53+
return signature;
54+
}
55+
56+
byte[] parseMac() {
57+
IASN1Sequence authDataSequence = getAuthDataSequence();
58+
return BC_FACTORY.createASN1OctetString(authDataSequence.getObjectAt(6)).getOctets();
59+
}
60+
61+
IASN1Set parseAuthAttributes() {
62+
IASN1Sequence authDataSequence = getAuthDataSequence();
63+
return BC_FACTORY.createASN1Set(BC_FACTORY.createASN1TaggedObject(authDataSequence.getObjectAt(5)), false);
64+
}
65+
66+
IASN1Sequence parseMessageDigest() {
67+
IASN1Set authAttributes = parseAuthAttributes();
68+
return BC_FACTORY.createASN1Sequence(authAttributes.getObjectAt(2));
69+
}
70+
71+
byte[] parseMacKey() {
72+
IASN1Sequence authDataSequence = getAuthDataSequence();
73+
IASN1Sequence recInfo = BC_FACTORY.createASN1Sequence(BC_FACTORY.createASN1TaggedObject(
74+
BC_FACTORY.createASN1Set(authDataSequence.getObjectAt(1)).getObjectAt(0)).getObject());
75+
IASN1OctetString encryptedKey = BC_FACTORY.createASN1OctetString(recInfo.getObjectAt(3));
76+
77+
return encryptedKey.getOctets();
78+
}
79+
80+
String parseDigestAlgorithm() {
81+
IASN1Sequence authDataSequence = getAuthDataSequence();
82+
IASN1Primitive digestAlgorithmContainer =
83+
BC_FACTORY.createASN1TaggedObject(authDataSequence.getObjectAt(3)).getObject();
84+
IASN1ObjectIdentifier digestAlgorithm;
85+
if (BC_FACTORY.createASN1ObjectIdentifier(digestAlgorithmContainer) != null) {
86+
digestAlgorithm = BC_FACTORY.createASN1ObjectIdentifier(digestAlgorithmContainer);
87+
} else {
88+
digestAlgorithm = BC_FACTORY.createASN1ObjectIdentifier(
89+
BC_FACTORY.createASN1Sequence(digestAlgorithmContainer).getObjectAt(0));
90+
}
91+
92+
return digestAlgorithm.getId();
93+
}
94+
95+
private IASN1Sequence getAuthDataSequence() {
96+
IASN1Sequence contentInfoSequence;
97+
try (IASN1InputStream din =
98+
BC_FACTORY.createASN1InputStream(new ByteArrayInputStream(macContainer))) {
99+
contentInfoSequence = BC_FACTORY.createASN1Sequence(din.readObject());
100+
} catch (IOException e) {
101+
throw new PdfException(KernelExceptionMessageConstant.CONTAINER_PARSING_EXCEPTION, e);
102+
}
103+
return BC_FACTORY.createASN1Sequence(BC_FACTORY.createASN1TaggedObject(
104+
contentInfoSequence.getObjectAt(1)).getObject());
105+
}
106+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package com.itextpdf.kernel.mac;
2+
3+
import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
4+
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
5+
import com.itextpdf.commons.bouncycastle.asn1.IASN1InputStream;
6+
import com.itextpdf.commons.bouncycastle.asn1.IASN1ObjectIdentifier;
7+
import com.itextpdf.commons.bouncycastle.asn1.IASN1Sequence;
8+
import com.itextpdf.commons.bouncycastle.asn1.IASN1Set;
9+
import com.itextpdf.commons.bouncycastle.asn1.IASN1TaggedObject;
10+
import com.itextpdf.commons.bouncycastle.asn1.IDEROctetString;
11+
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
12+
import com.itextpdf.kernel.exceptions.PdfException;
13+
import com.itextpdf.kernel.pdf.PdfDictionary;
14+
import com.itextpdf.kernel.pdf.PdfName;
15+
import com.itextpdf.kernel.pdf.PdfString;
16+
17+
import java.io.ByteArrayInputStream;
18+
import java.io.IOException;
19+
20+
class MacSignatureContainerReader extends MacContainerReader {
21+
private static final IBouncyCastleFactory BC_FACTORY = BouncyCastleFactoryCreator.getFactory();
22+
private static final String ID_ATTR_PDF_MAC_DATA = "1.0.32004.1.2";
23+
24+
MacSignatureContainerReader(PdfDictionary authDictionary) {
25+
super(authDictionary);
26+
}
27+
28+
@Override
29+
byte[] parseSignature(PdfDictionary authDictionary) {
30+
PdfDictionary signatureDictionary = getSignatureDictionary(authDictionary);
31+
PdfString contentsString = signatureDictionary.getAsString(PdfName.Contents);
32+
contentsString.markAsUnencryptedObject();
33+
return parseSignatureValueFromSignatureContainer(contentsString.getValueBytes());
34+
}
35+
36+
@Override
37+
long[] parseByteRange(PdfDictionary authDictionary) {
38+
PdfDictionary signatureDictionary = getSignatureDictionary(authDictionary);
39+
return signatureDictionary.getAsArray(PdfName.ByteRange).toLongArray();
40+
}
41+
42+
@Override
43+
byte[] parseMacContainer(PdfDictionary authDictionary) {
44+
PdfDictionary signatureDictionary = getSignatureDictionary(authDictionary);
45+
PdfString contentsString = signatureDictionary.getAsString(PdfName.Contents);
46+
contentsString.markAsUnencryptedObject();
47+
return parseMacContainerFromSignatureContainer(contentsString.getValueBytes());
48+
}
49+
50+
private static byte[] parseSignatureValueFromSignatureContainer(byte[] signature) {
51+
try {
52+
IASN1Sequence signerInfoSeq = parseSignerInfoSequence(signature);
53+
54+
int signatureValueIndex = 3;
55+
IASN1TaggedObject taggedSignedAttributes =
56+
BC_FACTORY.createASN1TaggedObject(signerInfoSeq.getObjectAt(signatureValueIndex));
57+
if (taggedSignedAttributes != null) {
58+
++signatureValueIndex;
59+
}
60+
IDEROctetString signatureDataOS = BC_FACTORY.createDEROctetString(
61+
signerInfoSeq.getObjectAt(++signatureValueIndex));
62+
return signatureDataOS.getOctets();
63+
} catch (Exception e) {
64+
throw new PdfException(KernelExceptionMessageConstant.MAC_EXTRACTION_EXCEPTION, e);
65+
}
66+
}
67+
68+
private static byte[] parseMacContainerFromSignatureContainer(byte[] signature) {
69+
try {
70+
IASN1Sequence signerInfoSeq = parseSignerInfoSequence(signature);
71+
72+
int unsignedAttributesIndex = 3;
73+
IASN1TaggedObject taggedSignedAttributes =
74+
BC_FACTORY.createASN1TaggedObject(signerInfoSeq.getObjectAt(unsignedAttributesIndex));
75+
if (taggedSignedAttributes != null) {
76+
++unsignedAttributesIndex;
77+
}
78+
unsignedAttributesIndex += 2;
79+
if (signerInfoSeq.size() > unsignedAttributesIndex) {
80+
IASN1Set unsignedAttributes = BC_FACTORY.createASN1Set(BC_FACTORY.createASN1TaggedObject(
81+
signerInfoSeq.getObjectAt(unsignedAttributesIndex)), false);
82+
for (int i = 0; i < unsignedAttributes.size(); i++) {
83+
IASN1Sequence attrSeq = BC_FACTORY.createASN1Sequence(unsignedAttributes.getObjectAt(i));
84+
IASN1ObjectIdentifier attrType = BC_FACTORY.createASN1ObjectIdentifier(attrSeq.getObjectAt(0));
85+
if (ID_ATTR_PDF_MAC_DATA.equals(attrType.getId())) {
86+
IASN1Set macSet = BC_FACTORY.createASN1Set(attrSeq.getObjectAt(1));
87+
return macSet.getObjectAt(0).toASN1Primitive().getEncoded();
88+
}
89+
}
90+
}
91+
} catch (Exception e) {
92+
throw new PdfException(KernelExceptionMessageConstant.MAC_EXTRACTION_EXCEPTION, e);
93+
}
94+
throw new PdfException(KernelExceptionMessageConstant.MAC_ATTRIBUTE_NOT_SPECIFIED);
95+
}
96+
97+
private static PdfDictionary getSignatureDictionary(PdfDictionary authDictionary) {
98+
if (authDictionary.getAsDictionary(PdfName.SigObjRef) == null) {
99+
throw new PdfException(KernelExceptionMessageConstant.SIG_OBJ_REF_NOT_SPECIFIED);
100+
}
101+
return authDictionary.getAsDictionary(PdfName.SigObjRef);
102+
}
103+
104+
private static IASN1Sequence parseSignerInfoSequence(byte[] signature) throws IOException {
105+
try (IASN1InputStream is = BC_FACTORY.createASN1InputStream(new ByteArrayInputStream(signature))) {
106+
IASN1Sequence contentInfo = BC_FACTORY.createASN1Sequence(is.readObject());
107+
IASN1Sequence signedData = BC_FACTORY.createASN1Sequence(
108+
BC_FACTORY.createASN1TaggedObject(contentInfo.getObjectAt(1)).getObject());
109+
110+
int signerInfoIndex = 4;
111+
IASN1TaggedObject taggedObj = BC_FACTORY.createASN1TaggedObject(signedData.getObjectAt(signerInfoIndex));
112+
if (taggedObj != null) {
113+
++signerInfoIndex;
114+
}
115+
return BC_FACTORY.createASN1Sequence(BC_FACTORY.createASN1Set(
116+
signedData.getObjectAt(signerInfoIndex)).getObjectAt(0));
117+
}
118+
}
119+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.itextpdf.kernel.mac;
2+
3+
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
4+
import com.itextpdf.kernel.exceptions.PdfException;
5+
import com.itextpdf.kernel.pdf.PdfDictionary;
6+
import com.itextpdf.kernel.pdf.PdfName;
7+
8+
class MacStandaloneContainerReader extends MacContainerReader {
9+
MacStandaloneContainerReader(PdfDictionary authDictionary) {
10+
super(authDictionary);
11+
}
12+
13+
@Override
14+
byte[] parseSignature(PdfDictionary authDictionary) {
15+
return null;
16+
}
17+
18+
@Override
19+
long[] parseByteRange(PdfDictionary authDictionary) {
20+
return authDictionary.getAsArray(PdfName.ByteRange).toLongArray();
21+
}
22+
23+
@Override
24+
byte[] parseMacContainer(PdfDictionary authDictionary) {
25+
if (authDictionary.getAsString(PdfName.MAC) == null) {
26+
throw new PdfException(KernelExceptionMessageConstant.MAC_NOT_SPECIFIED);
27+
}
28+
return authDictionary.getAsString(PdfName.MAC).getValueBytes();
29+
}
30+
}

0 commit comments

Comments
 (0)