itextpdf 提取数字签名签章图片
因业务需求,需要对签名的pdf进行签名验证,政府的电子验证的功能是除了提取pdf表单内容外,还可以提取签章的图片,研究了好久,特此记录一下。
/** * 获取pdf签名图片信息 * @param path pdf文件 * @param fileBasePath 图片保存基础路径 * @return 提取结果 */
public static PdfSignInfo getImageFromPdf(String path, String fileBasePath) {
PdfSignInfo pdfSignInfo = new PdfSignInfo();
try {
FileInputStream fis = new FileInputStream(path);
PdfReader reader = new PdfReader(fis);
String fileName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
if (!fileBasePath.endsWith("/")) {
fileBasePath = fileBasePath + "/";
}
fileBasePath = fileBasePath + fileName + "/";
File file = new File(fileBasePath);
file.mkdirs();
pdfSignInfo.setName(path.substring(path.lastIndexOf("/") + 1));
Field rsaDataField = PdfPKCS7.class.getDeclaredField("RSAdata");
rsaDataField.setAccessible(true);
int numberOfPages = reader.getNumberOfPages();
// pdf 页码数量
log.debug("page size: {}", numberOfPages);
int xrefSize = reader.getXrefSize();
ExtImageRenderListener listener = new ExtImageRenderListener();
listener.setBasePath(fileBasePath);
// 获取acro字段
AcroFields fields = reader.getAcroFields();
Rectangle pageSize = reader.getPageSize(numberOfPages - 1);
// pdf 作用域尺寸(全屏)
log.info("pdf rectangle: {},{},{},{}", pageSize.getLeft(), pageSize.getBottom(), pageSize.getRight(), pageSize.getTop());
// 获取签名名称
ArrayList<String> signatureNames = fields.getSignatureNames();
for (String name : signatureNames) {
// 指定提供者 获取签名的pkcs7数据
PdfPKCS7 pkcs7 = fields.verifySignature(name);
X509Certificate certificate = pkcs7.getSigningCertificate();
PdfSignInfo.SignatureDetail info = new PdfSignInfo.SignatureDetail();
String[] appearanceStates = fields.getAppearanceStates(name);
// 表单域的位置
List<AcroFields.FieldPosition> fieldPositions = fields.getFieldPositions(name);
for (AcroFields.FieldPosition fieldPosition : fieldPositions) {
Rectangle position = fieldPosition.position;
log.info("field {} position: {}, {}, {}, {}, {}", name, fieldPosition.page,
position.getLeft(), position.getBottom(), position.getRight(), position.getTop());
info.page = fieldPosition.page;
info.llx = position.getLeft();
info.lly = position.getBottom();
info.urx = position.getRight();
info.ury = position.getTop();
}
AcroFields.Item fieldItem = fields.getFieldItem(name);
int size = fieldItem.size();
for (int i = 0; i < size; i++) {
PdfDictionary value = fieldItem.getValue(i);
Set<PdfName> keys = value.getKeys();
for (PdfName key : keys) {
String keyName = PdfName.decodeName(new String(key.getBytes()));
if ("Rect".equalsIgnoreCase(keyName)) {
log.debug("签名域的坐标为:{}", value.get(key));
}
}
PdfObject pdfObject = value.get(PdfName.AP);
if (pdfObject != null) {
PdfDictionary dictionary = (PdfDictionary) pdfObject;
PdfIndirectReference ref = (PdfIndirectReference) dictionary.get(PdfName.N);
int number = ref.getNumber();
log.debug("fieldName: {}, idx: {}", name, number);
// 根据ap 获取签章图片
PdfObject pdfObjectRelease = reader.getPdfObject(number);
if (pdfObjectRelease instanceof PdfStream) {
PdfStream s = (PdfStream) pdfObjectRelease;
PdfDictionary resources = s.getAsDict(PdfName.RESOURCES);
listener.setI(number);
try {
PdfContentStreamProcessor processor = new PdfContentStreamProcessor(listener);
processor.processContent(ContentByteUtils.getContentBytesFromContentObject(s), resources);
info.filePath = fileBasePath + number + ".png";
} catch (Exception ignore) { }
}
}
}
// 签名信息
info.name = name;
info.time = pkcs7.getSignDate().getTime();
info.notBefore = certificate.getNotBefore();
info.notAfter = certificate.getNotAfter();
info.dnName = certificate.getSubjectDN().getName();
info.serialNumber = certificate.getSerialNumber();
info.pubKey = Base64Encoder.encode(certificate.getPublicKey().getEncoded());
info.pubKeyFormat = certificate.getPublicKey().getFormat();
info.sigAlgName = certificate.getSigAlgName();
info.userDnName = certificate.getIssuerDN().getName();
info.reason = pkcs7.getReason();
info.location = pkcs7.getLocation();
info.res = pkcs7.verify();
pdfSignInfo.getSignatureDetails().add(info);
}
log.info(JSON.toJSONString(pdfSignInfo));
} catch (Exception e) {
e.printStackTrace();
}
return pdfSignInfo;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PdfSignInfo {
private String name;
private List<SignatureDetail> signatureDetails = new ArrayList<>();
public static class SignatureDetail {
public String name;
// 签名日期时间
public Date time;
// 有效期开始时间
public Date notBefore;
// 有效期
public Date notAfter;
// 证书名称
public String dnName;
// 序列号
public BigInteger serialNumber;
// 证书格式 // 证书颁发者
public String pubKey, pubKeyFormat, sigAlgName, userDnName;
public boolean res;
public String imageContent, imagePath;
public String reason, location;
public int page;
public float llx, lly, urx, ury;
public String filePath;
}
}
@Slf4j
@Data
public class ExtImageRenderListener implements RenderListener {
private int i;
private String basePath;
@Override
public void beginTextBlock() {
}
public void setBasePath(String basePath) {
if (basePath.endsWith("/")) {
this.basePath = basePath;
} else {
this.basePath = basePath + "/";
}
}
@Override
public void renderText(TextRenderInfo renderInfo) {
}
@Override
public void endTextBlock() {
}
PdfDictionary resources;
List<List<Integer>> numbers = new ArrayList<>();
@SneakyThrows
@Override
public void renderImage(ImageRenderInfo renderInfo) {
PdfImageObject image = renderInfo.getImage();
if (image == null) {
log.debug("Image {} could not be read", renderInfo.getRef().getNumber());
return;
}
BufferedImage bufferedImage = image.getBufferedImage();
if (bufferedImage != null) {
log.debug("bufferedImage: {}", bufferedImage.getHeight());
writeImage(bufferedImage);
}
// PRStream maskStream = (PRStream) image.getDictionary().getAsStream(PdfName.SMASK);
// if (maskStream != null) {
// PdfImageObject pdfImageObject = new PdfImageObject(maskStream);
// log.debug("mask image: {}", pdfImageObject.getFileType());
// BufferedImage mask = pdfImageObject.getBufferedImage();
// writeImage(mask);
// }
}
private void writeImage(BufferedImage bufferedImage) {
try {
File file = new File(basePath + i + ".png");
if (file.exists()) {
return;
}
log.debug("generated image: {}", file.getPath());
ImageIO.write(bufferedImage, "png", file);
} catch (IOException e) {
e.printStackTrace();
}
}
}