Java Apache PDFBox - Encryption not deterministic - java

I have the following code.
public byte[] encryptPdf(byte[] pdf, String password) throws IOException {
ByteArrayOutputStream baos;
try (PDDocument pdDocument = PDDocument.load(pdf)) {
AccessPermission accessPermission = new AccessPermission();
StandardProtectionPolicy protectionPolicy = new
StandardProtectionPolicy(null, password, accessPermission);
protectionPolicy.setEncryptionKeyLength(128);
protectionPolicy.setPermissions(accessPermission);
pdDocument.protect(protectionPolicy);
baos = new ByteArrayOutputStream();
pdDocument.save(baos);
}
return baos.toByteArray();
}
#Test
public void shouldEncryptedPDFEquals() {
byte[] pdf = IOUtils.toByteArray(getClass().getClassLoader().getResourceAsStream("sample.pdf"));
byte[] firstEncryption = encryptPdf(pdf, "token");
byte[] secondEncryption = encryptPdf(pdf, "token");
assertThat(firstEncryption.length, is(secondEncryption.length));
}
Inside a test i will check encrypted documents if there are equals.
The Problem is, that the generates byte array is not deterministic.
If i call the method multiple times, the array length are not ever equals. The assert failed. But not for all types of pdf files.
Is there a bug inside the Apache PDFBox library?

Related

How to read a CSV file stored in a byte array, in order to generate checksum?

