我尝试了下面的代码,但 pdf 仍然显示签名无效。你能检查一下代码吗,
System.out.println("Hash Signing started");
List<Certificate> certList = getFormatCertificate(strCertificate);
PrivateKey privateKey;
CMSSignedData s = null;
Security.addProvider(new BouncyCastleProvider());
byte[] signature = null;
try {
privateKey = loadPrivateKey(strPrivatekey);
byte[] buffer = new byte[1024];
int numOfBytesRead =0;
MessageDigest md = null;
md = MessageDigest.getInstance("SHA256","BC");
while((numOfBytesRead = content.read(buffer)) != -1 ){
md.update(buffer, 0, numOfBytesRead);
}
byte[] digest = md.digest();
// Separate signature container creation step
JcaCertStore certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
Attribute attr = new Attribute(CMSAttributes.messageDigest,
new DERSet(new DEROctetString(digest)));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(attr);
SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));
//AlgorithmIdentifier sha256withECDSA = new DefaultSignatureAlgorithmIdentifierFinder().find(signerAlgorithm);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(certList.get(certList.size()-1).getEncoded());
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
gen.addSignerInfoGenerator(builder.build(
new JcaContentSignerBuilder(signerAlgorithm).build(privateKey),
new JcaX509CertificateHolder(cert)));
//DErse
// gen.addSignerInfoGenerator(builder.build(
// new JcaContentSignerBuilder(sha256withRSA,
// new DefaultDigestAlgorithmIdentifierFinder().find(sha256withRSA))
// .build(PrivateKeyFactory.createKey(privateKey.getEncoded())),
// new JcaX509CertificateHolder(cert)));
gen.addCertificates(certs);
s = gen.generate(new CMSAbsentContent(), false);
System.out.println("Hash sign completed");
signature = s.getEncoded();// ConstructEcdsaSigValue(s.getEncoded());
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("GeneralSecurityException ::"+e.toString());
} catch (OperatorCreationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("OperatorCreationException ::"+e.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("IOException ::"+e.toString());
} catch (CMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("CMSException ::"+e.toString());
}finally{
return signature;
}
我已附上 pdf 文件。 Pdf file created
@Mkl/Tilman : 我创建了 2 个 pdf 文件,PDFA 使用 pdf 内容的摘要和以下代码进行签名,
System.out.println("Hash Signing started");
List<Certificate> certList = getFormatCertificate(strCertificate);
PrivateKey privateKey;
CMSSignedData s = null;
Security.addProvider(new BouncyCastleProvider());
byte[] signature = null;
try {
privateKey = loadPrivateKey(strPrivatekey);
/*byte[] buffer = new byte[1024];
int numOfBytesRead =0;
MessageDigest md = null;
//md = MessageDigest.getInstance("SHA256","BC");
md = MessageDigest.getInstance("SHA-256");
while((numOfBytesRead = content.read(buffer)) != -1 ){
md.update(buffer, 0, numOfBytesRead);
}
byte[] digest = md.digest();*/
MessageDigest md = MessageDigest.getInstance("SHA256", "BC");
byte[] digest = md.digest(IOUtils.toByteArray(content));
// Separate signature container creation step
JcaCertStore certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
Attribute attr = new Attribute(CMSAttributes.messageDigest,
new DERSet(new DEROctetString(digest)));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(attr);
SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));
//AlgorithmIdentifier sha256withECDSA = new DefaultSignatureAlgorithmIdentifierFinder().find(signerAlgorithm);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(certList.get(certList.size()-1).getEncoded());
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
gen.addSignerInfoGenerator(builder.build(
new JcaContentSignerBuilder(signerAlgorithm).build(privateKey),
new JcaX509CertificateHolder(cert)));
//DErse
// gen.addSignerInfoGenerator(builder.build(
// new JcaContentSignerBuilder(sha256withRSA,
// new DefaultDigestAlgorithmIdentifierFinder().find(sha256withRSA))
// .build(PrivateKeyFactory.createKey(privateKey.getEncoded())),
// new JcaX509CertificateHolder(cert)));
gen.addCertificates(certs);
s = gen.generate(new CMSAbsentContent(), false);
System.out.println("Hash sign completed");
signature = s.getEncoded();// ConstructEcdsaSigValue(s.getEncoded());
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("GeneralSecurityException ::"+e.toString());
} catch (OperatorCreationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("OperatorCreationException ::"+e.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("IOException ::"+e.toString());
} catch (CMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("CMSException ::"+e.toString());
}finally{
return signature;
}
PDFA
PDFB 是使用相同的私钥和证书创建的,但我没有使用摘要,而是直接使用 pdf 文档内容,这给了我有效的签名 pdf,PDFB 代码如下,
SignatureInterface signatureInterface = new SignatureInterface() {
@SuppressWarnings("rawtypes")
@Override
public byte[] sign(InputStream content) throws IOException {
try {
byte[] certificateByte = null;
Store certs = new JcaCertStore(certificates);
//PAdES - PDF Advanced Electronic Signature
//ESS - Enhanced Security Services
//ASN1 - Abstract Syntax Notation One-standard interface description language for defining data structures that can be serialized and deserialized in a cross-platform way
// Generating certificate hash
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(certificates.get(certificates.size()-1).getEncoded());
byte[] certHash = md.digest();
// Generating certificate hash ends
System.out.println("Cert hash generated");
//ESSCertIDv2 identifies the certificate from the hash
ESSCertIDv2 essCert1 =
new ESSCertIDv2(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256), certHash);
ESSCertIDv2[] essCert1Arr =
{
essCert1
};
SigningCertificateV2 scv2 = new SigningCertificateV2(essCert1Arr);
Attribute certHAttribute =
new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, new DERSet(scv2));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(certHAttribute);
AttributeTable at = new AttributeTable(v);
//Create a standard attribute table from the passed in parameters - certhash
CMSAttributeTableGenerator attrGen = new DefaultSignedAttributeTableGenerator(at){
protected Hashtable createStandardAttributeTable(Map parameters)
{
Hashtable result = super.createStandardAttributeTable(parameters);
result.remove(CMSAttributes.signingTime);
return result;
}
};
//PAdES-end
System.out.println("CMSAttributeTableGenerator generated");
SignerInfoGeneratorBuilder genBuild =
new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider());
genBuild.setSignedAttributeGenerator(attrGen);
//Get single certificate
org.spongycastle.asn1.x509.Certificate certas1 = org.spongycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(certificates.get(certificates.size()-1).getEncoded()));
// ContentSigner interface creates SHA256withECDSA signer using PvtKey
ContentSigner sha1Signer = new JcaContentSignerBuilder(signerAlgorithm).build(privateKey);
//Creates SignerInfoGenerator using X.509 cert and ContentSigner
SignerInfoGenerator sifGen = genBuild.build(sha1Signer, new X509CertificateHolder(certas1));
// CMSSignedDataGenerator generates a pkcs7-signature message
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addCertificates(certs);
gen.addSignerInfoGenerator(sifGen);
//Creates CMS message from PDF
CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
//Generate a CMS Signed Data object which can be carrying a detached CMS signature
//msg - content to be signed
CMSSignedData signedData = gen.generate(msg, false);
System.out.println("CMSSignedData is done");
return signedData.getEncoded();
} catch (GeneralSecurityException e) {
throw new IOException(e);
} catch (CMSException e) {
throw new IOException(e);
} catch (OperatorCreationException e) {
throw new IOException(e);
}
}
};
System.out.println("CMSSignedData is done2");
PDDocument pdDocument = PDDocument.load(inputfile);
System.out.println("pdDocument loaded");
pdDocument.addSignature(signature, signatureInterface);
PDFB
我认为 PDFA 的签名部分缺少一些我无法弄清楚的东西。