Convert byte[] to String Java Android Encryption - java

I am using the JNCryptor library to encrypt a string before sending it to my server as an encrypted string. Here is my code:
String teststring = "Hello World";
JNCryptor cryptor = new AES256JNCryptor();
byte[] plaintext = teststring.getBytes();
String password = "test";
try {
byte[] ciphertext = cryptor.encryptData(plaintext, password.toCharArray());
String a = new String(ciphertext);
return a;
} catch (CryptorException e) {
// Something went wrong
e.printStackTrace();
return "0";
}
However, when I send my string "a" to the server, it has a bunch of unrecognizable characters. I read an explanation
regarding this:
String is not a suitable container for binary data and ciphertext is
binary data. For any given character encoding not all bytes and byte
sequences represents characters and when an un-representable byte or
sequence is found it is converted to some error character. Obviously
this error character cannot be converted back to a unique byte or byte
sequence (it is a many->one mapping).
Is this advice correct? In that case, how do I convert the byte[] to a string correctly? So that I can readably store it on my server?

There is no standard way for converting from a byte array to a string. You have to encode the byte array. A common way to do this is base64 encoding.
For an explanation of how base64 encoding works: http://en.wikipedia.org/wiki/Base64
Then once it gets to your server, base64 decode it back into your original byte array and store it, done!

Related

PHP vs Java: Base64 en-/decoding of special characters

I've got an encrypted string in PHP and need to send this in an HTTP request. So I decide to use Base64 encoding and URL encoding. The received is a Java module; but when I use the Base64 decoder, it shows me a completely different string. How can I get the original encrypted string back. I need actually to pick each character from that original string and use its corresponding ASCII code for decryption.
// PHP code
$encrypted = "©’Ÿ£ šd¤¨m";
$base64 = base64_encode($string);
$parameter = urlencode($base64);
// this returns eventually: qZKfo6CaZKSobQ%3D%3D
// Java code
// FYI: the variable "name" is a string that the Java software extracts from the posted data.
String base64 = java.net.URLDecoder.decode(name, "UTF-8");
byte[] bytes = java.util.Base64.getDecoder().decode(base64);
String encrypted = new String(bytes);
// This returns: ������d��m

String decrypted by vb.net function is different than the original String encrypted by java function

