I need to encode some data in the Base64 encoding in Java. How do I do that? What is the name of the class that provides a Base64 encoder?
I tried to use the sun.misc.BASE64Encoder class, without success. I have the following line of Java 7 code:
wr.write(new sun.misc.BASE64Encoder().encode(buf));
I'm using Eclipse. Eclipse marks this line as an error. I imported the required libraries:
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
But again, both of them are shown as errors. I found a similar post here.
I used Apache Commons as the solution suggested by including:
import org.apache.commons.*;
and importing the JAR files downloaded from: http://commons.apache.org/codec/
But the problem still exists. Eclipse still shows the errors previously mentioned. What should I do?
You need to change the import of your class:
import org.apache.commons.codec.binary.Base64;
And then change your class to use the Base64 class.
Here's some example code:
byte[] encodedBytes = Base64.encodeBase64("Test".getBytes());
System.out.println("encodedBytes " + new String(encodedBytes));
byte[] decodedBytes = Base64.decodeBase64(encodedBytes);
System.out.println("decodedBytes " + new String(decodedBytes));
Then read why you shouldn't use sun.* packages.
Update (2016-12-16)
You can now use java.util.Base64 with Java 8. First, import it as you normally do:
import java.util.Base64;
Then use the Base64 static methods as follows:
byte[] encodedBytes = Base64.getEncoder().encode("Test".getBytes());
System.out.println("encodedBytes " + new String(encodedBytes));
byte[] decodedBytes = Base64.getDecoder().decode(encodedBytes);
System.out.println("decodedBytes " + new String(decodedBytes));
If you directly want to encode string and get the result as encoded string, you can use this:
String encodeBytes = Base64.getEncoder().encodeToString((userName + ":" + password).getBytes());
See Java documentation for Base64 for more.
Use Java 8's never-too-late-to-join-in-the-fun class: java.util.Base64
new String(Base64.getEncoder().encode(bytes));
In Java 8 it can be done as:
Base64.getEncoder().encodeToString(string.getBytes(StandardCharsets.UTF_8))
Here is a short, self-contained complete example:
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Temp {
public static void main(String... args) throws Exception {
final String s = "old crow medicine show";
final byte[] authBytes = s.getBytes(StandardCharsets.UTF_8);
final String encoded = Base64.getEncoder().encodeToString(authBytes);
System.out.println(s + " => " + encoded);
}
}
Output:
old crow medicine show => b2xkIGNyb3cgbWVkaWNpbmUgc2hvdw==
You can also convert using Base64 encoding. To do this, you can use the javax.xml.bind.DatatypeConverter#printBase64Binary method.
For example:
byte[] salt = new byte[] { 50, 111, 8, 53, 86, 35, -19, -47 };
System.out.println(DatatypeConverter.printBase64Binary(salt));
With Guava
pom.xml:
<dependency>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
<type>jar</type>
<version>14.0.1</version>
</dependency>
Sample code:
// encode
String s = "Hello Việt Nam";
String base64 = BaseEncoding.base64().encode(s.getBytes("UTF-8"));
// decode
System.out.println("Base64:" + base64); // SGVsbG8gVmnhu4d0IE5hbQ==
byte[] bytes = BaseEncoding.base64().decode(base64);
System.out.println("Decoded: " + new String(bytes, "UTF-8")); // Hello Việt Nam
Eclipse gives you an error/warning because you are trying to use internal classes that are specific to a JDK vendor and not part of the public API. Jakarta Commons provides its own implementation of base64 codecs, which of course reside in a different package. Delete those imports and let Eclipse import the proper Commons classs for you.
For Java 6-7, the best option is to borrow code from the Android repository. It has no dependencies.
https://github.com/android/platform_frameworks_base/blob/master/core/java/android/util/Base64.java
Java 8 does contain its own implementation of Base64. However, I found one slightly disturbing difference. To illustrate, I will provide a code example:
My codec wrapper:
public interface MyCodec
{
static String apacheDecode(String encodedStr)
{
return new String(Base64.decodeBase64(encodedStr), Charset.forName("UTF-8"));
}
static String apacheEncode(String decodedStr)
{
byte[] decodedByteArr = decodedStr.getBytes(Charset.forName("UTF-8"));
return Base64.encodeBase64String(decodedByteArr);
}
static String javaDecode(String encodedStr)
{
return new String(java.util.Base64.getDecoder().decode(encodedStr), Charset.forName("UTF-8"));
}
static String javaEncode(String decodedStr)
{
byte[] decodedByteArr = decodedStr.getBytes(Charset.forName("UTF-8"));
return java.util.Base64.getEncoder().encodeToString(decodedByteArr);
}
}
Test Class:
public class CodecDemo
{
public static void main(String[] args)
{
String decodedText = "Hello World!";
String encodedApacheText = MyCodec.apacheEncode(decodedText);
String encodedJavaText = MyCodec.javaEncode(decodedText);
System.out.println("Apache encoded text: " + MyCodec.apacheEncode(encodedApacheText));
System.out.println("Java encoded text: " + MyCodec.javaEncode(encodedJavaText));
System.out.println("Encoded results equal: " + encodedApacheText.equals(encodedJavaText));
System.out.println("Apache decode Java: " + MyCodec.apacheDecode(encodedJavaText));
System.out.println("Java decode Java: " + MyCodec.javaDecode(encodedJavaText));
System.out.println("Apache decode Apache: " + MyCodec.apacheDecode(encodedApacheText));
System.out.println("Java decode Apache: " + MyCodec.javaDecode(encodedApacheText));
}
}
OUTPUT:
Apache encoded text: U0dWc2JHOGdWMjl5YkdRaA0K
Java encoded text: U0dWc2JHOGdWMjl5YkdRaA==
Encoded results equal: false
Apache decode Java: Hello World!
Java decode Java: Hello World!
Apache decode Apache: Hello World!
Exception in thread "main" java.lang.IllegalArgumentException: Illegal base64 character d
at java.util.Base64$Decoder.decode0(Base64.java:714)
at java.util.Base64$Decoder.decode(Base64.java:526)
at java.util.Base64$Decoder.decode(Base64.java:549)
Notice that the Apache encoded text contain additional line breaks (white spaces) at the end. Therefore, in order for my codec to yield the same result regardless of Base64 implementation, I had to call trim() on the Apache encoded text. In my case, I simply added the aforementioned method call to the my codec's apacheDecode() as follows:
return Base64.encodeBase64String(decodedByteArr).trim();
Once this change was made, the results are what I expected to begin with:
Apache encoded text: U0dWc2JHOGdWMjl5YkdRaA==
Java encoded text: U0dWc2JHOGdWMjl5YkdRaA==
Encoded results equal: true
Apache decode Java: Hello World!
Java decode Java: Hello World!
Apache decode Apache: Hello World!
Java decode Apache: Hello World!
CONCLUSION: If you want to switch from Apache Base64 to Java, you must:
Decode encoded text with your Apache decoder.
Encode resulting (plain) text with Java.
If you switch without following these steps, most likely you will run into problems. That is how I made this discovery.
To convert this, you need an encoder & decoder which you will get from Base64Coder - an open-source Base64 encoder/decoder in Java. It is file Base64Coder.java you will need.
Now to access this class as per your requirement you will need the class below:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Base64 {
public static void main(String args[]) throws IOException {
/*
* if (args.length != 2) {
* System.out.println(
* "Command line parameters: inputFileName outputFileName");
* System.exit(9);
* } encodeFile(args[0], args[1]);
*/
File sourceImage = new File("back3.png");
File sourceImage64 = new File("back3.txt");
File destImage = new File("back4.png");
encodeFile(sourceImage, sourceImage64);
decodeFile(sourceImage64, destImage);
}
private static void encodeFile(File inputFile, File outputFile) throws IOException {
BufferedInputStream in = null;
BufferedWriter out = null;
try {
in = new BufferedInputStream(new FileInputStream(inputFile));
out = new BufferedWriter(new FileWriter(outputFile));
encodeStream(in, out);
out.flush();
}
finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
private static void encodeStream(InputStream in, BufferedWriter out) throws IOException {
int lineLength = 72;
byte[] buf = new byte[lineLength / 4 * 3];
while (true) {
int len = in.read(buf);
if (len <= 0)
break;
out.write(Base64Coder.encode(buf, 0, len));
out.newLine();
}
}
static String encodeArray(byte[] in) throws IOException {
StringBuffer out = new StringBuffer();
out.append(Base64Coder.encode(in, 0, in.length));
return out.toString();
}
static byte[] decodeArray(String in) throws IOException {
byte[] buf = Base64Coder.decodeLines(in);
return buf;
}
private static void decodeFile(File inputFile, File outputFile) throws IOException {
BufferedReader in = null;
BufferedOutputStream out = null;
try {
in = new BufferedReader(new FileReader(inputFile));
out = new BufferedOutputStream(new FileOutputStream(outputFile));
decodeStream(in, out);
out.flush();
}
finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
private static void decodeStream(BufferedReader in, OutputStream out) throws IOException {
while (true) {
String s = in.readLine();
if (s == null)
break;
byte[] buf = Base64Coder.decodeLines(s);
out.write(buf);
}
}
}
In Android you can convert your bitmap to Base64 for Uploading to a server or web service.
Bitmap bmImage = //Data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bmImage.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] imageData = baos.toByteArray();
String encodedImage = Base64.encodeArray(imageData);
This “encodedImage” is text representation of your image. You can use this for either uploading purpose or for diplaying directly into an HTML page as below (reference):
<img alt="" src="data:image/png;base64,<?php echo $encodedImage; ?>" width="100px" />
<img alt="" src="data:image/png;base64,/9j/4AAQ...........1f/9k=" width="100px" />
Documentation: http://dwij.co.in/java-base64-image-encoder
On Android, use the static methods of the android.util.Base64 utility class. The referenced documentation says that the Base64 class was added in API level 8 (Android 2.2 (Froyo)).
import android.util.Base64;
byte[] encodedBytes = Base64.encode("Test".getBytes());
Log.d("tag", "encodedBytes " + new String(encodedBytes));
byte[] decodedBytes = Base64.decode(encodedBytes);
Log.d("tag", "decodedBytes " + new String(decodedBytes));
Apache Commons has a nice implementation of Base64. You can do this as simply as:
// Encrypt data on your side using BASE64
byte[] bytesEncoded = Base64.encodeBase64(str .getBytes());
System.out.println("ecncoded value is " + new String(bytesEncoded));
// Decrypt data on other side, by processing encoded data
byte[] valueDecoded= Base64.decodeBase64(bytesEncoded );
System.out.println("Decoded value is " + new String(valueDecoded));
You can find more details about base64 encoding at Base64 encoding using Java and JavaScript.
If you are using Spring Framework at least version 4.1, you can use the org.springframework.util.Base64Utils class:
byte[] raw = { 1, 2, 3 };
String encoded = Base64Utils.encodeToString(raw);
byte[] decoded = Base64Utils.decodeFromString(encoded);
It will delegate to Java 8's Base64, Apache Commons Codec, or JAXB DatatypeConverter, depending on what is available.
Simple example with Java 8:
import java.util.Base64;
String str = "your string";
String encodedStr = Base64.getEncoder().encodeToString(str.getBytes("utf-8"));
In Java 7 I coded this method
import javax.xml.bind.DatatypeConverter;
public static String toBase64(String data) {
return DatatypeConverter.printBase64Binary(data.getBytes());
}
If you are stuck to an earlier version of Java than 8 but already using AWS SDK for Java, you can use com.amazonaws.util.Base64.
I tried with the following code snippet. It worked well. :-)
com.sun.org.apache.xml.internal.security.utils.Base64.encode("The string to encode goes here");
public String convertImageToBase64(String filePath) {
byte[] fileContent = new byte[0];
String base64encoded = null;
try {
fileContent = FileUtils.readFileToByteArray(new File(filePath));
} catch (IOException e) {
log.error("Error reading file: {}", filePath);
}
try {
base64encoded = Base64.getEncoder().encodeToString(fileContent);
} catch (Exception e) {
log.error("Error encoding the image to base64", e);
}
return base64encoded;
}
GZIP + Base64
The length of the string in a Base64 format is greater then original: 133% on average. So it makes sense to first compress it with GZIP, and then encode to Base64. It gives a reduction of up to 77% for strings greater than 200 characters and more. Example:
public static void main(String[] args) throws IOException {
byte[] original = randomString(100).getBytes(StandardCharsets.UTF_8);
byte[] base64 = encodeToBase64(original);
byte[] gzipToBase64 = encodeToBase64(encodeToGZIP(original));
byte[] fromBase64 = decodeFromBase64(base64);
byte[] fromBase64Gzip = decodeFromGZIP(decodeFromBase64(gzipToBase64));
// test
System.out.println("Original: " + original.length + " bytes, 100%");
System.out.println("Base64: " + base64.length + " bytes, "
+ (base64.length * 100 / original.length) + "%");
System.out.println("GZIP+Base64: " + gzipToBase64.length + " bytes, "
+ (gzipToBase64.length * 100 / original.length) + "%");
//Original: 3700 bytes, 100%
//Base64: 4936 bytes, 133%
//GZIP+Base64: 2868 bytes, 77%
System.out.println(Arrays.equals(original, fromBase64)); // true
System.out.println(Arrays.equals(original, fromBase64Gzip)); // true
}
public static byte[] decodeFromBase64(byte[] arr) {
return Base64.getDecoder().decode(arr);
}
public static byte[] encodeToBase64(byte[] arr) {
return Base64.getEncoder().encode(arr);
}
public static byte[] decodeFromGZIP(byte[] arr) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(arr);
GZIPInputStream gzip = new GZIPInputStream(bais);
return gzip.readAllBytes();
}
public static byte[] encodeToGZIP(byte[] arr) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(baos);
gzip.write(arr);
gzip.finish();
return baos.toByteArray();
}
public static String randomString(int count) {
StringBuilder str = new StringBuilder();
for (int i = 0; i < count; i++) {
str.append(" ").append(UUID.randomUUID().toString());
}
return str.toString();
}
See also: How to get the JAR file for sun.misc.BASE64Encoder class?
add this library into your app level dependancies
implementation 'org.apache.commons:commons-collections4:4.4'
Related
I'm taking part in a Capture the Flag contest. A question I'm stuck on relates to deserialization of a Java object. I'm interrogating a cookie string which is base64 encoded. When I decode that, I believe it is a Java serialized object which I want to deserialize in order to alter their values and re-encode in base64.
I know nothing about java, I've tried to deserialize the base64 decoded bytes, but I think this is the wrong type.
import java.util.Base64;
import java.util.UUID;
import java.io.UnsupportedEncodingException;
import java.io.ObjectInputStream;
public class decode {
public static void main(String args[]){
try {
// Encode using basic encoder
String base64encodedString = "mybase64encodedstring==";
System.out.println("Base64 encoded string :" + base64encodedString);
// Decode
byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
ObjectInputStream in = new ObjectInputStream(base64decodedBytes);
System.out.println("Deserialised data: \n" + in.readObject().toString());
System.out.println("Original String: " + new String(base64decodedBytes, "utf-8"));
}catch(UnsupportedEncodingException e){
System.out.println("Error :" + e.getMessage());
}
}
}
The error I get is:
incompatible types: byte[] cannot be converted to InputStream
Any help appreciated!
Use ByteArrayInputStream instead of ObjectInputStream.
So, use this:
InputStream in = new ByteArrayInputStream(base64decodedBytes);
instead this:
ObjectInputStream in = new ObjectInputStream(base64decodedBytes);
#Hrabosch said use ByteArrayInputStream instead. Actually, use ByteArrayInputStream aswell
Also, if the data is a bunch of bytes written by an ObjectOuputStream, then it makes no sense to construct a string from those bytes
Like so
import java.util.Base64;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
public class Decode {
public static void main(String args[]){
try {
// Encode using basic encoder
// I assume you replace this with actual data...
String base64encodedString = "mybase64encodedstring==";
System.out.println("Base64 encoded string :" + base64encodedString);
// Decode
byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
InputStream in = new ByteArrayInputStream(base64decodedBytes);
ObjectInputStream obin = new ObjectInputStream(in);
Object object = obin.readObject();
System.out.println("Deserialised data: \n" + object.toString());
// You could also try...
System.out.println("Object class is " + object.getClass().toString());
// Don't do this!! The original data was not a string!
//System.out.println("Original String: " + new String(base64decodedBytes, "utf-8"));
}catch(ClassNotFoundException | IOException e){
System.out.println("Error :" + e.getMessage());
}
}
}
I have an application where I am generating a "target file" based on a Java "source" class. I want to regenerate the target when the source changes. I have decided the best way to do this would be to get a byte[] of the class contents and calculate a checksum on the byte[].
I am looking for the best way to get the byte[] for a class. This byte[] would be equivalent to the contents of the compiled .class file. Using ObjectOutputStream does not work. The code below generates a byte[] that is much smaller than the byte contents of the class file.
// Incorrect function to calculate the byte[] contents of a Java class
public static final byte[] getClassContents(Class<?> myClass) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try( ObjectOutputStream stream = new ObjectOutputStream(buffer) ) {
stream.writeObject(myClass);
}
// This byte array is much smaller than the contents of the *.class file!!!
byte[] contents = buffer.toByteArray();
return contents;
}
Is there a way to get the byte[] with the identical contents of the *.class file? Calculating the checksum is the easy part, the hard part is obtaining the byte[] contents used to calculate an MD5 or CRC32 checksum.
THis is the solution that I ended up using. I don't know if it's the most efficient implementation, but the following code uses the class loader to get the location of the *.class file and reads its contents. For simplicity, I skipped buffering of the read.
// Function to obtain the byte[] contents of a Java class
public static final byte[] getClassContents(Class<?> myClass) throws IOException {
String path = myClass.getName().replace('.', '/');
String fileName = new StringBuffer(path).append(".class").toString();
URL url = myClass.getClassLoader().getResource(fileName);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (InputStream stream = url.openConnection().getInputStream()) {
int datum = stream.read();
while( datum != -1) {
buffer.write(datum);
datum = stream.read();
}
}
return buffer.toByteArray();
}
I don't get what you means, but i think you are looking for this, MD5.
To check MD5 of a file, you can use this code
public String getMd5(File file)
{
DigestInputStream stream = null;
try
{
stream = new DigestInputStream(new FileInputStream(file), MessageDigest.getInstance("MD5"));
byte[] buffer = new byte[65536];
read = stream.read(buffer);
while (read >= 1) {
read = stream.read(buffer);
}
}
catch (Exception ignored)
{
int read;
return null;
}
return String.format("%1$032x", new Object[] { new BigInteger(1, stream.getMessageDigest().digest()) });
}
Then, you can store the md5 of a file in any way for exmaple XML. An exmaple of MD5 is 49e6d7e2967d1a471341335c49f46c6c so once the file name and size change, md5 will change. You can store md5 of each file in XML format and next time your run a code to check md5 and compare the md5 of each file in the xml file.
If you really want the contents of the .class file, you should read the contents of .class file, not the byte[] representation that is in memory. So something like
import java.io.*;
public class ReadSelf {
public static void main(String args[]) throws Exception {
Class classInstance = ReadSelf.class;
byte[] bytes = readClass(classInstance);
}
public static byte[] readClass(Class classInstance) throws Exception {
String name = classInstance.getName();
name = name.replaceAll("[.]", "/") + ".class";
System.out.println("Reading this: " + name);
File file = new File(name);
System.out.println("exists: " + file.exists());
return read(file);
}
public static byte[] read(File file) throws Exception {
byte[] data = new byte[(int)file.length()]; // can only read a file of size INT_MAX
DataInputStream inputStream =
new DataInputStream(
new BufferedInputStream(
new FileInputStream(file)));
int total = 0;
int nRead = 0;
try {
while((nRead = inputStream.read(data)) != -1) {
total += nRead;
}
}
finally {
inputStream.close();
}
System.out.println("Read " + total
+ " characters, which should match file length of "
+ file.length() + " characters");
return data;
}
}
I have an assignment where i need to create a .xlsx and convert it to base64 and this file will sent as an attachment in an email webservice.The code for conversion of .xlsx to base64 is as below:
import java.io.*;
import org.apache.commons.codec.binary.Base64;
public class Test {
public static void main(String [] args) {
String fileName = "C:/Users/kk.txt";
try {
byte[] buffer = new byte[1000];
FileInputStream inputStream =
new FileInputStream(fileName);
int total = 0;
int nRead = 0;
String reference=null;
while((nRead = inputStream.read(buffer)) != -1) {
String name=(new String(buffer));
byte[] encodedBytes = Base64.encodeBase64(name.getBytes());
System.out.println(new String(encodedBytes));
}
inputStream.close();
System.out.println("Read " + total + " bytes");
}
catch(FileNotFoundException ex) {
System.out.println(
"Unable to open file '" +
fileName + "'");
}
catch(IOException ex) {
System.out.println(
"Error reading file '"
+ fileName + "'");
// Or we could just do this:
// ex.printStackTrace();
}
}
}
but when i send it as a mail using BPEL and when i open the file it shows general input/output error.But when i send it as a text it works fine. Should i not use this code for excel or where am i wrong.Am totally new to java.
Okay - my guess is that you're not changing the mimetype etc features of the email you're creating for the attachment ( and that it's default is text/html ).
Take a look here - which is for a word doc instead - but outlines what I think you should be doing ( assuming I found the right kind of BPEL )
For your file type ".xlsx", this table shows the appropriate mime-type is like
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
package com.denemeler;
import java.io.*;
import java.util.Base64;
public class test {
public static void main(String[] args) throws IOException {
String filePath = "D:\\test.xls";
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
byte[] bytes = new byte[(int) file.length()];
fis.read(bytes);
String base64 = new sun.misc.BASE64Encoder().encode(bytes);
String destinationPath = "D:\\Destination\\donusmusHali.xls";
//decode Base64 String to image
FileOutputStream fos = new FileOutputStream(destinationPath);
bytes = new sun.misc.BASE64Decoder().decodeBuffer(base64);
fos.write(bytes);
fis.close();
fos.close();
}
}
this code works for encoding and decoding excel files.
Generally, we use filename.xls in outputStream to write our excel workbook.
But if need to send it over network in base64 format then ByteArrayOutputStream is the option to go for it:
Workbook workbook = new HSSFWorkbook();
ByteArrayOutputStream b = new ByteArrayOutputStream();
try {
workbook.write(b);
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
byte[] bytes = bos.toByteArray(
);
Base64.encodeBase64String(bytes);
This solution worked for me, As first excel was created using Apache POI then applied above code and converted it to base64, which when decoded over network then encoded file was getting opened in a excel application as expected. :)
You can follow same on https://nupur28ag.blogspot.com/2020/01/get-base64-from-excel-created-using.html
I am writting a program where I take a string, encrypt it and then write it in a file. Then later, I read from the file the string, decrypt it and then modify it. Here's my code for DES encryption/decryption:
/* class for crypting and decrypting a file */
class DESEncrypter
{
private Cipher encryptionCipher;
private Cipher decryptionCipher;
public DESEncrypter (SecretKey key) throws Exception
{
encryptionCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
decryptionCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
decryptionCipher.init(Cipher.DECRYPT_MODE, key);
}
/* write to 'out' the encryption of the information read from 'in' */
public String encrypt(String unencryptedString)
{
String encryptedString = "";
try {
byte[] unencryptedByteArray = unencryptedString.getBytes("UTF8");
byte[] encryptedBytes = this.encryptionCipher.doFinal(unencryptedByteArray);
encryptedString = new sun.misc.BASE64Encoder().encode(encryptedBytes);
} catch (Exception ex) {
Logger.getLogger(DESEncrypter.class.getName()).log(Level.SEVERE, null, ex);
}
return encryptedString;
}
private static String bytes2String(byte[] bytes)
{
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < bytes.length; i++)
{
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
/* write to 'out' the information obtained by decrypting the information read from 'in' */
public String decrypt (String encryptedString) throws UnsupportedEncodingException
{
byte[] unencryptedByteArray = new byte[4096];
try {
// Encode bytes to base64 to get a string
byte[] decodedBytes = new sun.misc.BASE64Decoder().decodeBuffer(encryptedString);
// Decrypt
unencryptedByteArray = this.decryptionCipher.doFinal(decodedBytes);
} catch (Exception ex) {
Logger.getLogger(DESEncrypter.class.getName()).log(Level.SEVERE, null, ex);
}
return bytes2String(unencryptedByteArray);
}
}
And this is the function where I write a encrypted String in a file:
public void writeToFileEncrypted(String filename, String owner, String departament)
{
try
{
BufferedReader br = new BufferedReader(new FileReader(new File("files_encrypted")));
String crypt = "";
String aux;
while ((aux = br.readLine()) != null)
{
crypt += aux;
}
br.close();
String info = this.server.crypt.decrypt(crypt);
info += filename + " " + owner + " " + departament + "\n";
/* delete the old encryption */
File temp = new File("files_encrypted");
temp.delete();
String infoCrypt = this.server.crypt.encrypt(info);
File newFiles = new File("files_encrypted");
if (newFiles.createNewFile() == false)
{
log.severe("Failed to re-create the 'files_encrypted' file when trying to add a new file");
return;
}
BufferedWriter bw = new BufferedWriter(new FileWriter(newFiles));
bw.write(infoCrypt);
bw.close();
}
catch (Exception e)
{
log.warning("An exception was caught while trying to remove '" + clientName + "' from the banned list");
e.printStackTrace();
return;
}
}
While the server runs, I can make modification to that String from file(run that function many time). The problem is when I close the server and then I open it again because I get the error:
javax.crypto.BadPaddingException: Given final block not properly padded
This is how I read from file when the server opens:
BufferedReader br = new BufferedReader(new FileReader(new File("files_encrypted")));
String crypto = new String();
String aux;
while ((aux = br.readLine()) != null)
{
crypto += aux;
readBytes++;
}
br.close();
System.out.println(readBytes);
info = this.crypt.decrypt(crypto);
Why do I get that error? What I'm doing wrong? I must write the encrypted String in file some other way?
LATER EDIT:
I've changed the function that read a String from a file, decrypt it, modify it , encrypt it and then write it in file.
public void writeToFileEncrypted(String filename, String owner, String departament)
{
try
{
File f = new File("files_encrypted");
int nrRead = 0;
String info = null;
FileInputStream fis = new FileInputStream(f);
StringBuffer sb = new StringBuffer();
int ch;
while ((ch = fis.read()) != -1)
{
sb.append((char)ch);
nrRead++;
}
fis.close();
StringBuilder sba = null;
if (nrRead != 0)
{
info = this.server.crypt.decrypt(new String(sb.toString().getBytes("UTF-8"), "UTF-8"));
sba = new StringBuilder(info);
sba.append(filename + " " + owner + " " + departament + " ");
}
else
{
sba = new StringBuilder(filename + " " + owner + " " + departament + " ");
}
/* delete the old encryption */
File temp = new File("files_encrypted");
temp.delete();
//System.out.println("before: " + sba.toString());
String infoCrypt = this.server.crypt.encrypt(sba.toString());
//System.out.println("after: " + infoCrypt);
File newFiles = new File("files_encrypted");
if (newFiles.createNewFile() == false)
{
log.severe("Failed to re-create the 'files_encrypted' file when trying to add a new file");
return;
}
FileOutputStream fos = new FileOutputStream(newFiles);
fos.write(infoCrypt.getBytes("UTF-8"));
fos.flush();
fos.close();
}
catch (Exception e)
{
log.warning("An exception was caught while trying to remove '" + clientName + "' from the banned list");
e.printStackTrace();
return;
}
}
I've also modified where I read the info from file when server opens for the first time:
FileInputStream fis = new FileInputStream(f);
StringBuffer sb = new StringBuffer();
int ch;
while ((ch = fis.read()) != -1)
{
sb.append((char)ch);
readBytes++;
}
fis.close();
if (readBytes != 0)
{
System.out.println("on: " + sb.toString());
info = this.crypt.decrypt(new String(sb.toString().getBytes("UTF-8"), "UTF-8"));
System.out.println("load: " + info);
}
}
At the System.out.println with "on: " what I read from file is exactly what I've written encrypted, without any spaces or new lines. If I read with read(buffer), where buffer is byte[], it seems that adds a lot of spaces.
Although I've made all this modifications I still get the error javax.crypto.BadPaddingException: Given final block not properly padded
Has somebody any idea what's going on here?
There are a few things here.
private static String bytes2String(byte[] bytes)
Is dodgy, you are casting a byte to a char in this method so there is no character encoding specified here. To convert bytes to characters you should just use the String constructor that takes an array of bytes and an encoding. e.g.
byte[] tmp = new byte[10];
String a = new String(tmp, "UTF-8");
Be careful using BufferedReaders + .readLine() - this will strip out any newline characters from your file as you read it unless you add them back into your buffer. Although I don't think this is your problem.
But I think the best way to simplify your code is to write the encoded bytes via an OutputStream directly to the file. Unless you need to send the contents of the file over a transport that doesn't like binary data, there is no need to base64 encode. Just use Input/OutputStreams to write the encrypted bytes direct to disk.
RESPONSE TO LATER EDIT:
You are still mixing up your use of binary data (bytes) and character data (String/chars). You can't do things like:
int ch;
while ((ch = fis.read()) != -1)
{
sb.append((char)ch);
The input stream is retuning bytes, a byte is not a character and just casting it to one is going to cause problems. When using encryption the output from the encryption operation is binary data, and the input to the decryption operation is also binary data. The fact that your are encrypting text is something you deal with before the encryption occurs, and after the decryption occurs. You basic operation should go along the following lines.
Take the text you want to encrypt and convert it to bytes, specifying an encoding using the .getBytes(String charsetName) on your String.
Pass these bytes into your encryption routine
Write the resulting bytes directly to disk
To decrypt:
Read the bytes from the file
Pass the bytes to your decryption routine (as bytes! no Strings/ text involved)
Take the out put bytes and re-construct you String using new String(byte[] bytes, String charsetName) specifying the same encoding as before.
You might find the following (untested, but should work) methods useful:
public byte[] readBinaryFile(File f) throws IOException
{
byte[] contents = new byte[(int)f.length()];
BufferedInputStream bis = null;
try
{
bis = new BufferedInputStream(new FileInputStream(f));
DataInputStream dis = new DataInputStream(bis);
dis.readFully(contents);
}
finally
{
if(bis != null)
{
bis.close();
}
}
return contents;
}
public void writeBinaryFile(byte[] contents, File f) throws IOException
{
BufferedOutputStream bos = null;
try
{
bos = new BufferedOutputStream(new FileOutputStream(f));
bos.write(contents);
}
finally
{
if(bos != null)
{
bos.close();
}
}
}
So you will also need to change the interface, and internals of your encrypt and decrypt methods so they take and return byte arrays, and ditch the base64 encoding.
You have several problems. The reading and decrypting process should be symmetric with the encrypting and writing process. But
you transform your String into a byte[] using getBytes("UTF8"), which is fine, but you don't use new String(byte[], "UTF8") to do the reverse operation.
you write a whole String to a file, including potential line breaks, but you read it line by line and concatenate each line, thus losing the line breaks in the process. You must read each and every char that has been written.
Also, relying on undocumented, unsupported classes like sun.misc.Base64Encoder/Decoder shouldn't be done. Use Apache commons-codec to find a documented Base64 encoding, guaranteed to still be there when the next JDK comes out, and which can be used on every JVM, including non-Sun JVMs.
I think it is in the initialization
SecureRandom sr = new SecureRandom();
cipher.init( Cipher.DECRYPT_MODE, desKey ,sr);
Not sure this is the primary problem, but when you return the decrypted String from decrypt(), you should be using:
return new String(unencryptedByteArray, "UTF-8");
https://web.archive.org/web/20110422225659/https://en.wikipedia.org/wiki/Base64#URL_applications
talks about base64Url - Decode
a modified Base64 for URL variant exists, where no padding '=' will be used, and the '+' and '/' characters of standard Base64 are respectively replaced by '-' and '_'
I created the following function:
public static String base64UrlDecode(String input) {
String result = null;
BASE64Decoder decoder = new BASE64Decoder();
try {
result = decoder.decodeBuffer(input.replace('-','+').replace('/','_')).toString();
}
catch (IOException e) {
System.out.println(e.getMessage());
}
return result;
}
it returns a very small set of characters that don't even resemble to the expected results.
any ideas?
Java8+
import java.util.Base64;
return Base64.getUrlEncoder().encodeToString(bytes);
Base64 encoding is part of the JDK since Java 8. URL safe encoding is also supported with java.util.Base64.getUrlEncoder(), and the "=" padding can be skipped by additionally using the java.util.Base64.Encoder.withoutPadding() method:
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public String encode(String raw) {
return Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(raw.getBytes(StandardCharsets.UTF_8));
}
With the usage of Base64 from Apache Commons, who can be configured to URL safe, I created the following function:
import org.apache.commons.codec.binary.Base64;
public static String base64UrlDecode(String input) {
String result = null;
Base64 decoder = new Base64(true);
byte[] decodedBytes = decoder.decode(input);
result = new String(decodedBytes);
return result;
}
The constructor Base64(true) makes the decoding URL-safe.
In the Android SDK, there's a dedicated flag in the Base64 class: Base64.URL_SAFE, use it like so to decode to a String:
import android.util.Base64;
byte[] byteData = Base64.decode(body, Base64.URL_SAFE);
str = new String(byteData, "UTF-8");
Guava now has Base64 decoding built in.
https://google.github.io/guava/releases/17.0/api/docs/com/google/common/io/BaseEncoding.html
public static byte[] encodeUrlSafe(byte[] data) {
byte[] encode = Base64.encode(data);
for (int i = 0; i < encode.length; i++) {
if (encode[i] == '+') {
encode[i] = '-';
} else if (encode[i] == '/') {
encode[i] = '_';
}
}
return encode;
}
public static byte[] decodeUrlSafe(byte[] data) {
byte[] encode = Arrays.copyOf(data, data.length);
for (int i = 0; i < encode.length; i++) {
if (encode[i] == '-') {
encode[i] = '+';
} else if (encode[i] == '_') {
encode[i] = '/';
}
}
return Base64.decode(encode);
}
Right off the bat, it looks like your replace() is backwards; that method replaces the occurrences of the first character with the second, not the other way around.
#ufk's answer works, but you don't actually need to set the urlSafe flag when you're just decoding.
urlSafe is only applied to encode operations. Decoding seamlessly
handles both modes.
Also, there are some static helpers to make it shorter and more explicit:
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
public static String base64UrlDecode(String input) {
StringUtils.newStringUtf8(Base64.decodeBase64(input));
}
Docs
newStringUtf8()
decodeBase64()
This class can help:
import android.util.Base64;
public class Encryptor {
public static String encode(String input) {
return Base64.encodeToString(input.getBytes(), Base64.URL_SAFE);
}
public static String decode(String encoded) {
return new String(Base64.decode(encoded.getBytes(), Base64.URL_SAFE));
}
}
I know the answer is already there, but still, if someone wants...
import java.util.Base64; public
class Base64BasicEncryptionExample {
publicstaticvoid main(String[] args) {
// Getting encoder
Base64.Encoder encoder = Base64.getUrlEncoder();
// Encoding URL
String eStr = encoder.encodeToString
("http://www.javatpoint.com/javatutorial/".getBytes());
System.out.println("Encoded URL: "+eStr);
// Getting decoder
Base64.Decoder decoder = Base64.getUrlDecoder();
// Decoding URl
String dStr = new String(decoder.decode(eStr));
System.out.println("Decoded URL: "+dStr);
}
}
Took help from: https://www.javatpoint.com/java-base64-encode-decode
In Java try the method Base64.encodeBase64URLSafeString() from Commons Codec library for encoding.