iText - Encrypt Embedded Files Only - pdf

I want to create an unencrypted pdf file with an encrypted embedded file with iText as described in Section 7.6.1 of PDF 32000-1:2008:
Beginning with PDF 1.5, embedded files can be encrypted in an
otherwise unencrypted document
The following example (iText 7.0.1), however, produces a PDF file with an unencrypted embedded file stream (compression switched off to better analyze the resulting PDF file):
/* cf. 7.6.3.1: Documents in which only file attachments are
encrypted shall use the same password as the user and owner password.*/
PdfWriter writer = new PdfWriter(fileName, new WriterProperties()
.setStandardEncryption("secret".getBytes(),
"secret".getBytes(), EncryptionConstants.ALLOW_PRINTING |
EncryptionConstants.ALLOW_MODIFY_ANNOTATIONS,
EncryptionConstants.ENCRYPTION_AES_128 |
EncryptionConstants.DO_NOT_ENCRYPT_METADATA |
EncryptionConstants.EMBEDDED_FILES_ONLY)
.setCompressionLevel(CompressionConstants.NO_COMPRESSION));
PdfDocument pdf = new PdfDocument(writer);
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(pdf,"attached file".getBytes(),
null,"attachment.txt",null,null,null,true);
pdf.addFileAttachment("attachment.txt", fs);
try (Document doc = new Document(pdf)) {
doc.add(new Paragraph("main file"));
}
This result appears to be in contrast to the spec stating:
if the contents of the stream are embedded within the PDF file (see
7.11.4, "Embedded File Streams"), they shall be encrypted like any other stream in the file
The pdf file produced by the above example contains the correct entry for the encrypted embedded file stream in the CF dictionary:
<</CF<</StdCF<</AuthEvent/EFOpen/CFM/AESV2/Length 16>>>>/EFF/StdCF
Table 20 of the spec states:
Conforming writers shall respect this value when encrypting embedded
files, except for embedded file streams that have their own crypt
filter specifier.
The stream in our case has no own CF specifier and hence should be encrytped using AESV2. In our example, however, the stream is not encrypted:
4 0 obj
<</Length 13/Params<</ModDate(D:20160930101501+02'00')/Size 13>>/Subtype /application#2foctet-stream/Type/EmbeddedFile>>stream
attached file
endstream
endobj
This leads to the following questions:
Is this a bug in iText or did I misinterpret the PDF spec?
How can I create unencrypted pdf files with encrypted embedded files
with iText?
If this is not (yet) possible, is there any other free library or command
line tool to do this?
PS: Acrobat Reader DC and PDF-XChange Viewer 2.5 ask for a password to open the attachment, whereas (non-conforming) readers like evince open the attachment without any questions. But this is not my question here. My question is not about reader behavior and possibly ethics, but about the pdf file itself and its compliance with the spec.