Here's the situation, I wrote a code to convert the CSV file(with information in it) into a ByteArray and to a String and stored it in the DB. Right now I need to generate the checksum for the said file without doing it manually. Now here's my question(there are a few actually):
1.)Is it literally possible to take in the ByteArray to generate the checksum?
2.)If so for (1), then why is my ByteArray different everytime I run the program even though I did not change anything(nor add any value) in the CSV file?
Or are there any better approaches in tackling this task? Any help would be much appreciated.
Here is the code:
public void main(){
// prepare the workbook
Workbook reportWorkbook = new XSSFWorkbook();
List<CellStyle> styles = initializeAllCellStyle(reportWorkbook);
// Write Tap On Phone Sheet
writeDataToSheet(reportWorkbook, styles, monthStart, monthEnd, year);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
reportWorkbook.write(bos);
} finally {
bos.close();
}
byte[] bytes = bos.toByteArray();
Date today = new Date();
String strYearMonth = yyyy + MM;
String xlsxCopyInBase64 = Base64.encodeBase64String(bytes);
Report bReport = new Report();
byte[] shaInBytes = ReportServiceImpl.digest(bytes, "SHA-256");
bReport.setBillingReport(EncryptionUtil.doAESEncrypt(xlsxCopyInBase64, "123",EncryptionUtil.HEX));
bReport.setCreatedBy("MANUAL");
bReport.setCreatedDate(today);
bReport.setReportDate(strYearMonth);
mobileDAO.saveToDB(bReport);
//Just to print out and test the checksum
logger.info(String.format(OUTPUT_FORMAT, "SHA-256" + "(hex)", bytesToHex(shaInBytes)));
}
public static byte[] digest(byte[] input, String algorithm) {
MessageDigest md;
try {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
byte[] result = md.digest(input);
md.reset();
return result;
}
public static String bytesToHexes(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}

JCPABE Encypt Decrypt String

I see the JCPABE project, but the methods that are in classes enable me to encrypt file or InputStream but not a simple Java string. How can I use these methods to encrypt/decrypt a string ? I have tried to convert the string in byte array but it doesn't work (i.e. String.getBytes("UTF_8"); same if i convert the String in an InputStream. How can I encrypt/decrypt a simple string?
Example:
My simple code:
String test="Message";
policy="newyork or losangeles";
Cpabe.encrypt(publickey, policy, test, test);
I have this message: The method encrypt(File, String, File, File) in the type Cpabe is not applicable for the arguments (File, String, String, String).
The function encrypt is this:
public static void encrypt(File publicKeyFile, String policy, File inputFile, File outputFile) throws IOException, AbeEncryptionException {
AbePublicKey publicKey = AbePublicKey.readFromFile(publicKeyFile);
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(inputFile));
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outputFile))) {
encrypt(publicKey, policy, in, out);
}
I have changed the function in:
public static void encrypt(File publicKeyFile, String policy, String inputstr, String outputstr) throws IOException, AbeEncryptionException {
AbePublicKey publicKey = AbePublicKey.readFromFile(publicKeyFile);
try (String in = new String(inputstr);
String out = new String(outputstr)) {
encrypt(publicKey, policy, in, out);
}
}
but I have other message: The resource type String does not implement java.lang.AutoCloseable on the String in and String out; while on the encrypt I have these message: The method encrypt(AbePublicKey, String, InputStream, OutputStream) in the type Cpabe is not applicable for the arguments (AbePublicKey, String, String, String).
This is the function with the 2 InputStream parameters:
public static void encrypt(AbePublicKey publicKey, String policy, InputStream input, OutputStream output) throws AbeEncryptionException, IOException {
AbeEncrypted encrypted = encrypt(publicKey, policy, input);
encrypted.writeEncryptedData(output, publicKey);
}
and this is the writeEncryptedData method:
public void writeEncryptedData(OutputStream out, AbePublicKey publicKey) throws IOException {
AbeOutputStream abeOut = new AbeOutputStream(out, publicKey);
Version.writeToStream(abeOut);
cipher.writeToStream(abeOut);
abeOut.writeInt(iv.length);
abeOut.write(iv);
byte[] buffer = new byte[1024];
int len;
while ((len = dataStream.read(buffer)) != -1) {
abeOut.write(buffer, 0, len);
}
}
Your code can not because of various reasons. First you need an InputStream and OutputStream. To use a String you have to first convert it to byte[] and then to a stream.
In your function you defined something like a "out parameter" String outputstr. However Strings are immutable in Java, therefore you can not use it that way and change it's content. Use it as return value instead.
Third never ever try to convert byte[] to String using new String(<byte array>). This does not return a String with printable characters but a String with binary non-printable content. You have to encode it e.g. using base64. Before decrypting it you have to apply base64 decode.
public static String encrypt(File publicKeyFile, String policy, String inputstr) throws IOException, AbeEncryptionException {
AbePublicKey publicKey = AbePublicKey.readFromFile(publicKeyFile);
try (InputStream in = new ByteArrayInputStream(inputstr.getBytes(StandardCharsets.UTF_8);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
encrypt(publicKey, policy, in, out);
return Base64.getEncoder().encodeToString(out.toByteArray());
}
}

Unable to load and store keystore from p12 inside J2EE container

I was trying to load the keystore from a p12 file , the behaviour is highly inconsistent where in the keystore.aliases().nextElement() gives proper alias once and displaying CN in other cases. In the later case I am not able to store the keystore (using keystore.store) and the output stream is empty .
Below is the code snippet. Let me know if I overlooked anything.
// the main code where i am facing issue
private byte[] generateKeyStoreData(String appName, Map<String, String> credentials)
throws ApplicationException {
try {
if (!credentials.containsKey(KEYSTORE)) {
throw new NullPointerException("No keystore provided");
}
if (!credentials.containsKey(KEYSTORE_PASSWORD)) {
throw new NullPointerException("No keystore password provided");
}
String keystoreStr = credentials.get(KEYSTORE);
char[] keystorePass = credentials.get(KEYSTORE_PASSWORD).toCharArray();
// I have printed the base64 string here and tried loading inside a standalone code
and it is working. The method is below
InputStream keystoreIs = base64stringToInputStream(keystoreStr);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(keystoreIs, keystorePass);
// I printed the keyStore.aliases().nextElement() which returns correct alias "omss"
// but returns CN in cases where it fails.
ByteArrayOutputStream keyStoreOut = new ByteArrayOutputStream();
keyStore.store(keyStoreOut, keystorePass);
// I printed the keystoreOut.toByteArray() and it is empty in failing cases
return keyStoreOut.toByteArray();
} catch (Exception e) {
// exception
}
}
// the conversion code from base64string to bytearrayinputstream
private InputStream base64stringToInputStream(String str) {
byte[] ba = DatatypeConverter.parseBase64Binary(str);
return new ByteArrayInputStream(ba);
}
//--------------------------------------------------------------------
// Below is api which calls the generateKeystore
//-------------------------------------------------------------------
// We get the inputstream from the uploaded p12 file and the below api is called
public void createKeystore(InputStream certFile,
char[] password) {
Util.nullCheck(certFile,
"Certificate File cannot be null or empty");
Util.nullCheck(password, "Password Cannot be null");
try {
// the method is below
byte[] raw = toByteArray(certFile);
// converting to base64 string
String base64encodedString = DatatypeConverter
.printBase64Binary(raw);
//....... we create a map of keystore string and password
// and the call is made to generateKeystore method above
}
catch(Exception e){
}
// the inputstream is converted to bytearray inputstream
private static byte[] toByteArray(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int reads = is.read();
while (reads != -1) {
baos.write(reads);
reads = is.read();
}
return baos.toByteArray();
}
Looks like the keystore.load() is not using "SunJSSE" as the default keystore provider in my j2ee environment rather it was using oraclepki provider. Now that i am loading keystore.load(Is,"SunJSSE") it is able to load properly.

HtmlUnit use WebClient to download an image as base64 encoded DATA Uri

I want to use an existing instance of WebClient to download an image. The reason for this is because I want the cookies to be passed with the request.
How can I download an image using an existing instance of WebClient?
Also, how can I base64 encode the image to be able to view it using data:image/jpeg;base64,...
Current code:
WebClient client = new WebClient(BrowserVersion.FIREFOX_3_6);
UnexpectedPage imagePage = client.getPage("http://...");
String imageString = imagePage.getWebResponse().getContentAsString();
BASE64Encoder encoder = new BASE64Encoder();
String base64data = encoder.encode(imageString.getBytes());
So now I have base64 data of the image, but I still can't view the image using data:image/jpeg;base64,....
A couple of things to consider:
The BASE64Encoder() generates a string that has a line break every 77 chars. Take that out using .replaceAll("\r?\n","").
For that method also, it is better to retrieve the web page InputStream rather than the string. Also, to convert that to a byte array, I used a utility method (source and other options can be found here).
Working source code:
public static void main (String args[]) throws IOException {
WebClient client = new WebClient(BrowserVersion.FIREFOX_3_6);
UnexpectedPage imagePage = client.getPage("http://i.stack.imgur.com/9DdHc.jpg");
BASE64Encoder encoder = new BASE64Encoder();
String base64data = encoder.encode(inputStreamToByteArray(imagePage.getWebResponse().getContentAsStream()));
System.out.println("<img src=\"data:image/png;base64,"+base64data.replaceAll("\r?\n","")+"\" />");
}
private static byte[] inputStreamToByteArray(InputStream is) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
return buffer.toByteArray();
}
Source image:
Output base64 image here.

Save Image Byte Array to .net webservice and retrieve it

I have a .net ASMX webservice that I'm consuming using the ksoap2 library. In the service, I first save the user image and later retrieve it. However, once I retrieve it, the byte array is intact, but the BitmapFactory is unable to decode it and returns a null.
To convert to byte array:
Bitmap viewBitmap = Bitmap.createBitmap(imageView.getWidth(),
imageView.getHeight(), Bitmap.Config.ARGB_8888);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
viewBitmap.compress(CompressFormat.PNG, 0 /* ignored for PNG */, bos);
byte[] bitmapdata = bos.toByteArray();
The webservice accepts the bytearray in the byte[] format.
To convert the array into bitmap:
byte[] blob= info.get(Main.KEY_THUMB_BYTES).getBytes();
Bitmap bmp=BitmapFactory.decodeByteArray(blob,0,blob.length); // Return null :(
imageView.setImageBitmap(bmp);
From partial-analysis, it appears that the byte array does not change. Then why does decoding return null? Is there a better to save an image and pass it through a webservice? I didn't analyze the whole byte array, so I'm guessing it might've changed a bit.
Any thoughts? Many thanks!
UPDATE:
I just tried converting the byte[] to string using:
Base64.encodeToString( bos.toByteArray(), Base64.DEFAULT);
And decode using:
byte[] blob= Base64.decode(info.get(Main.KEY_THUMB_BYTES));
Now all i get is a White picture. I'm not sure what's wrong here. Please help.
UPDATE:
I'm storing this image inside of a database, in a column of type varchar(max). Should I be storing this byte array string inside a different sql data type? I'm not too experienced with SQL, so I used varchar because it did not convert text to unicode, which I thought might be good for thie byte array.
Thanks!
convert your byte arrays to Base64 that is a string and easy to transfer:
public static String bitmapToBase64(Bitmap bitmap) {
byte[] bitmapdata = bitmapToByteArray(bitmap);
return Base64.encodeBytes(bitmapdata);
}
public static byte[] bitmapToByteArray(Bitmap bitmap) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG, 0 /* ignored for PNG */, bos);
byte[] bitmapdata = bos.toByteArray();
return bitmapdata;
}
and
public static Bitmap base64ToBitmap(String strBase64) throws IOException {
byte[] bitmapdata = Base64.decode(strBase64);
Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapdata, 0,
bitmapdata.length);
return bitmap;
}
also you can do it not only on image files but also on every file types:
public static String fileToBase64(String path) throws IOException {
byte[] bytes = fileToByteArray(path);
return Base64.encodeBytes(bytes);
}
public static byte[] fileToByteArray(String path) throws IOException {
File imagefile = new File(path);
byte[] data = new byte[(int) imagefile.length()];
FileInputStream fis = new FileInputStream(imagefile);
fis.read(data);
fis.close();
return data;
}
public static void base64ToFile(String path, String strBase64)
throws IOException {
byte[] bytes = Base64.decode(strBase64);
byteArrayTofile(path, bytes);
}
public static void byteArrayTofile(String path, byte[] bytes)
throws IOException {
File imagefile = new File(path);
File dir = new File(imagefile.getParent());
if (!dir.exists()) {
dir.mkdirs();
}
FileOutputStream fos = new FileOutputStream(imagefile);
fos.write(bytes);
fos.close();
}

Categories

Resources