我正在尝试数字签名pdf文档,需要使用MSSP(移动签名服务提供商)将签名附加到签名面板。我研究了一些stackoverflow问题,做了如下工作。
首先,我创建pdf的校验和。在生成校验和之前,将空签名添加到pdf。生成校验和后,我将其作为数据发送到服务器,以便对文档进行签名。服务器给了我base64签名,我从base64签名中找到了证书链。现在我需要将签名附加到pdf,显示在Adobe reader的“签名面板”部分。
我从base64签名中提取证书链,但我不知道如何将其附加到pdf。
我的代码是:
此函数不创建pdf的空签名。
public static void emptySignature(String src, String dest, String fieldname) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, fieldname);
ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.signExternalContainer(appearance, external, 8192);
}
此函数不获取pdf的SHA-256哈希值。
public static String getHashValue(String filename) throws NoSuchAlgorithmException, IOException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
String hex = checksum("output.pdf", md);
System.out.println("CHECKSUM: " + hex);
return hex;
}
private static String checksum(String filepath, MessageDigest md) throws IOException {
try (DigestInputStream dis = new DigestInputStream(new FileInputStream(filepath), md)) {
while (dis.read() != -1) ;
md = dis.getMessageDigest();
}
StringBuilder result = new StringBuilder();
for (byte b : md.digest()) {
result.append(String.format("%02x", b));
}
return result.toString();
}
然后我把pdf的哈希发送到服务器,得到了64个基本签名值:"MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAGEFADCABgkqhkiG9w0BBwGggAQEVTVAAAAACggDCCBhwggQEoAMCAQIC...NKodC346j0GKueTJ595rhi2NbT679XZwMaMMqEyT41pimV76Nm85eW/2yYjHt08gCNVSJGP7laR8taVAAAAAAAAA="
我尝试了一些方法将签名附加到pdf的签名面板,但它需要私钥。所以请帮我提一些建议,谢谢。
更新1:
此代码是我附加签名的方式(我从链的第一个证书生成了pem文件):
final String SRC = "test.pdf";
final String DEST = "signed.pdf";
final String CERT = "cert.pem";
File initialFile = new File(CERT);
InputStream is = new FileInputStream(initialFile);
// We get the self-signed certificate from the client
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate[] chain = new Certificate[1];
chain[0] = factory.generateCertificate(is);
System.out.println("chain[0]: -----> " + chain[0]);
// we create a reader and a stamper
PdfReader reader = new PdfReader(SRC);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0');
// we create the signature appearance
PdfSignatureAppearance sap = stamper.getSignatureAppearance();
sap.setReason("test");
sap.setLocation("test");
sap.setVisibleSignature(new Rectangle(36, 748, 36, 748), 1, "signature"); //invisible
sap.setCertificate(chain[0]);
// we create the signature infrastructure
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.setReason(sap.getReason());
dic.setLocation(sap.getLocation());
dic.setContact(sap.getContact());
dic.setDate(new PdfDate(sap.getSignDate()));
sap.setCryptoDictionary(dic);
HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
exc.put(PdfName.CONTENTS, new Integer(8192 * 2 + 2));
sap.preClose(exc);
ExternalDigest externalDigest = new ExternalDigest() {
public MessageDigest getMessageDigest(String hashAlgorithm)
throws GeneralSecurityException {
return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
}
};
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, externalDigest, false);
InputStream data = sap.getRangeStream();
byte hash[] = DigestAlgorithms.digest(data, externalDigest.getMessageDigest("SHA256"));
// we get OCSP and CRL for the cert
OCSPVerifier ocspVerifier = new OCSPVerifier(null, null);
OcspClient ocspClient = new OcspClientBouncyCastle(ocspVerifier);
byte[] ocsp = null;
if (chain.length >= 2 && ocspClient != null) {
ocsp = ocspClient.getEncoded((X509Certificate) chain[0], (X509Certificate) chain[1], null);
}
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, ocsp, null, MakeSignature.CryptoStandard.CMS);
byte[] signedAttributesHash = DigestAlgorithms.digest(new ByteArrayInputStream(sh), externalDigest.getMessageDigest("SHA256"));
ByteArrayOutputStream os = baos;
byte[] signedHash = java.util.Base64.getDecoder().decode(base64Signature);
// we complete the PDF signing process
sgn.setExternalDigest(signedHash, null, "RSA");
Collection<byte[]> crlBytes = null;
TSAClientBouncyCastle tsaClient = null;
byte[] encodedSig = sgn.getEncodedPKCS7(hash, tsaClient, ocsp, crlBytes, MakeSignature.CryptoStandard.CMS);
byte[] paddedSig = new byte[8192];
System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length);
PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));
try {
sap.close(dic2);
} catch (DocumentException e) {
throw new IOException(e);
}
FileOutputStream fos = new FileOutputStream(new File(DEST));
os.writeTo(fos);
更新2:
public byte[] sign(byte[] message) throws GeneralSecurityException {
MessageDigest messageDigest = MessageDigest.getInstance(getHashAlgorithm());
byte[] messageHash = messageDigest.digest(message);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < messageHash.length; ++i) {
sb.append(Integer.toHexString((messageHash[i] & 0xFF) | 0x100).substring(1, 3));
}
byte[] signedByte = null;
String msisdn = "97688888888";
Client client = null;
try {
client = new Client( msisdn, sb.toString());
} catch (JSONException e) {
e.printStackTrace();
}
try {
String strResult = client.sendRequest();
JSONObject jsonResult = new JSONObject(strResult);
System.out.println("Response:" + jsonResult);
String base64Signature = jsonResult.getJSONObject("MSS_SignatureResp").getJSONObject("MSS_Signature").getString("Base64Signature");
System.out.println(base64Signature);
signedByte = Base64.getDecoder().decode(base64Signature);
} catch (IOException | JSONException e) {
e.printStackTrace();
}
return signedByte;
}
更新3:
问题中的代码基于多阶段方法:
ExternalBlankSignatureContainer
)
如果签名服务器可能需要很长时间才能返回签名容器,则这种方法是合适的,但在本例中,注释会澄清
服务器快速响应(仅几秒钟)
在这种用例中,应采用单步方法(就iText签名API调用而言):
PdfReader reader = new PdfReader(...);
PdfStamper stamper = PdfStamper.createSignature(reader, ..., '\0');
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "Signature");
ExternalSignatureContainer external = new RemoteSignatureContainer();
MakeSignature.signExternalContainer(appearance, external, 8192);
使用自定义的ExternalSignatureContainer
实现RemoteSignatureContainer
:
class RemoteSignatureContainer implements ExternalSignatureContainer {
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
[return a CMS signature container signing the data from the InputStream argument]
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.put(PdfName.FILTER, PdfName.ADOBE_PPKLITE);
signDic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);
}
}
我不知道您访问签名服务器的API,但基于您的UPDATE 2,我假设您的远程签名容器
中的Sign
方法如下所示:
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA256");
byte[] messageHash = messageDigest.digest(StreamUtil.inputStreamToArray(data));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < messageHash.length; ++i) {
sb.append(Integer.toHexString((messageHash[i] & 0xFF) | 0x100).substring(1, 3));
}
String msisdn = "97688888888";
try {
Client client = new Client(msisdn, sb.toString());
String strResult = client.sendRequest();
JSONObject jsonResult = new JSONObject(strResult);
String base64Signature = jsonResult
.getJSONObject("MSS_SignatureResp")
.getJSONObject("MSS_Signature")
.getString("Base64Signature");
return Base64.getDecoder().decode(base64Signature);
} catch (IOException | JSONException e) {
throw new GeneralSecurityException(e);
}
}
我在实现中适用于简单情况,但不适用于复杂情况。原始 pdf 将显示在中央窗格中。我有一个侧窗格,其中有一些矩形,如“名称”,“签名”,“时间戳”,可以在pdf上拖动。使用 j 查询可拖动 我知道图像以像素为单位,而PDF尺寸以点为单位。因此,我将以像素为单位的图像坐标转换为点(0.75)。还考虑到,对于图像,原点是在左上 角,而在pdf中,原点是左下角,对于图像,y轴是南区,但对于pdf y轴是北
对于IText5,添加数字签名相当容易。其留档的链接是:http://developers.itextpdf.com/examples/security/digital-signatures-white-paper/digital-signatures-chapter-2 有人可以在ITEXT 7中共享文档链接吗?我试过各种方法,但都没有用。在网上找不到任何链接。我可以取消签名并检查签名,但不能添
问题内容: 我想建立一个不牺牲SEO的ajax网站。我的问题是:如果我的页面上有这样的链接: …当单击每个链接时,我想用相应的标签更新地址栏。因此,如果单击“猫”链接,则当前位置为http://example.com/#cats,我可以用它来显示我的Ajax内容。如果javascript关闭或用户是搜索引擎,他们将直接转到/ cats 问题答案: 您可以更改属性,它会更改当前的锚标识符,而无需导航
出身背景 我使用iTextSharp已经有一段时间了。我已经创建了一个带有两个可签名的PdfFormFields的pdf文档。如果我打开pdf文档,我可以手动对每个字段进行手动签名。我希望通过iTextSharp完成这件事。 我目前正在从X509Store检索证书。直到现在,我都能弄明白。 问题 有人能告诉我如何使用X509Certificate2签署一个已经存在的签名字段吗。 工具书类 以下参考
我有一个密钥库文件,其中有两个密钥:一个用于调试构建,另一个用于发布构建类型。因此,我的gradle构建脚本根据需要生成两个APK。现在,要部署调试构建apk,android gradle插件中有installDebug任务,但是部署发布构建apk怎么样?Andorid gradle插件没有像installRelease这样的任务。如何使用gradle将版本构建apk直接部署到连接的设备?
我想问一个问题,如果我想在多页pdf中添加数字签名,每页都有相同的印章,我是否可以在第一页只添加一次数字签名,然后其他页面只需要引用第一个印章的外观。因为使用这种方法可以减少添加邮票的时间。 我使用了mkl给出的代码,但我有一个问题。我用其他代码替换了以下代码。 原件: 现在: 原始代码在加盖印花时有效,但修改后的代码将使印花无效。我使用Adobe Acrobat Pro DC打开已签名的文档。此