Update 2021:
Release 7.1.16 finally implemented encryption of embedded files in otherwise not encrypted pdf documents.
(the API changed slightly: in the test for iText 7 below, remove the last parameter of createEmbeddedFileSpec so that it reads PdfFileSpec.createEmbeddedFileSpec(pdf,"attached file".getBytes(),null,"attachment.txt",null,null,null);)
Original answer
As I didn't get any answers I made some more tests with iText 5.5.9 and iText 7.0.1 and came to the conclusion that not to encrypt embedded file streams with EMBEDDED_FILES_ONLY is a bug in the new version of iText 7. It only worked with iText 5 and ENCRYPTION_AES_256, although Acrobat reader gave a warning that an error existed on this page and it might not display the page correctly. For details see the following table:
Following is the code of the minimal, complete, and verifiable examples to produce the pdf files used in the above table with iText 5.5.9 ...
package pdfencryptef_itext5;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfFileSpecification;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class PDFEncryptEF_iText5 {
public static void main(String[] args) throws Exception {
new PDFEncryptEF_iText5().createPDF("iText5_STD128.pdf", PdfWriter.STANDARD_ENCRYPTION_128);
new PDFEncryptEF_iText5().createPDF("iText5_AES128.pdf", PdfWriter.ENCRYPTION_AES_128);
new PDFEncryptEF_iText5().createPDF("iText5_AES256.pdf", PdfWriter.ENCRYPTION_AES_256);
Security.addProvider(new BouncyCastleProvider());
new PDFEncryptEF_iText5().createPDF("iText5_AES128C.pdf", -PdfWriter.ENCRYPTION_AES_128);
new PDFEncryptEF_iText5().createPDF("iText5_AES256C.pdf", -PdfWriter.ENCRYPTION_AES_256);
}
public void createPDF(String fileName, int encryption ) throws FileNotFoundException, DocumentException, IOException, CertificateException {
Document document = new Document();
Document.compress = false;
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(fileName));
if( encryption >= 0 ){
writer.setEncryption("secret".getBytes(),"secret".getBytes(), 0,
encryption | PdfWriter.EMBEDDED_FILES_ONLY);
} else {
Certificate cert = getPublicCertificate("MyCert.cer" );
writer.setEncryption( new Certificate[] {cert}, new int[] {0}, -encryption | PdfWriter.EMBEDDED_FILES_ONLY);
}
writer.setPdfVersion(PdfWriter.VERSION_1_6);
document.open();
PdfFileSpecification fs = PdfFileSpecification.fileEmbedded(writer, null, "attachment.txt", "attached file".getBytes(), 0);
writer.addFileAttachment( fs );
document.add(new Paragraph("main file"));
document.close();
}
public Certificate getPublicCertificate(String path) throws IOException, CertificateException {
FileInputStream is = new FileInputStream(path);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
return cert;
}
}
... and iText 7.0.1:
package pdfencryptef_itext7;
import com.itextpdf.kernel.pdf.CompressionConstants;
import com.itextpdf.kernel.pdf.EncryptionConstants;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfVersion;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.WriterProperties;
import com.itextpdf.kernel.pdf.filespec.PdfFileSpec;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class PDFEncryptEF_iText7 {
public static void main(String[] args) throws Exception {
new PDFEncryptEF_iText7().createPDF("iText7_STD128.pdf", EncryptionConstants.STANDARD_ENCRYPTION_128);
new PDFEncryptEF_iText7().createPDF("iText7_AES128.pdf", EncryptionConstants.ENCRYPTION_AES_128);
new PDFEncryptEF_iText7().createPDF("iText7_AES256.pdf", EncryptionConstants.ENCRYPTION_AES_256);
Security.addProvider(new BouncyCastleProvider());
new PDFEncryptEF_iText7().createPDF("iText7_AES128C.pdf", -EncryptionConstants.ENCRYPTION_AES_128);
new PDFEncryptEF_iText7().createPDF("iText7_AES256C.pdf", -EncryptionConstants.ENCRYPTION_AES_256);
}
public void createPDF(String fileName, int encryption ) throws FileNotFoundException, IOException, CertificateException{
PdfWriter writer;
if( encryption >= 0 ){
writer = new PdfWriter(fileName, new WriterProperties().setStandardEncryption("secret".getBytes(),"secret".getBytes(),
0,
encryption | EncryptionConstants.EMBEDDED_FILES_ONLY)
.setPdfVersion(PdfVersion.PDF_1_6));
} else {
Certificate cert = getPublicCertificate("MyCert.cer" );
writer = new PdfWriter(fileName, new WriterProperties().setPublicKeyEncryption( new Certificate[] {cert},
new int[] {0},
-encryption | EncryptionConstants.EMBEDDED_FILES_ONLY )
.setPdfVersion(PdfVersion.PDF_1_6));
}
writer.setCompressionLevel(CompressionConstants.NO_COMPRESSION);
PdfDocument pdf = new PdfDocument(writer);
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(pdf,"attached file".getBytes(),null,"attachment.txt",null,null,null,true);
pdf.addFileAttachment("attachment.txt", fs);
try (Document doc = new Document(pdf)) {
doc.add(new Paragraph("main file"));
}
}
public Certificate getPublicCertificate(String path) throws IOException, CertificateException {
FileInputStream is = new FileInputStream(path);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
return cert;
}
}
I must admit that I'm a bit disappointed that there was no feedback from the iText people to at least the first of my three questions but, hopefully, future versions of iText 7 will correctly process the EMBEDDED_FILES_ONLY flag. As the tests showed, it seems to be far from trivial for both the pdf producer as well as the reader to correctly handle this feature.

