I try to sign a pdf file but encounter "Document has been altered or corrupted since it was signed" error when I open the signed pdf file in Adobe.
The error is not so descriptive and I'm not sure where to look at because the code seems good to me but apparently it's not..
The code that I use is:
public class SafeService_INPUT_Pdf {
private static final String input = "";
private static final String tmp = "";
private static final String output = "";
private static final String token = "";
public static void main(String[] args) throws Exception {
BouncyCastleProvider providerBC = new BouncyCastleProvider();
Security.addProvider(providerBC);
CredentialsInfoResponseDto credentialsInfoResponseDto = SafeAmaHelper.getCredentialsInfo(token);
String cert0 = "-----BEGIN CERTIFICATE-----\n"+ credentialsInfoResponseDto.getCert().getCertificates().get(0) +"\n-----END CERTIFICATE-----";
String cert1 = "-----BEGIN CERTIFICATE-----\n"+ credentialsInfoResponseDto.getCert().getCertificates().get(1) +"\n-----END CERTIFICATE-----";
String cert2 = "-----BEGIN CERTIFICATE-----\n"+ credentialsInfoResponseDto.getCert().getCertificates().get(2) +"\n-----END CERTIFICATE-----";
Certificate[] chain = new Certificate[3];
try {
chain[0] = SafePdfHelper.convertStringCert(cert0);
chain[1] = SafePdfHelper.convertStringCert(cert1);
chain[2] = SafePdfHelper.convertStringCert(cert2);
}catch (Exception e){
System.out.println(e.getCause().getMessage());
}
byte[] hash4Sign = SafePdfHelper.emptySignature(input, tmp, "sig", chain);
//concatenate sha_prefix with hash4Sign and convert to BASE64
String **hashToSign** = SafePdfHelper.getHashtoSign(hash4Sign);
//CALL AMA and gets
String amaSignature = SafeAmaHelper.getAssinat(token,**hashToSign**).getSignatures().get(0);
byte[] signedFinallyHash = Base64.getDecoder().decode(String.valueOf(amaSignature.toCharArray()));
//insert HASH (AMA) to PDF temp and creates a signed PDF
SafePdfHelper.createSignature(signedFinallyHash, tmp, output, "sig", chain);
}
public class SafePdfHelper {
public static String getHashtoSign(byte[] hash4Sign) throws NoSuchAlgorithmException {
byte[] sha256SigPrefix = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
byte[] hash4SignWithPrefix = new byte[sha256SigPrefix.length + hash4Sign.length];
System.arraycopy(sha256SigPrefix, 0, hash4SignWithPrefix, 0, sha256SigPrefix.length);
System.arraycopy(hash4Sign, 0, hash4SignWithPrefix, sha256SigPrefix.length, hash4Sign.length);
return Base64.getEncoder().encodeToString(hash4SignWithPrefix);
}
public static Certificate convertStringCert(String certificate) throws Exception{
InputStream targetStream = new ByteArrayInputStream(certificate.getBytes());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(targetStream);
return cert;
}
public static byte[] emptySignature(String src, String dest, String fieldname, Certificate[] chain) throws IOException, GeneralSecurityException, IOException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfSigner signer = new PdfSigner(reader, os, new StampingProperties().useAppendMode());
signer.setFieldName(fieldname);
SafePdfHelper.MyExternalBlankSignatureContainer external = new SafePdfHelper.MyExternalBlankSignatureContainer(chain, PdfName.Adobe_PPKMS, PdfName.Adbe_pkcs7_detached);
signer.signExternalContainer(external, 12000);
byte[] hash4Sign = external.getHash4Sign();
os.close();
reader.close();
return hash4Sign;
}
static class MyExternalBlankSignatureContainer implements IExternalSignatureContainer {
/* Signature dictionary. Filter and SubFilter. */
private final PdfDictionary sigDic;
private byte[] hash4Sign = null;
private Certificate[] chain = null;
public MyExternalBlankSignatureContainer(Certificate[] _chain, PdfName filter, PdfName subFilter) {
sigDic = new PdfDictionary();
sigDic.put(PdfName.Filter, filter);
sigDic.put(PdfName.SubFilter, subFilter);
chain = _chain;
}
public byte[] getHash4Sign() {
return hash4Sign;
}
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
try {
String hashAlgorithm = DigestAlgorithms.SHA256;//"SHA-256";
BouncyCastleDigest digest = new BouncyCastleDigest();
MessageDigest md = digest.getMessageDigest(hashAlgorithm);
byte[] hash = DigestAlgorithms.digest(data, md);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, digest, false);
OcspClientBouncyCastle ocspClient = new OcspClientBouncyCastle(null);
Collection<byte[]> ocsp = new ArrayList<>();
for (var i = 0; i < chain.length - 1; i++) {
byte[] encoded = ocspClient.getEncoded((X509Certificate) chain[i], (X509Certificate) chain[i + 1], null);
if (encoded != null) ocsp.add(encoded);
}
byte[] attributeBytes = sgn.getAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, ocsp, null);
//create sha256 message digest
hash4Sign = MessageDigest.getInstance(hashAlgorithm).digest(attributeBytes);
return new byte[0];
} catch (IOException | GeneralSecurityException de) {
de.printStackTrace();
throw new GeneralSecurityException(de);
}
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.putAll(sigDic);
}
}
public static void createSignature(byte[] hashSigned, String src, String dest, String fieldName, Certificate[] chain) throws IOException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
try (FileOutputStream os = new FileOutputStream(dest)) {
PdfSigner signer = new PdfSigner(reader, os, new StampingProperties());
IExternalSignatureContainer external = new SafePdfHelper.MyExternalSignatureContainer(hashSigned, chain, PdfName.Adobe_PPKMS, PdfName.Adbe_pkcs7_detached);
// Signs a PDF where space was already reserved. The field must cover the whole document.
signer.signDeferred(signer.getDocument(), fieldName, os, external);
}
reader.close();
}
static class MyExternalSignatureContainer implements IExternalSignatureContainer {
/* Signature dictionary. Filter and SubFilter. */
private PdfDictionary sigDic;
private byte[] signedHash = null;
private Certificate[] chain = null;
public MyExternalSignatureContainer(byte[] _signedHash, Certificate[] _chain, PdfName filter, PdfName subFilter) {
sigDic = new PdfDictionary();
sigDic.put(PdfName.Filter, filter);
sigDic.put(PdfName.SubFilter, subFilter);
signedHash = _signedHash;
chain = _chain;
}
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
try {
String hashAlgorithm = DigestAlgorithms.SHA256;//"SHA-256";
BouncyCastleDigest digest = new BouncyCastleDigest();
MessageDigest md = digest.getMessageDigest(hashAlgorithm);
byte[] hash = DigestAlgorithms.digest(data, md);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, digest, false);
OcspClientBouncyCastle ocspClient = new OcspClientBouncyCastle(null);
Collection<byte[]> ocsp = new ArrayList<>();
for (var i = 0; i < chain.length - 1; i++) {
byte[] encoded = ocspClient.getEncoded((X509Certificate) chain[i], (X509Certificate) chain[i + 1], null);
if (encoded != null) ocsp.add(encoded);
}
sgn.setExternalDigest(signedHash, null, "RSA");
ITSAClient tsaClient = null;//new GSTSAClient(access);
return sgn.getEncodedPKCS7(hash, PdfSigner.CryptoStandard.CMS, tsaClient, ocsp, null);
} catch (IOException | GeneralSecurityException de) {
de.printStackTrace();
throw new GeneralSecurityException(de);
}
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.putAll(sigDic);
}
}
Base64 format of the hash that is signed is (tmp file + sha_prefix):
MDEwDQYJYIZIAWUDBAIBBQAEIKCZG8Xc6M2de3fuj8CMHLhW8XvMArW6Smy75TgABlGQ
Base64 format of the signature (AMA) is:
X5vg7qXJNsiB8hYtauih/wMFNf9uLAnT8h4M7DvHyw0bLdM03BJc7Ar1yGIoA0MTXaEdq85DP6JJFeMJZBRRc/NTA1C4IpjBN5N5Fpaa7HFnNxORQBc00d/bXuSzV1DNwCdIfcDYSUjh5Z3OWFdWzqmDhmAWRK/Hudf90m34B1mpfTtvtRAzrgn79fIBUd9D09iXpnClqTVYIzWcJ+Dz6yU75a0gvR79wNLCpUYNw2kxdmp/odAMm5cn10x9hLB+UhaNSUsnYyQUZtFsSkIE+oPXFqZc9ky4j5ha9Xfz8GGcLPEkAupyxOb5f9/NGicOeegX793swY09O4NxDW9RVtMtmdKt8kZAxB70PG1r18Ui2gheY4yuMg2aqpkcw5vgBO1GYe2DwDp99Qs4xHJjhbiUZOKT0moU+tDb3EySHZkkkci/GQTUg8IYHU8umv9TyuD7A3NRBQTyQud6j9H6bG3zQE+V4T8N2fnUPmoFEfDWyIvxvV+7YL+BymeZX4A0
Can anyone help?
