我正在集成CSC2QR热情id集成。在这里,我发送Base64 URL编码的SHA256哈希签名,作为响应,我得到了PKCS#1原始签名。我无法理解如何使用这些信息对文档进行数字签名。我正在使用itext7。
"signatures": [
"uXPiaQOEToyju50OIMrNe5gTstUQhnufmlcxmI9mG5tPCCMDYdfEV4Y+gKkixdzD\r\nZqni1+QAfy8cabRzpq13Puz31qUJ5spDRLfY/VLgBvLZLWTDK0KnJPsPWb36vMY5\r\n8CAn3DSbB02QkOoAafJkcOL3StnXc/JnAszk0lICwIM4lC3IW/pv3tWetrIn6pAJ\r\n7XBSX/zw2tfW9czFFrBaLm7hSe2NlQ1JsMgyLWEBauvFHeyFLdf9rLMM+aCoagRU\r\nD7T4Z31LrxmHFKVelS5dRvZuj8GTYJ78lfYfigSiVMsD8NEY3+YDthAsw2Lmgqs5\r\nMgVmRaQrjSdUMNeDZduFR1IeC/DLmQoBa8oXmeVqgxM0nIplq9gze1FklbPgiZ7G\r\n5zmdD8lnAP9BLawu9P+hC2GZNkeqVep3QzmoO149Iyu0jK8nrhYmxcEEqzaZiklc\r\nIKK7t03Ypst93Kps0OLc0s09A2g2wU+KzuuM+s29VKaE/gua9DKHNtf1iIZDmLtv\r\nRUoQxV9odJvRZwa+UMPsRTVejKb9pbgodiUtieyLq8Kr/NjJl+wnuH8CIiXYWxpe\r\nFoQ+J1teMOok/`sbO2X90SNqg5jvsyFTCBrGSgGWSob1TFghgWgRNiDBienXWWY09`\r\noaii058RhlJDm5l1KhMurBUZsyAre9rs74qj5tntMyQ="
]我还将获得签名证书作为响应响应-:
{
"authMode": "oauth2code",
"cert": {},
"key": {
"algo": [
"1.2.840.113549.1.1.11"
],
"len": 4096,
"status": "enabled"
},
"lang": "en-US",
"multisign": 1
}发布于 2022-03-24 15:24:11
我不知道CSC2QRAPI,更不用说已经完成了解决它的代码。因此,在这个答案中,我将显示一个框架来集成一个任意的远程签名API。
将远程签名服务与iText 7签名API集成的最简单方法是相应地实现IExternalSignature。这可能如下所示(特定于由伪代码表示的CSC2QRAPI的代码):
public class CSC2QRSignature implements IExternalSignature {
Certificate[] chain;
String hashAlgorithm = "SHA256";
String encryptionAlgorithm = "RSA";
public CSC2QRSignature([... parameters you need for CSC 2QR communication ...]) {
[... request your CSC 2QR Credentials Info and initialize ...]
[... chain, hashAlgorithm, and encryptionAlgorithm accordingly ...]
}
@Override
public String getEncryptionAlgorithm() {
return encryptionAlgorithm;
}
@Override
public String getHashAlgorithm() {
return hashAlgorithm;
}
public Certificate[] getChain() {
return chain;
}
@Override
public byte[] sign(byte[] message) throws GeneralSecurityException {
byte[] digest = MessageDigest.getInstance(hashAlgorithm).digest(message);
[... call CSC 2QR Sign Hash for the base64 encoded digest ...]
[... and return the base64 decoded signature from the response ...]
}
}使用该类,您可以签署如下PDF文件:
PdfReader reader = new PdfReader(SOURCE_PDF);
OutputStream os = new FileOutputStream(RESULT_PDF);
CSC2QRSignature signature = new CSC2QRSignature(...);
IExternalDigest digest = new BouncyCastleDigest();
PdfSigner signer = new PdfSigner(reader, os, new StampingProperties());
signer.signDetached(digest, signature, signature.getChain() , null, null, null, 0, CryptoStandard.CMS);发布于 2022-08-04 21:17:05
在注释中,很明显,您没有考虑在sign实现的IExternalSignature方法中执行所有与签名服务相关的内容(正如我在另一个答案中所建议的那样)。相反,您更愿意使用延迟签名。
在尝试延迟签名时,您创建了CMS签名容器,以最简单的形式嵌入到PDF中。但是,由于您希望创建PAdES签名,这种简单的格式是无效的,因此需要一个更复杂的CMS容器配置文件。详情见ETSI EN 319 142-1。
因此,这个答案的重点是为延迟签名用例的第一步中确定的字节范围的哈希创建一个PAdES基线兼容的CMS签名容器。在第二步中,这个签名容器可以嵌入到准备好的PDF中。
有两个主要选项,一个可以尝试提取iText为此使用的代码,另一个可以直接使用某些安全库(例如BouncyCastle )的CMS构建器代码。我们这里使用的是第一种方法。
因此,让我们对signer.signDetached进行剖析,并了解它如何为另一个答案中准备好的PDF的已签名字节范围的散列创建一个CMS签名容器。
该方法中的相关代码是
PdfPKCS7 sgn = new PdfPKCS7((PrivateKey) null, chain, hashAlgorithm, null, externalDigest, false);
if (signaturePolicy != null) {
sgn.setSignaturePolicy(signaturePolicy);
}
InputStream data = getRangeStream();
byte[] hash = DigestAlgorithms.digest(data, SignUtils.getMessageDigest(hashAlgorithm, externalDigest));
List<byte[]> ocspList = new ArrayList<>();
if (chain.length > 1 && ocspClient != null) {
for (int j = 0; j < chain.length - 1; ++j) {
byte[] ocsp = ocspClient.getEncoded((X509Certificate) chain[j], (X509Certificate) chain[j + 1], null);
if (ocsp != null) {
ocspList.add(ocsp);
}
}
}
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, sigtype, ocspList, crlBytes);
byte[] extSignature = externalSignature.sign(sh);
sgn.setExternalDigest(extSignature, null, externalSignature.getEncryptionAlgorithm());
byte[] encodedSig = sgn.getEncodedPKCS7(hash, sigtype, tsaClient, ocspList, crlBytes);对于PAdES基线签名,CRL和OCSP响应不会添加到签名容器中。对于基线-B我们甚至不需要一个TSA客户端
因此,在您的情况下,您可以这样进行:
try (
InputStream resource = ...;
ByteArrayOutputStream preparedArrayStream = new ByteArrayOutputStream()
) {
IExternalDigest externalDigest = new BouncyCastleDigest();
String digestAlgorithm = "SHA256";
// deferred signing, step one
String base64DocumentHash = prepareSignature(resource, preparedArrayStream);
// prepare PdfPKCS7 with document hash
byte[] hash = Base64.decodeBase64(base64DocumentHash);
PdfPKCS7 sgn = new PdfPKCS7((PrivateKey) null, chain, digestAlgorithm, null, externalDigest, false);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, CryptoStandard.CADES, null, null);
// retrieve signature bytes:
// * calculate hash of sh,
// * prepare ZealId signature request for hash of sh (QR code etc)
// * request signature value for hash of sh
byte[] extSignature = ... signature value for hash of sh ...;
// finalize PdfPKCS7 with signature bytes
sgn.setExternalDigest(extSignature, null, "RSA");
byte[] encodedSig = sgn.getEncodedPKCS7(hash, CryptoStandard.CADES, null, null, null);
// deferred signing, step two
try (ByteArrayInputStream preparedInputStream = new ByteArrayInputStream(preparedArrayStream.toByteArray())) {
createSignature(preparedInputStream , Base64.encodeBase64String(encodedSig));
}
}当然,您必须从您的PdfPKCS7中删除ExternalPrecalculatedSignatureContainer代码,因此它会被刻录为
class ExternalPrecalculatedSignatureContainer extends ExternalBlankSignatureContainer
{
byte[] cmsSignatureContents;
public ExternalPrecalculatedSignatureContainer(byte[] cmsSignatureContents) {
super(new PdfDictionary());
this.cmsSignatureContents = cmsSignatureContents;
}
@Override
public byte[] sign(InputStream data) throws CertificateException, InvalidKeyException, NoSuchProviderException, NoSuchAlgorithmException {
return cmsSignatureContents;
}
}发布于 2022-08-03 09:16:04
这是我的代码,在得到热诚签名后。签名是有效的,但仍然显示PDF-不是ETSI。
public static String prepareSignature(InputStream in, ByteArrayOutputStream preparedArrayStream ) throws FileNotFoundException, IOException,GeneralSecurityException {
PdfReader reader = new PdfReader(in);
Rectangle rect = new Rectangle(36, 648, 200, 100);
PdfSigner signer = new PdfSigner(reader, preparedArrayStream, new StampingProperties().useAppendMode());
PdfSignatureAppearance appearance = signer.getSignatureAppearance();
appearance.setPageRect(rect)
.setPageNumber(1)
.setLocation("EU")
.setReason("Test");
signer.setFieldName("testing");
PreSignatureContainer external = new PreSignatureContainer(PdfName.Adobe_PPKLite,
PdfName.ETSI_CAdES_DETACHED);
signer.signExternalContainer(external, 8192);
String hash = Base64.encodeBase64String(external.getHash());
return hash;
// byte[] preSignedBytes = os.toByteArray();
// outputStream.close();
//return outputStream;
}
static class PreSignatureContainer implements IExternalSignatureContainer
{
private PdfDictionary sigDic;
private byte hash[];
public PreSignatureContainer(PdfName filter, PdfName subFilter) {
sigDic = new PdfDictionary();
sigDic.put(PdfName.Filter, filter);
sigDic.put(PdfName.SubFilter, subFilter);
}
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
String hashAlgorithm = "SHA256";
BouncyCastleDigest digest = new BouncyCastleDigest();
try {
this.hash = DigestAlgorithms.digest(data, digest.getMessageDigest(hashAlgorithm));
} catch (IOException e) {
throw new GeneralSecurityException("PreSignatureContainer signing exception", e);
}
return new byte[0];
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.putAll(sigDic);
}
public byte[] getHash() {
return hash;
}
}
public static void createSignature(InputStream in, String signatures) throws Exception {
byte[] decodeSignature = Base64.decodeBase64(signatures);
PdfReader reader = null;
reader = new PdfReader(in);
FileOutputStream outputStream = new FileOutputStream(new File("D:\\zealid\\test\\newZeal.pdf"));
PdfSigner signer = new PdfSigner(reader, outputStream, new StampingProperties().useAppendMode());
signer.signDeferred(signer.getDocument(), "testing", outputStream, new ExternalPrecalculatedSignatureContainer(decodeSignature));
outputStream.close();
}
class ExternalPrecalculatedSignatureContainer extends ExternalBlankSignatureContainer
{
byte[] cmsSignatureContents;
public ExternalPrecalculatedSignatureContainer(byte[] cmsSignatureContents)
{
super(new PdfDictionary());
this.cmsSignatureContents = cmsSignatureContents;
}
@Override
public byte[] sign(InputStream data) throws CertificateException, InvalidKeyException, NoSuchProviderException, NoSuchAlgorithmException
{
BouncyCastleDigest digest = new BouncyCastleDigest();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, DigestAlgorithms.SHA256, null, digest, false);
sgn.setExternalDigest(cmsSignatureContents, null,"RSA");
cmsSignatureContents = sgn.getEncodedPKCS7();
return cmsSignatureContents;
}
}https://stackoverflow.com/questions/71597066
复制相似问题