Related

In Itext 7, how to sign a pdf with 2 steps?

Following the answers given in this previous question : In Itext 7, how to get the range stream to sign a pdf?, i've tried to reimplement the two steps signing method working in Itext 5 but i encounter an issue when trying to reopen the document result of the first step (with the PdfReader or a pdf reader).(invalid document)
Here is the presigning part for a document already containing an empty signature field named certification ... why is the result of this step invalid ?
PdfReader reader = new PdfReader(fis);
Path signfile = Files.createTempFile("sign", ".pdf");
FileOutputStream os = new FileOutputStream(signfile.toFile());
PdfSigner signer = new PdfSigner(reader, os, false);
signer.setFieldName("certification"); // this field already exists
signer.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING);
PdfSignatureAppearance sap = signer.getSignatureAppearance();
sap.setReason("Certification of the document");
sap.setLocation("On server");
sap.setCertificate(maincertificate);
BouncyCastleDigest digest = new BouncyCastleDigest();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, digest,false);
//IExternalSignatureContainer like BlankContainer
PreSignatureContainer external = new PreSignatureContainer(PdfName.Adobe_PPKLite,PdfName.Adbe_pkcs7_detached);
signer.signExternalContainer(external, 8192);
byte[] hash=external.getHash();
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null,PdfSigner.CryptoStandard.CMS);// sh will be sent for signature
And here is the PreSignatureContainer class :
public class PreSignatureContainer implements IExternalSignatureContainer {
private PdfDictionary sigDic;
private byte hash[];
public PreSignatureContainer(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 {
String hashAlgorithm = "SHA256";
BouncyCastleDigest digest = new BouncyCastleDigest();
try {
this.hash= DigestAlgorithms.digest(data, digest.getMessageDigest(hashAlgorithm));
} catch (IOException e) {
throw new GeneralSecurityException("PreSignatureContainer signing exception",e);
}
return new byte[0];
}
#Override
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.putAll(sigDic);
}
public byte[] getHash() {
return hash;
}
public void setHash(byte hash[]) {
this.hash = hash;
}
}
why is the result of this step invalid
Because you essentially discovered a bug... ;)
Your sample input file has one feature which triggers the bug: It is compressed using object streams.
When iText manipulates such a file, it also tries to put as many objects as possible into object streams. Unfortunately it also does so with the signature dictionary. This is unfortunate because after writing the whole file it tries to enter some information (which are not available before) into this dictionary which damages the compressed object stream.
What you can do...
You can either
wait for iText development to fix this issue - I assume this won't take too long but probably you don't have the time to wait; or
convert the file to sign into a form which does not use object streams - this can be done using iText itself but probably you cannot accept the file growth this means, or probably the files already are signed which forbids any such transformation; or
patch iText 7 to force the signature dictionary not to be added to an object stream - it is a trivial patch but you probably don't want to used patched libraries.
The patch mentioned above indeed is trivial, the method PdfSigner.preClose(Map<PdfName, Integer>) contains this code:
if (certificationLevel > 0) {
// add DocMDP entry to root
PdfDictionary docmdp = new PdfDictionary();
docmdp.put(PdfName.DocMDP, cryptoDictionary.getPdfObject());
document.getCatalog().put(PdfName.Perms, docmdp); // TODO: setModified?
}
document.close();
The cryptoDictionary.getPdfObject()) is the signature dictionary I mentioned above. During document.close() it is added to an object stream unless it has been written to the output before. Thus, you simply have to add a call to flush that object right before that close call and by parameter make clear that it shall not be added to an object stream:
cryptoDictionary.getPdfObject().flush(false);
With that patch in place, the PDFs your code returns are not damaged as above anymore.
As an aside, iText 5 does contain a similar line in the corresponding PdfSignatureAppearance.preClose(HashMap<PdfName, Integer>) right above the if block corresponding to the if block above. It seems to have been lost during refactoring to iText 7.

