Crear firma pkcs7 a partir de resumen de archivo
Actualmente tengo una aplicación cliente-servidor que, dado un archivo PDF, la firma (con el certificado del servidor), adjunta la firma con el archivo original y devuelve la salida al cliente (todo esto se logra con PDFBox).
Tengo un controlador de Firma, que es mi Soporte de firma externa (donde el contenido es el archivo PDF)
public byte[] sign(InputStream content) throws IOException {
try {
System.out.println("Generating CMS signed data");
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("Sha1WithRSA").build(privateKey);
generator.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
.build(sha1Signer, new X509CertificateHolder(certificate.getEncoded())));
CMSTypedData cmsData = new CMSProcessableByteArray(IOUtils.toByteArray(content));
CMSSignedData signedData = generator.generate(cmsData, false);
return signedData.getEncoded();
} catch (GeneralSecurityException e) {
throw new IOException(e);
} catch (CMSException e) {
throw new IOException(e);
} catch (OperatorCreationException e) {
throw new IOException(e);
}
}
Funciona bien, pero estaba pensando: ¿qué pasa si el archivo PDF es demasiado grande para cargarlo? ex: 100mb ... ¡llevaría una eternidad! Teniendo en cuenta eso, estoy tratando de averiguar si, en lugar de firmar el archivo PDF, ¿es posible simplemente firmar el Hash (ex SHA1) de ese archivo y que el cliente lo ponga todo junto al final?
Actualizar:
He estado tratando de resolver esto, y ahora mi método de firma es:
@Override
public byte[] sign(InputStream content) throws IOException {
// testSHA1WithRSAAndAttributeTable
try {
MessageDigest md = MessageDigest.getInstance("SHA1", "BC");
List<Certificate> certList = new ArrayList<Certificate>();
CMSTypedData msg = new CMSProcessableByteArray(IOUtils.toByteArray(content));
certList.add(certificate);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
Attribute attr = new Attribute(CMSAttributes.messageDigest,
new DERSet(new DEROctetString(md.digest(IOUtils.toByteArray(content)))));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(attr);
SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));
AlgorithmIdentifier sha1withRSA = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(certificate.getEncoded());
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
gen.addSignerInfoGenerator(builder.build(
new BcRSAContentSignerBuilder(sha1withRSA,
new DefaultDigestAlgorithmIdentifierFinder().find(sha1withRSA))
.build(PrivateKeyFactory.createKey(privateKey.getEncoded())),
new JcaX509CertificateHolder(cert)));
gen.addCertificates(certs);
CMSSignedData s = gen.generate(new CMSAbsentContent(), false);
return new CMSSignedData(msg, s.getEncoded()).getEncoded();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new IOException(e);
}
}
Y estoy fusionando la firma con el PDF con pdfbox
ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(output);
byte[] cmsSignature = sign(externalSigning.getContent());
externalSigning.setSignature(cmsSignature);
El problema es que Adobe dice que la firma no es válida porque el "documento ha sido alterado o dañado desde que se firmó". ¿Alguien puede ayudar?