MacSignatureContainerReader.java
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2025 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 <https://www.gnu.org/licenses/>.
*/
package com.itextpdf.kernel.mac;
import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
import com.itextpdf.commons.bouncycastle.asn1.IASN1InputStream;
import com.itextpdf.commons.bouncycastle.asn1.IASN1ObjectIdentifier;
import com.itextpdf.commons.bouncycastle.asn1.IASN1Sequence;
import com.itextpdf.commons.bouncycastle.asn1.IASN1Set;
import com.itextpdf.commons.bouncycastle.asn1.IASN1TaggedObject;
import com.itextpdf.commons.bouncycastle.asn1.IDEROctetString;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfString;
import java.io.ByteArrayInputStream;
import java.io.IOException;
class MacSignatureContainerReader extends MacContainerReader {
private static final IBouncyCastleFactory BC_FACTORY = BouncyCastleFactoryCreator.getFactory();
private static final String ID_ATTR_PDF_MAC_DATA = "1.0.32004.1.2";
MacSignatureContainerReader(PdfDictionary authDictionary) {
super(authDictionary);
}
@Override
byte[] parseSignature(PdfDictionary authDictionary) {
PdfDictionary signatureDictionary = getSignatureDictionary(authDictionary);
PdfString contentsString = signatureDictionary.getAsString(PdfName.Contents);
contentsString.markAsUnencryptedObject();
return parseSignatureValueFromSignatureContainer(contentsString.getValueBytes());
}
@Override
long[] parseByteRange(PdfDictionary authDictionary) {
PdfDictionary signatureDictionary = getSignatureDictionary(authDictionary);
return signatureDictionary.getAsArray(PdfName.ByteRange).toLongArray();
}
@Override
byte[] parseMacContainer(PdfDictionary authDictionary) {
PdfDictionary signatureDictionary = getSignatureDictionary(authDictionary);
PdfString contentsString = signatureDictionary.getAsString(PdfName.Contents);
contentsString.markAsUnencryptedObject();
return parseMacContainerFromSignatureContainer(contentsString.getValueBytes());
}
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));
IASN1ObjectIdentifier 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) throws IOException {
try (IASN1InputStream is = BC_FACTORY.createASN1InputStream(new ByteArrayInputStream(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));
}
}
}