Java Itext通过客户端的64进制字符串签名对pdf进行签名
我正在尝试使用来自整个客户端的签名(格式为base 64)对pdf文档进行签名Java Itext通过客户端的64进制字符串签名对pdf进行签名,java,pdf,itext,sign,Java,Pdf,Itext,Sign,我正在尝试使用来自整个客户端的签名(格式为base 64)对pdf文档进行签名 服务请求从文档计算哈希值 我从文档的pdf中获取内容,根据算法计算哈希值 服务接受接收到的散列并对其签名,发送接收到的签名以及要签名的文档的字节 我得到一个以64为基数的字符串和要签名的pdf字节 有可能吗?我给出一个代码示例 public byte[] insertSignature(byte[] document, String signature) { try (InputStream inputStr
public byte[] insertSignature(byte[] document, String signature) {
try (InputStream inputStream = new ByteArrayInputStream(document);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ByteArrayOutputStream result = new ByteArrayOutputStream()) {
byte[] decodeSignature = Base64.decodeBase64(signature);
CAdESSignature cades = new CAdESSignature(decodeSignature, null, null);
var certificate = cades.getCAdESSignerInfo(0).getSignerCertificate();
var subject = new Subject(certificate.getSubjectX500Principal().getEncoded());
List<String> names = getSignaturesFields(document);
String sigFieldName = String.format("Signature %s", names.size() + 1);
PdfName filter = PdfName.Adobe_PPKLite;
PdfName subFilter = PdfName.ETSI_CAdES_DETACHED;
int estimatedSize = 8192;
PdfReader reader = new PdfReader(inputStream);
StampingProperties stampingProperties = new StampingProperties();
if (names.size() > 1) {
stampingProperties.useAppendMode();
}
PdfSigner signer = new PdfSigner(reader, os, stampingProperties);
signer.setCertificationLevel(PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED);
PdfSignatureAppearance appearance = signer.getSignatureAppearance();
appearance
.setContact(subject.email().orElse(""))
.setSignatureCreator(subject.organizationName().orElse(""))
.setLocation(subject.country())
.setReuseAppearance(false)
.setPageNumber(1);
signer.setFieldName(sigFieldName);
ContainerForPrepareSignedDocument external = new ContainerForPrepareSignedDocument(filter, subFilter);
signer.signExternalContainer(external, estimatedSize);
byte[] preSignedBytes = os.toByteArray();
ContainerReadyToSignedDocument extSigContainer = new ContainerReadyToSignedDocument(decodeSignature);
PdfDocument docToSign = new PdfDocument(new PdfReader(new ByteArrayInputStream(preSignedBytes)));
PdfSigner.signDeferred(docToSign, sigFieldName, result, extSigContainer);
docToSign.close();
return result.toByteArray();
}
catch (IOException e) {
throw new InternalException("IO exception by insert signature to document:", e);
}
catch (GeneralSecurityException e) {
throw new InternalException("General security by insert signature to document:", e);
}
catch (CAdESException e) {
throw new InternalException("CAdESException by insert signature to document:", e);
}
}
private List<String> getSignaturesFields(byte[] document)
throws IOException {
try (InputStream inputStream = new ByteArrayInputStream(document);
PdfReader reader = new PdfReader(inputStream);
PdfDocument pdfDocument = new PdfDocument(reader)) {
SignatureUtil signUtil = new SignatureUtil(pdfDocument);
return signUtil.getSignatureNames();
}
}
static class ContainerForPrepareSignedDocument implements IExternalSignatureContainer {
private final PdfName filter;
private final PdfName subFilter;
public ContainerForPrepareSignedDocument(PdfName filter,
PdfName subFilter) {
this.filter = filter;
this.subFilter = subFilter;
}
public byte[] sign(InputStream docBytes) {
return new byte[0];
}
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.put(PdfName.Filter, filter);
signDic.put(PdfName.SubFilter, subFilter);
}
}
static class ContainerReadyToSignedDocument implements IExternalSignatureContainer {
private byte[] cmsSignatureContents;
public ContainerReadyToSignedDocument(byte[] cmsSignatureContents) {
this.cmsSignatureContents = cmsSignatureContents;
}
public byte[] sign(InputStream docBytes) {
return cmsSignatureContents;
}
public void modifySigningDictionary(PdfDictionary signDic) {
}
}
public byte[]插入签名(byte[]文档,字符串签名){
try(InputStream-InputStream=newbytearrayinputstream(文档);
ByteArrayOutputStream os=新建ByteArrayOutputStream();
ByteArrayOutputStream结果=新建ByteArrayOutputStream()){
字节[]decodeSignature=Base64.decodesbase64(签名);
CAdESSignature cades=新的CAdESSignature(decodeSignature,null,null);
var certificate=cades.getCAdESSignerInfo(0.getSignerCertificate();
var subject=new subject(certificate.getSubjectX500Principal().getEncoded());
列表名称=GetSignatures字段(文档);
String sigFieldName=String.format(“签名%s”,names.size()+1);
PdfName filter=PdfName.Adobe\u PPKLite;
PdfName subFilter=PdfName.ETSI_CAdES_;
int estimatedSize=8192;
PdfReader reader=新PdfReader(inputStream);
StampingProperties StampingProperties=新的StampingProperties();
如果(names.size()>1){
stampingProperties.UseAmpendMode();
}
PdfSigner signer=新的PdfSigner(读卡器、操作系统、冲压属性);
签名者设置认证级别(PdfSigner.CERTIFIED不允许更改);
PdfSignatureAppearance外观=signer.getSignatureAppearance();
外观
.setContact(subject.email().orElse(“”)
.setSignatureCreator(subject.organizationName().orElse(“”)
.setLocation(subject.country())
.SetReuseApearance(false)
.设置页码(1);
signer.setFieldName(sigFieldName);
ContainerFormReparesignedDocument external=新的ContainerFormReparesignedDocument(过滤器、子过滤器);
signer.signExternalContainer(外部,估计大小);
byte[]preSignedBytes=os.toByteArray();
ContainerReadyToSignedDocument extSigContainer=新的ContainerReadyToSignedDocument(decodeSignature);
PdfDocument docToSign=新PdfDocument(新PDF阅读器(新ByteArrayInputStream(预签名字节));
signDeferred(docToSign、sigFieldName、result、extSigContainer);
docToSign.close();
返回result.toByteArray();
}
捕获(IOE异常){
抛出新的InternalException(“通过向文档插入签名引发IO异常:”,e);
}
捕获(一般安全性例外e){
抛出新的InternalException(“通过在文档中插入签名的一般安全性:”,e);
}
渔获物(e){
抛出新的InternalException(“CAdESException,通过在文档中插入签名:”,e);
}
}
私有列表getSignaturesFields(字节[]文档)
抛出IOException{
try(InputStream-InputStream=newbytearrayinputstream(文档);
PdfReader reader=新PdfReader(inputStream);
PdfDocument PdfDocument=新PdfDocument(读卡器)){
SignatureUtil signUtil=新的SignatureUtil(pdfDocument);
返回signUtil.getSignatureNames();
}
}
静态类ContainerForpRepresignedDocument实现IExternalSignatureContainer{
专用最终PdfName过滤器;
专用最终PdfName子筛选器;
public ContainerFormReparesignedDocument(PdfName筛选器,
PdfName子筛选器){
this.filter=过滤器;
this.subFilter=subFilter;
}
公共字节[]符号(InputStream docBytes){
返回新字节[0];
}
公共无效修改签名字典(PdfDictionary signDic){
signDic.put(PdfName.Filter,Filter);
signDic.put(PdfName.SubFilter,SubFilter);
}
}
静态类ContainerReadyToSignedDocument实现IExternalSignatureContainer{
专用字节[]cmsSignatureContents;
公共容器ReadyToSignedDocument(字节[]cmsSignatureContents){
this.cmsSignatureContents=cmsSignatureContents;
}
公共字节[]符号(InputStream docBytes){
返回cmsSignatureContents;
}
公共无效修改签名字典(PdfDictionary signDic){
}
}
“我从文档的pdf中获取内容,然后计算散列”-您是否对原始pdf进行散列?或者你已经准备好签字了吗?签署PDF要求您首先通过将值设置为以前存在的或新的PDF AcroForm签名字段来准备原始PDF;此值包含一个占位符,稍后将在其中嵌入签名容器。详情请阅读。在这个准备好的PDF中,除了占位符,你必须散列所有内容。你的意思是,在任何情况下,你都需要在文件中为签名创建一个容器,并从字节数组中获取散列,比如这个例子:test calchashandoccreationthendeferredsigntest01严格地说,这不是一个例子,但是这些是一些单元测试,它们测试延迟签名的某些方面,但是是的,在开始散列之前,您必须以类似于某些测试中显示的方式准备PDF。我是否正确理解,在这种情况下,每次散列都是不同的。例如,我们取一个文件,在其中添加一个用于签名的容器,然后从/ByteRange计算哈希值,结果,我得到哈希值1,如果我第二次执行相同的操作,那么即使使用相同的算法,也会有一个不同的哈希值?通常是的,因为其中有创建/修改时间、签名时间和唯一文档ID等条目