Import annotations (XFDF) to PDF

I have created a sample program to try to import XFDF to PDF using the Aspose library. The program can be run without exception, but the output PDF does not include any annotations. Any suggestions to solve this problem?
Update - 2014-12-12
I have also sent the issue to Aspose. They can reproduce the same problem and logged a ticket PDFNEWJAVA-34609 in their issue tracking system.
Following is my sample program:
public static void main(String[] args) {
final String ROOT = "C:\\PdfAnnotation\\";
final String sourcePDF = "hackermonthly-issue.pdf";
final String destPDF = "output.pdf";
final String sourceXFDF = "XFDFTest.xfdf";
try
{
// Specify the path of license file
License lic = new License();
lic.setLicense(ROOT + "Aspose.Pdf.lic");
//create an object of PdfAnnotationEditor class
PdfAnnotationEditor editor = new PdfAnnotationEditor();
//bind input PDF file
editor.bindPdf(ROOT + sourcePDF);
//create a file stream for input XFDF file to import annotations
FileInputStream fileStream = new FileInputStream(ROOT + sourceXFDF);
//create an enumeration of all the annotation types which you want to import
//int[] annType = {AnnotationType.Ink };
//import annotations of specified type(s) from XFDF file
//editor.importAnnotationFromXfdf(fileStream, annType);
editor.importAnnotationFromXfdf(fileStream);
//save output pdf file
editor.save(ROOT + destPDF);
} catch (Exception e) {
System.out.println("exception: " + e.getMessage());
}
}

Issues with iTextsharp and pdf manipulation

