我正在尝试使用通过使用aws kms对pdfdocument的sha256摘要签名获得的签名来对pdf本身应用签名。我甚至不确定我的方向是否正确。
所有操作都正常运行,但生成的文件的签名会引发错误:
Error during signature verification. ASN.1 parsing error: Error encountered while BER decoding:
如果这很重要,我可以从aws检索公钥,但是私钥保留在他们这边。我在网上看到的大多数文档都假定您可以访问私钥。此外,我不确定如何或从何处获取证书链,因为aws处理签名。我找到的所有文档都需要这个证书链。
代码
首先,我按照大多数文档的指示创建一个空的签名字段。我想这可能有点问题 PdfName.Adbe_pkcs7_detached
但如果这是错误的,我不知道还能用什么来代替它。
public void addEmptySignatureField(File src, File destination, String fieldName) throws IOException, GeneralSecurityException {
try (
var reader = new PdfReader(src);
var output = new FileOutputStream(destination)
) {
var signer = new PdfSigner(reader, output, new StampingProperties());
signer.getSignatureAppearance()
.setPageRect(new Rectangle(36, 748, 200, 100))
.setPageNumber(1)
.setLocation("whee")
.setSignatureCreator("Mario")
.setReason("because")
.setLayer2FontSize(14f);
signer.setFieldName(fieldName);
IExternalSignatureContainer blankSignatureContainer = new ExternalBlankSignatureContainer(PdfName.Adobe_PPKLite,
PdfName.Adbe_pkcs7_detached);
// Sign the document using an blankSignatureContainer container.
// 8192 is the size of the empty signature placeholder.
signer.signExternalContainer(blankSignatureContainer, 8192);
}
}
然后我试图在文件上签字:
public void completeSignature(File src, File destination, String fieldName) throws IOException, GeneralSecurityException {
try (
var reader = new PdfReader(src);
var pdfDocument = new PdfDocument(reader);
var writer = new PdfWriter(destination)
) {
// Signs a PDF where space was already reserved. The field must cover the whole document.
PdfSigner.signDeferred(pdfDocument, fieldName, writer, kmsBackedSignatureContainer);
}
}
作为参考,kmsbackedsignaturecontainer如下。 fileSigner.sign
aws kms a返回 byte[]
如其文件中所定义:
该值是由ans x9.62–2005和rfc 3279第2.2.3节定义的der编码对象。
public class KmsBackedSignatureContainer implements IExternalSignatureContainer
{
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
try {
var bytes = DigestAlgorithms.digest(data, new BouncyCastleDigest().getMessageDigest(DigestAlgorithms.SHA256));
var derEncodedBytes = fileSigner.sign(bytes);
return derEncodedBytes;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void modifySigningDictionary(PdfDictionary signDic)
{
}
}
1条答案
按热度按时间wnavrhmk1#
在本回答的上下文中,假设您已将凭据存储在
default
您的~/.aws/credentials
文件和您所在的地区default
您的~/.aws/config
文件。否则你就得适应环境KmsClient
在下面的代码中示例化或初始化。为aws kms密钥对生成证书
首先,aws-kms使用普通的非对称密钥对签名,它不提供x.509证书作为公钥。但是,可互操作的pdf签名需要公钥的x.509证书来建立对签名的信任。因此,互操作aws kms pdf签名的第一步是为aws kms签名密钥对的公钥生成x.509证书。
出于测试目的,您可以使用此助手方法创建自签名证书,该方法基于此堆栈溢出答案中的代码:
(certificateutils辅助方法)
这个
AwsKmsContentSigner
上面代码中使用的类是bouncycastle接口的这个实现ContentSigner
:(awskmscontentsigner)
出于生产目的,您通常希望使用由受信任的ca签名的证书。与上述类似,您可以为您的aws kms公钥创建并签名证书请求,将其发送到您选择的ca,然后从他们那里取回要使用的证书。
使用aws-kms密钥对对pdf进行签名
要用itext签署pdf,您需要itext的实现
IExternalSignature
或者IExternalSignatureContainer
接口。这里我们使用前者:(AWSKM签名)
在构造函数中,我们选择一个签名算法,该算法可用于所讨论的密钥。这实际上是在这里非常随意地完成的,而不是简单地采用您可能想要强制使用特定哈希算法的第一个算法。
getHashAlgorithm
以及getEncryptionAlgorithm
返回签名算法和sign
只是创建一个签名。付诸行动
假设您的aws-kms签名密钥对具有别名
SigningExamples-ECC_NIST_P256
您可以像这样使用上面的代码来签署pdf:(测试)简单测试
testSignSimpleEcdsa
)使用aws-kms密钥对签名pdf
上面我们使用了
IExternalSignature
签字。虽然这是最简单的方法,但它也有一些缺点:类PdfPKCS7
本例中使用的不支持rsassa pss用法,对于ecdsa签名,它使用错误的oid作为签名算法oid。为了不受这些问题的影响,我们在这里使用
IExternalSignatureContainer
相反,我们只使用bouncycastle功能自己构建完整的cms签名容器。(AWSKMS签名容器)
在构造函数中,我们还选择了一个签名算法,该算法可用于所讨论的密钥。不过,这里我们允许一个函数参数,允许调用者在可用的签名算法中进行选择。这对于rsassa pss的使用尤其必要。
重新审视将其付诸行动
假设您有一个aws kms签名rsa\U 2048密钥对,该密钥对具有别名signingexamples-rsa\U 2048,您可以像这样使用上面的代码使用rsassa pss对pdf进行签名:
(测试)简单测试
testSignSimpleRsaSsaPss
)使用此选择器功能
(testsignsimple helper方法)
大规模签名考虑事项
如果您计划使用aws kms进行批量签名,请注意aws kms为其某些操作建立的请求配额:
配额名称默认值(每秒)加密操作(rsa)请求速率500(共享)rsa cmkscryptographic operations(ecc)请求速率300(共享)椭圆曲线(ecc)cmksgetpublickey请求速率5
(摘自《aws密钥管理服务开发人员指南》/“配额”/“请求配额”/“每个aws kms api操作的请求配额”,查阅日期:2020-12-15)
rsa和ecc加密操作的请求速率可能不是问题。或者更重要的是,如果他们是一个问题,aws kms最有可能是不适合您的需要签署产品;相反,您应该寻找实际的hsm,无论是物理的还是作为服务的,例如aws cloudhsm。
另一方面,getpublickey请求速率很可能是个问题:两者都是
AwsKmsSignature
以及AwsKmsSignatureContainer
在各自的构造函数中调用该方法。因此,基于它们的简单批量签名代码将限制为每秒5个签名。根据您的用例,有不同的策略来解决这个问题。
如果只有极少数的签名代码示例同时运行,并且它们只使用极少数不同的密钥,那么只需重新使用
AwsKmsSignature
以及AwsKmsSignatureContainer
对象,在启动或按需创建它们,然后缓存它们。否则,您应该重构getpublickey方法的使用
AwsKmsSignature
以及AwsKmsSignatureContainer
施工人员。它仅用于确定在使用相关密钥签名时要使用的aws kms签名算法标识符。显然,您可以将该标识符与密钥标识符一起存储,这样就不需要getpublickey调用。