I am trying to sign a pdf with DSC usb Token using itext 7 version. I searched a lot and tried different combination of codes from the internet, but result being same "Document has been altered" I know there is some problem in the hash mismatch. I don't have the certificate to use pdfpkcs7. I am trying with epass2003auto, usb token.
I am trying to sign the pdf with usb dsc token.
- I have a client, which takes base64 of hash, and then returns signed hash. Code below:
private static byte[] sign(String content, PrivateKey privateKey, Certificate[] chain, String provider) throws Exception {
byte[] hash = Base64.decodeBase64(content);
String digestAlgorithm = "SHA-256";
AlgorithmId[] digestAlgorithmIds = new AlgorithmId[]{AlgorithmId.get(digestAlgorithm)};
Signature s = Signature.getInstance("SHA256WithRSA", provider);
s.initSign(privateKey);
s.update(hash);
byte[] signature = s.sign();
ContentInfo contentInfo = new ContentInfo(ContentInfo.DATA_OID, (DerValue)null);
int index = 0;
int length = chain == null ? 0 : chain.length;
X509Certificate[] certificates = new X509Certificate[length];
X509Certificate x509;
for(x509 = null; index < length; ++index) {
X509Certificate tempCertificate = (X509Certificate)chain[index];
certificates[index] = tempCertificate;
boolean[] keyUsage = tempCertificate.getKeyUsage();
if (keyUsage != null && keyUsage[0]) {
x509 = tempCertificate;
}
}
Validate.notNull(x509, "Selected encryption certificate cannot be used for digital signing.");
BigInteger serial = x509.getSerialNumber();
String issuerName = x509.getIssuerDN().getName();
AlgorithmId dAlgId = AlgorithmId.get(digestAlgorithm);
SignerInfo si = new SignerInfo(new X500Name(issuerName), serial, dAlgId, new AlgorithmId(AlgorithmId.RSAEncryption_oid), signature);
SignerInfo[] signerInfos = new SignerInfo[]{si};
PKCS7 pkcs7 = new PKCS7(digestAlgorithmIds, contentInfo, certificates, signerInfos);
ByteArrayOutputStream bytes = new DerOutputStream();
pkcs7.encodeSignedData(bytes);
byte[] output = bytes.toByteArray();
return output;
}
- Code to create empty signature block.
public Result preparesign(Http.Request request)
{
String src = config.getString("app.temporaryDirectory") + "dummy.pdf";
String dest = config.getString("app.temporaryDirectory") + "dsc_ready_to_sign.pdf";
byte[] hashToSign = createBlankSignature(src, dest, "Sig");
// send to client.
}
private byte[] createBlankSignature(String src, String temp, String fieldName) throws IOException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(temp);
PdfSigner signer = new PdfSigner(reader, new FileOutputStream(temp), new StampingProperties().useAppendMode());
PdfSignatureAppearance appearance = signer.getSignatureAppearance();
appearance
.setLayer2FontSize(6)
.setPageRect(new Rectangle(36, 650, SIGNATURE_WIDTH, SIGNATURE_HEIGHT))
.setPageNumber(1)
.setSignatureCreator("BS");
signer.setFieldName(fieldName);
MyExternalBlankSignatureContainer external = new MyExternalBlankSignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
signer.signExternalContainer(external, 16000);
return external.getHash4Sign();
}
public class MyExternalBlankSignatureContainer implements IExternalSignatureContainer {
/* Signature dictionary. Filter and SubFilter. */
private PdfDictionary sigDic;
@Getter
private byte[] hash4Sign = null;
public MyExternalBlankSignatureContainer(PdfName filter, PdfName subFilter) {
sigDic = new PdfDictionary();
sigDic.put(PdfName.Filter, filter);
sigDic.put(PdfName.SubFilter, subFilter);
}
@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);
hash4Sign = hash;
return new byte[0];
} catch (IOException | GeneralSecurityException de) {
de.printStackTrace();
throw new GeneralSecurityException(de);
}
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.putAll(sigDic);
}
}
- Send the signed hash to client. put the signed hash to pdf.
public Result signDoc(Http.Request request)
{
String src = config.getString("app.temporaryDirectory") + "dsc_ready_to_sign.pdf";
String dest = config.getString("app.temporaryDirectory") + "dsc_signed.pdf";
try
{
Form<DSCSignResponse> dscSignResponseForm = formFactory.form(DSCSignResponse.class).bindFromRequest(request);
if(dscSignResponseForm.hasErrors())
{
return badRequest(dscSignResponseForm.errorsAsJson());
}
for(DSCSignResponse.Document document : dscSignResponseForm.get().getDocuments()) {
PdfReader reader = new PdfReader(src);
try(FileOutputStream os = new FileOutputStream(dest)) {
PdfSigner signer = new PdfSigner(reader, os, new StampingProperties().useAppendMode());
byte[] signatureBytes = Base64.getDecoder().decode(document.getEncodeBase64Sign());
IExternalSignatureContainer external = new MyExternalSignatureContainer(signatureBytes, PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
PdfSigner.signDeferred(signer.getDocument(), "Sig", os, external);
}
}
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
}
public class MyExternalSignatureContainer implements IExternalSignatureContainer {
/* Signature dictionary. Filter and SubFilter. */
private PdfDictionary sigDic;
private byte[] signedHash =null;
private Certificate[] chain = null;
public MyExternalSignatureContainer(byte[] _signedHash, PdfName filter, PdfName subFilter) {
sigDic = new PdfDictionary();
sigDic.put(PdfName.Filter, filter);
sigDic.put(PdfName.SubFilter, subFilter);
signedHash = _signedHash;
}
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
try {
return signedHash;
} catch (Exception de) {
de.printStackTrace();
throw new GeneralSecurityException(de);
}
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.putAll(sigDic);
}
}
I am getting Document has been altered error.
Can anyone let me know the issue here. Appreciate your help.