I am getting a pdf-document (no password) which is generated from a third party software with javascript and a few editable fields in it. If I load this pdf-document with the pdfReader class the NumberOfPagesProperty is always 1 although the pdf-document has 17 pages. Oddly enough the document has 17 pages if I save the stream afterwards. When I now try to open the document the Acrobat Reader shows an extended feature warning and the fields are not fillable anymore (I haven't flattened the document). Do anyone know about such a problem?
Background Info:
My job is to remove the javascript code, fill out some fields and save the document afterwards.
I am using the iTextsharp version 5.5.3.0.
Unfortunately I can't upload a sample file because there are some confidental data in it.
private byte[] GetDocumentData(string documentName)
{
var document = String.Format("{0}{1}\\{2}.pdf", _component.OutputDirectory, _component.OutputFileName.Replace(".xml", ".pdf"), documentName);
if (File.Exists(document))
{
PdfReader.unethicalreading = true;
using (var originalData = new MemoryStream(File.ReadAllBytes(document)))
{
using (var updatedData = new MemoryStream())
{
var pdfTool = new PdfInserter(originalData, updatedData) {FormFlattening = false};
pdfTool.RemoveJavascript();
pdfTool.Save();
return updatedData.ToArray();
}
}
}
return null;
}
//Old version that wasn't working
public PdfInserter(Stream pdfInputStream, Stream pdfOutputStream)
{
_pdfInputStream = pdfInputStream;
_pdfOutputStream = pdfOutputStream;
_pdfReader = new PdfReader(_pdfInputStream);
_pdfStamper = new PdfStamper(_pdfReader, _pdfOutputStream);
}
//Solution
public PdfInserter(Stream pdfInputStream, Stream pdfOutputStream, char pdfVersion = '\0', bool append = true)
{
_pdfInputStream = pdfInputStream;
_pdfOutputStream = pdfOutputStream;
_pdfReader = new PdfReader(_pdfInputStream);
_pdfStamper = new PdfStamper(_pdfReader, _pdfOutputStream, pdfVersion, append);
}
public void RemoveJavascript()
{
for (int i = 0; i <= _pdfReader.XrefSize; i++)
{
PdfDictionary dictionary = _pdfReader.GetPdfObject(i) as PdfDictionary;
if (dictionary != null)
{
dictionary.Remove(PdfName.AA);
dictionary.Remove(PdfName.JS);
dictionary.Remove(PdfName.JAVASCRIPT);
}
}
}
The extended feature warning is a hint that the original PDF had been signed using a usage rights signature to "Reader-enable" it, i.e. to tell the Adobe Reader to activate some additional features when opening it, and the OP's operation on it has invalidated the signature.
Indeed, he operated using
_pdfStamper = new PdfStamper(_pdfReader, _pdfOutputStream);
which creates a PdfStamper which completely re-generates the document. To not invalidate the signature, though, one has to use append mode as in the OP's fixed code (for char pdfVersion = '\0', bool append = true):
_pdfStamper = new PdfStamper(_pdfReader, _pdfOutputStream, pdfVersion, append);
If I load this pdf-document with the pdfReader class the NumberOfPagesProperty is always 1 although the pdf-document has 17 pages. Oddly enough the document has 17 pages
Quite likely it is a PDF with a XFA form, i.e. the PDF is only a carrier of some XFA data from which Adobe Reader builds those 17 pages. The actual PDF in that case usually only contains one page saying something like "if you see this, your viewer does not support XFA."
For a final verdict, though, one has to inspect the PDF.

Signing a PDF with an eID using PKCS#11 and iText

After following the "Signing a document using a smart card and PKCS#11" topic in http://itextpdf.com/book/digitalsignatures and creating a code sample similar to the provided one, the signed file signature is invalid in Adobe Reader, the signature appearance has the name of the non-repudiation certificate (i.e., the name of the eID owner) but in Adobe Reader's Signature Panel shows:
The error occured while validating:
I'm using a Gemalto PinPad and the Portuguese eID pteidpkcs11.dll installed with the eID middleware software, located in C:\Windows\System32.
I've tried:
Null checking
Manually creating the Certificate chain, as the Certificate[] returned by ks.getCertificateChain("CITIZEN SIGNATURE CERTIFICATE"); only has the signature certificate
As an alternative you can sign with the portuguese eid card (Cartão de Cidadão) using only a java component available on www.poreid.org. It is also available on maven central repository with the artifactid poreid
Here is an example based on the sample provided in the itext documentation
public void createPdf(String filename) throws IOException, DocumentException {
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(filename));
document.open();
document.add(new Paragraph("Assinado com o Cartão de Cidadão!"));
document.close();
}
public void signPdf(String src, String dest)
throws IOException, DocumentException, GeneralSecurityException {
KeyStore ks = KeyStore.getInstance(POReIDConfig.POREID);
ks.load(null);
PrivateKey pk = (PrivateKey) ks.getKey(POReIDConfig.AUTENTICACAO, null);
Certificate[] chain = ks.getCertificateChain(POReIDConfig.AUTENTICACAO);
// reader and stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
// appearance
PdfSignatureAppearance appearance = stamper .getSignatureAppearance();
appearance.setReason("qualquer motivo");
appearance.setLocation("qualquer localização");
appearance.setVisibleSignature(new Rectangle(72, 732, 144, 780), 1, "primeira assinatura");
// digital signature
ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", POReIDConfig.POREID);
ExternalDigest digest = new ProviderDigest(null); // find provider
MakeSignature.signDetached(appearance, digest, es, chain, null, null, null, 0, CryptoStandard.CMS);
}
public static void main(String[] args) throws DocumentException, IOException, GeneralSecurityException {
Security.addProvider(new POReIDProvider());
App exemplo = new App();
exemplo.createPdf("/home/quim/exemplo.pdf");
exemplo.signPdf("/home/quim/exemplo.pdf","/home/quim/exemplo.assinado.pdf");
}
The provided code sample tries to get the PrivateKey of the signature certificate, I found it odd but figured it was just used as a reference. Navigating through the stack trace of the exception that is triggered when the user cancels the process in the PinPad gave me the following idea, which, fortunately, solved this:
Create a custom com.itextpdf.text.pdf.security.ExternalSignature implementation
Implement an utility class that, using the sun.security.pkcs11.wrapper.PKCS11 wrapper, interacts with your eID pkcs11 dll (in my case, pteidpkcs11.dll) and provides a signing method that receives a byte[] message which is then sent to the SmartCard reader to be signed, and returns the byte[] result of this operation
Use the utility class in your CustomExternalSignature.sign(...)
Some tips that you can use if you're developing for the Portuguese eID Cartão Cidadão:
For the second item of the previous list, I'm using the PTeID4JPKCS11 class from an opensource project named pteid4j created by André Barbosa, you just need to call PTeID4JPKCS11.getInstance().sign(...);
Regarding the Hash and Encryption algorithm required by the ExternalSignature interface, the hash is SHA-1 and the Encryption RSA
I've been working on digital signature for PDF documents using Portuguese citizen card, and here is what I have:
public void signCAdES(...) {
String pkcs11Config = "name=GemPC" + "\n" + "library=C:\\WINDOWS\\SysWOW64\\pteidpkcs11.dll";
ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11Config.getBytes());
Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
//provider_name: SunPKCS11-GemPC
Security.addProvider(pkcs11Provider);
javax.security.auth.callback.CallbackHandler cmdLineHdlr = new DialogCallbackHandler();
KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS11", pkcs11Provider,
new KeyStore.CallbackHandlerProtection(cmdLineHdlr));
KeyStore ks= builder.getKeyStore();
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', new File(tempPath), true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setCertificationLevel(level);
String alias = "CITIZEN SIGNATURE CERTIFICATE";
//certificates from electronic card and resources folder
Certificate[] certs = getSignatureCertificatesChain(ks);
PrivateKey pk = (PrivateKey) ks.getKey(alias, null);
ExternalSignature es = new PrivateKeySignature(pk, "SHA-1", pkcs11Provider.getName());
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, es, certs, null, null, null, 0, MakeSignature.CryptoStandard.CADES);
}
I have to build the certificates chain (getSignatureCertificatesChain(ks)) too since ks.getCertificateChain("CITIZEN SIGNATURE CERTIFICATE") only gives one certificate, and then the card itself doesn't have all the certificates so I have to get the missing ones at pki.cartaodecidadao.pt website and put them in a resources folder. Basically, I build my chain using both certificates in the card and in the resources folder, by linking them with the values in certificate.getIssuerX500Principal().getName() and certificate.getSubjecX500Principal().getName() (different cards can have different certificates of the same types, since the validity can vary so in one same type there can be 004 or 008 for example).
From what I understood, itext support for CAdES (MakeSignature.CryptoStandard.CADES) is more recent, but you need to use this since using MakeSignature.CryptoStandard.CMS might result in a signature that does not satisfy all the standards for CAdES (for example, missing the signing-certificate attribute - see http://docbox.etsi.org/ESI/Open/Latest_Drafts/prEN-319122-1v003-CAdES-core-STABLE-DRAFT.pdf).
The only minor issue with this code thought, is that there might be some optional attributes missing. I have used a validator from this tool https://github.com/arhs/sd-dss and while the signature generated passes the validation, it still gives a warning that the attribute issuer-serial is missing. I've created a post in hope anyone knows how to include the attribute here: CAdES Digital Signature
ks.getCertificateChain("CITIZEN SIGNATURE CERTIFICATE")
should return the certificate chain.
Where I can find a working online tool?
Suggestion: Make a call to the organization responsible for the middleware and ask for support.

How to write a string to Amazon S3 bucket?

How can I add a string as a file on amazon s3? From whaterver I searched, I got to know that we can upload a file to s3. What is the best way to upload data without creating file?
There is an overload for the AmazonS3.putObject method that accepts the bucket string, a key string, and a string of text content. I hadn't seen mention of it on stack overflow so I'm putting this here. It's going to be similar #Jonik's answer, but without the additional dependency.
AmazonS3 s3client = AmazonS3ClientBuilder.standard().withRegion(Regions.US_EAST_1).build();
s3client.putObject(bucket, key, contents);
Doesn't look as nice, but here is how you can do it using Amazons Java client, probably what JetS3t does behind the scenes anyway.
private boolean putArtistPage(AmazonS3 s3,String bucketName, String key, String webpage)
{
try
{
byte[] contentAsBytes = webpage.getBytes("UTF-8");
ByteArrayInputStream contentsAsStream = new ByteArrayInputStream(contentAsBytes);
ObjectMetadata md = new ObjectMetadata();
md.setContentLength(contentAsBytes.length);
s3.putObject(new PutObjectRequest(bucketname, key, contentsAsStream, md));
return true;
}
catch(AmazonServiceException e)
{
log.log(Level.SEVERE, e.getMessage(), e);
return false;
}
catch(Exception ex)
{
log.log(Level.SEVERE, ex.getMessage(), ex);
return false;
}
}
What is the best way to upload data
without creating file?
If you meant without creating a file on S3, well, you can't really do that. On Amazon S3, the only way to store data is as files, or using more accurate terminology, objects. An object can contain from 1 byte zero bytes to 5 terabytes of data, and is stored in a bucket. Amazon's S3 homepage lays out the basic facts quite clearly. (For other data storing options on AWS, you might want to read e.g. about SimpleDB.)
If you meant without creating a local temporary file, then the answer depends on what library/tool you are using. (As RickMeasham suggested, please add more details!) With the s3cmd tool, for example, you can't skip creating temp file, while with the JetS3t Java library uploading a String directly would be easy:
// (First init s3Service and testBucket)
S3Object stringObject = new S3Object("HelloWorld.txt", "Hello World!");
s3Service.putObject(testBucket, stringObject);
There is a simple way to do it with PHP, simply send the string as the body of the object, specifying the name of the new file in the key -
$s3->putObject(array(
'Bucket' => [Bucket name],
'Key' => [path/to/file.ext],
'Body' => [Your string goes here],
'ContentType' => [specify mimetype if you want],
));
This will create a new file according to the specified key, which has a content as specified in the string.
If you're using java, check out https://ivan-site.com/2015/11/interact-with-s3-without-temp-files/
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
class S3StreamJacksonTest {
private static final String S3_BUCKET_NAME = "bucket";
private static final String S3_KEY_NAME = "key";
private static final String CONTENT_TYPE = "application/json";
private static final AmazonS3 AMAZON_S3 = new AmazonS3Client();
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final TestObject TEST_OBJECT = new TestObject("test", 123, 456L);
public void testUploadWithStream() throws JsonProcessingException {
String fileContentString = OBJECT_MAPPER.writeValueAsString(TEST_OBJECT);
byte[] fileContentBytes = fileContentString.getBytes(StandardCharsets.UTF_8);
InputStream fileInputStream = new ByteArrayInputStream(fileContentBytes);
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(CONTENT_TYPE);
metadata.setContentLength(fileContentBytes.length);
PutObjectRequest putObjectRequest = new PutObjectRequest(
S3_BUCKET_NAME, S3_KEY_NAME, fileInputStream, metadata);
AMAZON_S3.putObject(putObjectRequest);
}
}
This works for me:
public static PutObjectResult WriteString(String bucket, String key, String stringToWrite, AmazonS3Client s3Client) {
ObjectMetadata meta = new ObjectMetadata();
meta.setContentMD5(new String(com.amazonaws.util.Base64.encode(DigestUtils.md5(stringToWrite))));
meta.setContentLength(stringToWrite.length());
InputStream stream = new ByteArrayInputStream(stringToWrite.getBytes(StandardCharsets.UTF_8));
return s3Client.putObject(bucket, key, stream, meta);
}
The sample code at https://docs.aws.amazon.com/AmazonS3/latest/dev/UploadObjSingleOpJava.html works for me.
s3Client.putObject(bucketName, stringObjKeyName, "Uploaded String Object");
Looks like this was added around 1.11.20, so make sure you are using that or new version of SDK.
https://javadoc.io/doc/com.amazonaws/aws-java-sdk-s3/1.11.20/com/amazonaws/services/s3/AmazonS3.html#putObject-java.lang.String-java.lang.String-java.lang.String-