I have an Android app that uses a webservice in an asp.net web application. This web service requires a username and encrypted password.
The problem is that the password decrypted by the vb.net function is not the same that the original password encrypted by the java function.
These are the functions:
java
public String encrypt(String password, String key, String VecI) throws GeneralSecurityException, UnsupportedEncodingException{
byte[] sessionKey = key.getBytes();
byte[] iv = VecI.getBytes() ;
byte[] plaintext = password.getBytes();
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sessionKey, "DES"), new IvParameterSpec(iv));
byte[] ciphertext = cipher.doFinal(plaintext);
String resp = ciphertext.toString();
return resp;
}
vb.net
Public Shared Function decrypt(Byval encrypted_password As String, ByVal key As String, ByVal VecI As String) As String
Dim plaintext() As Byte= Convert.FromBase64String(encrypted_password)
Dim keys() As Byte = Encoding.ASCII.GetBytes(key)
Dim memdata As New MemoryStream
Dim transforma As ICryptoTransform
Dim des As New DESCryptoServiceProvider
des.Mode = CipherMode.CBC
transforma = des.CreateEncryptor(keys, Encoding.ASCII.GetBytes(VecI))
Dim encstream As New CryptoStream(memdata, transforma, CryptoStreamMode.Write)
encstream.Write(plaintext, 0, plaintext.Length)
encstream.FlushFinalBlock()
encstream.Close()
Return Encoding.ASCII.GetString(memdata.ToArray)
End Function
Please, help me.
Thank`s.
Server side you're expecting a Base64 encoded string, which you're then converting to a byte array:
Dim plaintext() As Byte= Convert.FromBase64String(encrypted_password)
You should be doing the opposite of this on Android (converting the byte array to a Base64 string). Instead you're just calling toString on a byte[], which definitely won't give you what you want.
You can get the Base64 encoded string from your byte array as follows:
String resp = Base64.encode(cipherText, Base64.DEFAULT);
You can have a look at the Base64 docs for more information. You may have to play around with the flags supplied.
Another thing to consider is the fact that you're expecting everything to be in ASCII encoding. String.getBytes() returns characters encoded using the systems default charset. Passwords may be input using non-ASCII characters, such as é or ¥, and this could introduce subtle bugs. I'd recommend switching everything over to UTF-8 - server-side and client side (that means changing getBytes() to getBytes("UTF-8"), and Encoding.ASCII to Encoding.UTF8).
As an aside, creating a String from a byte array in Java is done using new String(someByteArray, "UTF-8") - or whatever the encoding you're using is.

Cannot output correct hash in Java. What is wrong?

In my Android app I have a SHA256 hash which I must further hash with the RIPEMD160 message digest algorithm.
I can output the correct sha256 and ripemd160 hash of any string, but when I try to hash the sha256 hash with ripemd160 I get a hash which is incorrect.
According to online hash calculators, the SHA256 value of the string 'test'(all lowercase) is:
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
And the RIPEMD160 value of the string 'test' is:
5e52fee47e6b070565f74372468cdc699de89107
The value from hashing the resulting sha256 hash with ripemd160 according to online calcs is:
4efc1c36d3349189fb3486d2914f56e05d3e66f8
And the one my app gives me is:
cebaa98c19807134434d107b0d3e5692a516ea66
which is obviously wrong.
Here is my code:
public static String toRIPEMD160(String in)
{
byte[] addr = in.getBytes();
byte[] out = new byte[20];
RIPEMD160Digest digest = new RIPEMD160Digest();
byte[] sha256 = sha256(addr);
digest.update(sha256,0,sha256.length);
digest.doFinal(out,0);
return getHexString(out);
}
public static byte[] sha256(byte[] data)
{
byte[] sha256 = new byte[32];
try
{
sha256 = MessageDigest.getInstance("SHA-256").digest(data);
}
catch(NoSuchAlgorithmException e)
{}
return sha256;
}
For the ripemd160 algorithm, you need bouncycastle and java.security.MessageDigest for sha256.
Your "online calculator" result is the result of hashing the bytes of the string "test" with SHA-256, converting the result of that hash to a hex string, then taking the bytes corresponding to the ASCII characters of that hex string and hashing those a second time. This is very different from your Java code, which passes the bytes that come out of the first hash directly to the second one, without printing them as hex and turning those characters back into bytes in between. The single byte with value 254 (decimal) becomes "fe" in hex, which becomes the two-byte sequence [0x66, 0x65] when converted back to bytes.
Your hash is working fine. The problem is that the online calculators that you're using are treating your input:
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
as a string instead of an array of bytes. In other words, it's treating each character as a byte instead of parsing character pairs as bytes in hexadecimal. If I give this as a string to online calculators, I indeed get exactly what you got:
4efc1c36d3349189fb3486d2914f56e05d3e66f8
However, you're treating the output as an array of bytes instead of a String and that's giving you different results. You should encode your raw SHA256 hash as a string, then pass the encoded string to the hash function. I see you have a getHexString method, so we'll just use that.
public static String toRIPEMD160(String in) {
try {
byte[] addr = in.getBytes();
byte[] out = new byte[20];
RIPEMD160Digest digest = new RIPEMD160Digest();
// These are the lines that changed
byte[] rawSha256 = sha256(addr);
String encodedSha256 = getHexString(rawSha256);
byte[] strBytes = encodedSha256.getBytes("UTF-8");
digest.update(strBytes, 0, strBytes.length);
digest.doFinal(out, 0);
return getHexString(out);
} catch (UnsupportedEncodingException ex) {
// Never happens, everything supports UTF-8
return null;
}
}
If you want to know it's working, take the value of encodedSha256 and put that into an online hash calculator. As long as the calculator uses UTF-8 encoding to turn the string into a byte array, it will match your output.
To get printable version of byte[] digest use this code:
StringBuffer hexString = new StringBuffer();
for (int i=0;i<out.length;i++) {
hexString.append( String.format("%02x", 0xFF & out[i]) );
}
and then call hexString.toString();

cookie encryption

how to encrypt cookies as it accepts only US-ASCII characters? when i use below code, it generates characters out of US_ASCII. how to limit encrypted characters not to go out of US_ASCII?
key = KeyGenerator.getInstance(algorithm).generateKey();
cipher = Cipher.getInstance("DESede");
messageSource.getMessage("encryption.algorithm",null,localeUtil.getLocale());
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] outputBytes = cipher.doFinal(input.getBytes());
Firstly: don't use String.getBytes without specifying an encoding, preferably something which can handle all of Unicode, such as UTF-8.
Secondly: the code you've given doesn't generate characters at all - it generates bytes. You mustn't treat those bytes as if they're encoded text - they're not. Instead, you should convert them to base64, e.g. with Apache Commons Codec or this public domain code.
Then when you need to decrypt, you simply reverse all the steps - convert back from base64 to binary, decrypt, and then create a string with new String(decryptedBytes, "UTF-8") (or whatever encoding you decided to use).
public static final String CHARSET = "ISO-8859-1";
String text = "Ángel";
String encodedText = new String(Base64.encodeBase64(text.getBytes(CHARSET)));
String decodedText = new String(Base64.decodeBase64(encodedText.getBytes(CHARSET)))
In addition to Jon's answer, you can view the available encodings here:
http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html

Java String to SHA1

I'm trying to make a simple String to SHA1 converter in Java and this is what I've got...
public static String toSHA1(byte[] convertme) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-1");
}
catch(NoSuchAlgorithmException e) {
e.printStackTrace();
}
return new String(md.digest(convertme));
}
When I pass it toSHA1("password".getBytes()), I get [�a�ɹ??�%l�3~��. I know it's probably a simple encoding fix like UTF-8, but could someone tell me what I should do to get what I want which is 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8? Or am I doing this completely wrong?
UPDATE
You can use Apache Commons Codec (version 1.7+) to do this job for you.
DigestUtils.sha1Hex(stringToConvertToSHexRepresentation)
Thanks to #Jon Onstott for this suggestion.
Old Answer
Convert your Byte Array to Hex String. Real's How To tells you how.
return byteArrayToHexString(md.digest(convertme))
and (copied from Real's How To)
public static String byteArrayToHexString(byte[] b) {
String result = "";
for (int i=0; i < b.length; i++) {
result +=
Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
}
return result;
}
BTW, you may get more compact representation using Base64. Apache Commons Codec API 1.4, has this nice utility to take away all the pain. refer here
This is my solution of converting string to sha1. It works well in my Android app:
private static String encryptPassword(String password)
{
String sha1 = "";
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(password.getBytes("UTF-8"));
sha1 = byteToHex(crypt.digest());
}
catch(NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch(UnsupportedEncodingException e)
{
e.printStackTrace();
}
return sha1;
}
private static String byteToHex(final byte[] hash)
{
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
Using Guava Hashing class:
Hashing.sha1().hashString( "password", Charsets.UTF_8 ).toString()
SHA-1 (and all other hashing algorithms) return binary data. That means that (in Java) they produce a byte[]. That byte array does not represent any specific characters, which means you can't simply turn it into a String like you did.
If you need a String, then you have to format that byte[] in a way that can be represented as a String (otherwise, just keep the byte[] around).
Two common ways of representing arbitrary byte[] as printable characters are BASE64 or simple hex-Strings (i.e. representing each byte by two hexadecimal digits). It looks like you're trying to produce a hex-String.
There's also another pitfall: if you want to get the SHA-1 of a Java String, then you need to convert that String to a byte[] first (as the input of SHA-1 is a byte[] as well). If you simply use myString.getBytes() as you showed, then it will use the platform default encoding and as such will be dependent on the environment you run it in (for example it could return different data based on the language/locale setting of your OS).
A better solution is to specify the encoding to use for the String-to-byte[] conversion like this: myString.getBytes("UTF-8"). Choosing UTF-8 (or another encoding that can represent every Unicode character) is the safest choice here.
This is a simple solution that can be used when converting a string to a hex format:
private static String encryptPassword(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(password.getBytes("UTF-8"));
return new BigInteger(1, crypt.digest()).toString(16);
}
Just use the apache commons codec library. They have a utility class called DigestUtils
No need to get into details.
As mentioned before use apache commons codec. It's recommended by Spring guys as well (see DigestUtils in Spring doc). E.g.:
DigestUtils.sha1Hex(b);
Definitely wouldn't use the top rated answer here.
It is not printing correctly because you need to use Base64 encoding. With Java 8 you can encode using Base64 encoder class.
public static String toSHA1(byte[] convertme) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
return Base64.getEncoder().encodeToString(md.digest(convertme));
}
Result
This will give you your expected output of 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
Message Digest (hash) is byte[] in byte[] out
A message digest is defined as a function that takes a raw byte array and returns a raw byte array (aka byte[]). For example SHA-1 (Secure Hash Algorithm 1) has a digest size of 160 bit or 20 byte. Raw byte arrays cannot usually be interpreted as character encodings like UTF-8, because not every byte in every order is an legal that encoding. So converting them to a String with:
new String(md.digest(subject), StandardCharsets.UTF_8)
might create some illegal sequences or has code-pointers to undefined Unicode mappings:
[�a�ɹ??�%l�3~��.
Binary-to-text Encoding
For that binary-to-text encoding is used. With hashes, the one that is used most is the HEX encoding or Base16. Basically a byte can have the value from 0 to 255 (or -128 to 127 signed) which is equivalent to the HEX representation of 0x00-0xFF. Therefore hex will double the required length of the output, that means a 20 byte output will create a 40 character long hex string, e.g.:
2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
Note that it is not required to use hex encoding. You could also use something like base64. Hex is often preferred because it is easier readable by humans and has a defined output length without the need for padding.
You can convert a byte array to hex with JDK functionality alone:
new BigInteger(1, token).toString(16)
Note however that BigInteger will interpret given byte array as number and not as a byte string. That means leading zeros will not be outputted and the resulting string may be shorter than 40 chars.
Using Libraries to Encode to HEX
You could now copy and paste an untested byte-to-hex method from Stack Overflow or use massive dependencies like Guava.
To have a go-to solution for most byte related issues I implemented a utility to handle these cases: bytes-java (Github)
To convert your message digest byte array you could just do
String hex = Bytes.wrap(md.digest(subject)).encodeHex();
or you could just use the built-in hash feature
String hex = Bytes.from(subject).hashSha1().encodeHex();
Base 64 Representation of SHA1 Hash:
String hashedVal = Base64.getEncoder().encodeToString(DigestUtils.sha1(stringValue.getBytes(Charset.forName("UTF-8"))));
Convert byte array to hex string.
public static String toSHA1(byte[] convertme) {
final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-1");
}
catch(NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] buf = md.digest(convertme);
char[] chars = new char[2 * buf.length];
for (int i = 0; i < buf.length; ++i) {
chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
}
return new String(chars);
}
The reason this doesn't work is that when you call String(md.digest(convertme)), you are telling Java to interpret a sequence of encrypted bytes as a String. What you want is to convert the bytes into hexadecimal characters.
Maybe this helps (works on java 17):
import org.apache.tomcat.util.codec.binary.Base64;
return new String(Base64.encodeBase64(md.digest(convertme)));

Categories