在您的示例文档中,术语签名适用于三重:
- 它包含根据 PDF 规范的数字签名ISO 32000-1:2008 http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf.
- 相应的可视化包含手写签名的位图图像。
- 相应的签名字典包含将所有签名数据添加到 PDF 的软件的专有信息。这些专有信息很可能包含 OP 评论中提到的生物识别数据。
根据创建这些多级签名的软件制造商的说法,手写签名似乎是主要的身份证明。数字文档仅用于保护文档不被更改;确实如此不必要反映手动签名者的身份,而不是创建该手动签名的设备的所有者的身份(“请在此处签名,表明您收到了包裹”):
功能
手写签名采集- 签名板、支付终端、iPad 或 Android 设备上的可法医识别的签名。
签名验证- 将手写签名与预先注册的个人资料进行比较。
控制签名过程中的所有步骤- 包括定位签名字段、填写表格、添加注释、添加附件等等。
保护文件的完整性- 通过使用数字签名对其进行密封。
(xyzmo英文网站首页 http://www.xyzmo.com/en/Pages/xyzmostart.aspx)
关于使用 iText 提取所有这些信息......
- 数字签名的属性可以很容易地提取和验证,正如 OP 使用 OP 的签名相关方法所观察到的那样。
AcroFields
class.
- 手写签名的位图图像也可以相当容易地提取。签名表单字段字典的外观流仅绘制作为资源附加到流的位图。
- 还可以提取包含专有信息的数据容器,因为它只是签名字典中另一个键的值。
- 但不幸的是,contents该数据容器的一部分被打包到一个调用自身的 XML 片段中加密签名数据容器。该XML片段的有效负载数据是否可以正确解密的如何解释它是 xyzmo 人自己要求的信息,我不知道他们是否认为该信息是公开的。
因此,最相关的信息是最难访问的信息。
PS关于加密生物识别有效负载的解密,我在制造商的网站上发现了以下内容:
该文档包含捕获的已加密签名(RSA 4096 + AES256)。签名板捕获个人签名后,会立即使用特殊证书的私钥对其进行加密。该特殊证书由公司使用 xyzmo 套件选择,通常存储在公司外部的安全环境中(银行保险箱、外部公证人等)。因此,xyzmo 本身无法访问该证书。对于签名的加密,xyzmo套件只需要证书的公钥。只有在解密和从文档中提取签名时才需要私钥。只有公司授予该证书访问权限的特定人员才能使用 PenAnalyst 工具(该套件作为套件的一部分提供)解密该配置文件。
(xyzmo 英文网站 数字签名捕获常见问题解答 http://www.xyzmo.com/en/resource-center/Pages/DigitalSignatureFAQ.aspx)
因此,要解密生物识别数据,您必须有权访问相应的私钥通常存储在公司外部的安全环境中(银行保险箱、外部公证人等)。如果您有这种访问权限,我们可以继续讨论这些解密数据的格式...;)
顺便说一句,如果任何人都可以简单地从签名文档中检索生物识别数据,那么它们很容易被复制到其他文档中以伪造签名。
提取手写签名的位图图像
由于人们对提取手写签名的位图图像特别感兴趣,因此这里有一个快速而肮脏的助手来提取签名的图像。正如已经说过的,我用 Java 来做,因为我更熟悉 Java:
public class XyzmoSignatureDataExtractor
{
public XyzmoSignatureDataExtractor(PdfReader reader)
{
this.reader = reader;
}
public PdfImageObject extractImage(String signatureName) throws IOException
{
MyImageRenderListener listener = new MyImageRenderListener();
PdfDictionary sigFieldDic = reader.getAcroFields().getFieldItem(signatureName).getMerged(0);
PdfDictionary appearancesDic = sigFieldDic.getAsDict(PdfName.AP);
PdfStream normalAppearance = appearancesDic.getAsStream(PdfName.N);
PdfDictionary resourcesDic = normalAppearance.getAsDict(PdfName.RESOURCES);
PdfContentStreamProcessor processor = new PdfContentStreamProcessor(listener);
processor.processContent(ContentByteUtils.getContentBytesFromContentObject(normalAppearance), resourcesDic);
return listener.image;
}
class MyImageRenderListener implements RenderListener
{
public void beginTextBlock() { }
public void endTextBlock() { }
public void renderImage(ImageRenderInfo renderInfo)
{
try
{
image = renderInfo.getImage();
}
catch (IOException e)
{
throw new RuntimeException("Failure retrieving image", e);
}
}
public void renderText(TextRenderInfo renderInfo) { }
PdfImageObject image = null;
}
final PdfReader reader;
}
你像这样使用它:
PdfReader reader = new PdfReader(resourceStream);
XyzmoSignatureDataExtractor extractor = new XyzmoSignatureDataExtractor(reader);
AcroFields acroFields = reader.getAcroFields();
for (String name: acroFields.getSignatureNames())
{
System.out.printf("\nTesting signature '%s'.\n", name);
PdfImageObject image = extractor.extractImage(name);
OutputStream os = new FileOutputStream("target/test-outputs/SampleXyzmoSignature-image-" + name + "." + image.getFileType());
os.write(image.getImageAsBytes());
os.close();
PdfDictionary imageDictionary = image.getDictionary();
PRStream maskStream = (PRStream) imageDictionary.getAsStream(PdfName.SMASK);
if (maskStream != null)
{
PdfImageObject maskImage = new PdfImageObject(maskStream);
os = new FileOutputStream("target/test-outputs/SampleXyzmoSignature-image-" + name + "-mask." + maskImage.getFileType());
os.write(maskImage.getImageAsBytes());
os.close();
}
}
Warning:班上XyzmoSignatureDataExtractor
确实是一个快速而肮脏的黑客。做了很多假设,null
-支票被遗漏了,...