I need to convert java code for encryption and decryption using AES/CBC/PKCS5Padding algorithm to dart code.
The java code of AES/CBC/PKCS5Padding encryption and decryption:
package test_Terminal.classes;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
*
* #author jeena
*/
public class IOTEncodingDecoding {
SecretKeySpec secretKeySpec;
IvParameterSpec ivSpec;
String EncryptionKey = "733D3A17-D8A0-454B-AD22-88608FD0C46A";
String saltString = "FA9A4D0F-5523-4EEF-B226-9A3E8F14FEF8";
String algorithm = "AES/CBC/PKCS5Padding";
int encoding_mode;
test_Terminal.classes.general General = new test_Terminal.classes.general();
void setSecretKey() {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(EncryptionKey.toCharArray(), saltString.getBytes(StandardCharsets.UTF_16LE), 1000, 384);
byte[] derivedData = factory.generateSecret(pbeKeySpec).getEncoded();
byte[] key = new byte[32];
byte[] iv = new byte[16];
System.arraycopy(derivedData, 0, key, 0, key.length);
System.arraycopy(derivedData, key.length, iv, 0, iv.length);
secretKeySpec = new SecretKeySpec(key, "AES");
ivSpec = new IvParameterSpec(iv);
} catch (Exception e) {
General.LogException("setSecretKey", e);
}
}
public String encrypt(String input) {
try {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] cipherText ;
if(encoding_mode==1)
cipherText = cipher.doFinal(input.getBytes(StandardCharsets.UTF_16LE));
else
cipherText = cipher.doFinal(input.getBytes());
return Base64.getEncoder().encodeToString(cipherText);
} catch (Exception e) {
General.LogException("encrypt", e);
}
return "";
}
public String decrypt(String cipherText) {
try {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText));
if(encoding_mode==1)
return new String(plainText, StandardCharsets.UTF_16LE);
else
return new String(plainText);
} catch (Exception e) {
General.LogException("decrypt", e);
General.LogActivity("decrypt", e.getMessage());
}
return "Ticket format error";
}
public void setMode() {
setSecretKey();
}
}
I need to get the following result:
Input(PlainText):C123492349C1CT20230206130645.
Output(Encrypted string):8tyHRaQCsxmmGW2xPBFYx/PALmvHkmjx/TzaXC2rIv0=
This is the dart code that I've got so far for decryption, but I'm getting error.
Uint8List? decrypt(String ciphertext, String password) {
Uint8List rawCipher = base64.decode(ciphertext);
var salt = rawCipher.sublist(0, 0 + 8);
var iv = rawCipher.sublist(8, 8 + 16);
var encrypted = rawCipher.sublist(8 + 16);
Uint8List key = generateKey(password, salt);
print('key => $key');
CBCBlockCipher cipher = CBCBlockCipher(AESEngine());
ParametersWithIV<KeyParameter> params =
ParametersWithIV<KeyParameter>(KeyParameter(key), iv);
PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null>
paddingParams =
PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null>(
params, null);
PaddedBlockCipherImpl paddingCipher =
PaddedBlockCipherImpl(PKCS7Padding(), cipher);
paddingCipher.init(false, paddingParams);
var val = paddingCipher.process(encrypted);
String res = String.fromCharCodes(val);
debugPrint('res => $res');
return val;
}
Uint8List generateKey(String passphrase, Uint8List salt) {
final derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64))
..init(Pbkdf2Parameters(salt, 1024, 16));
return derivator.process(utf8.encode(passphrase) as Uint8List);
}
I got this code from
The Exception that I'm getting is:
Exception has occurred.
ArgumentError (Invalid argument(s): Input data length must be a multiple of cipher's block size)
I think the values inside rawCipher.sublist() function is wrong. I'm stuck on this problem for few days, please help.
Both codes differ:
Regarding encodings: The Dart code does not consider the UTF-16 LE encoding of the salt. Furthermore, the encoding of the plaintext is unclear. For encoding_mode==1 it is UTF-16LE, otherwise it corresponds to the platform encoding in your environment (which only you know).
Regarding PBKDF2: The Java code derives key and IV from a static salt (note that a static salt is a vulnerability), while the Dart code assumes a concatenation in the order salt|IV|ciphertext during encryption (using a random 8 bytes salt and a random IV).
Also, different iteration counts are used: 1000 in the Java code, 1024 in the Dart code (note that both values are generally much too small for PBKDF2).
The differences can be fixed as follows:
Regarding encodings: In the Dart code, the salt must first be UTF-16 LE encoded: Since the utf package is deprecated, see e.g. here for a UTF-16 LE encoding and here for the decoding. The encoding can be adapted to:
Uint8List encodeUtf16LE(String salt) {
var byteData = ByteData(salt.codeUnits.length * 2);
for (var i = 0; i < salt.codeUnits.length; i += 1) {
byteData.setUint16(i * 2, salt.codeUnits[i], Endian.little);
}
return byteData.buffer.asUint8List();
}
Moreover, from the sample data it can be concluded (by testing) that the plaintext in the Java code has been encoded with UTF-8.
Regarding PBKDF2: In the Dart code, key and IV must be derived from the static salt applied in the Java code.
Also, the parameters from the Java code must be applied (digest: SHA-1, iteration count: 1000, keysize: 32 + 16 = 48 bytes):
Uint8List generateKey(String passphrase, Uint8List salt) {
final derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64))
..init(Pbkdf2Parameters(salt, 1000, 32 + 16));
return derivator.process(utf8.encode(passphrase) as Uint8List);
}
With these changes, key and IV can be derived as follows:
var salt = "FA9A4D0F-5523-4EEF-B226-9A3E8F14FEF8";
var passphrase = "733D3A17-D8A0-454B-AD22-88608FD0C46A";
var saltBytes = encodeUtf16LE(salt);
var keyIv = generateKey(passphrase, saltBytes);
var key = keyIv.sublist(0, 32);
var iv = keyIv.sublist(32, 32 + 16);
The decryption code can be applied unchanged, for decoding use utf8.decode() instead of String.fromCharCodes().
import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
...
var ciphertext = "8tyHRaQCsxmmGW2xPBFYx/PALmvHkmjx/TzaXC2rIv0=";
var encrypted = base64.decode(ciphertext);
var paddingCipher = PaddedBlockCipherImpl(PKCS7Padding(), CBCBlockCipher(AESEngine()))
..init(false, PaddedBlockCipherParameters(ParametersWithIV(KeyParameter(key), iv), null));
var decryptedBytes = paddingCipher.process(encrypted);
var decrypted = utf8.decode(decryptedBytes); // C123492349C1CT20230206130645
I need to translate a short code in java to python 3.
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class DesEncrypterPdys {
private static DesEncrypterPdys desEncrypter;
private static Cipher ecipher;
private static Cipher dcipher;
private DesEncrypterPdys() throws DesEncrypterException{
try {
if(ecipher == null || dcipher == null){
String cryptoKey;
cryptoKey = "RAMPOLO S.O. Plokity Lopiokiujhygh ;)";
SecretKey k = SecretKeyFactory.getInstance("DES").generateSecret(new DESKeySpec(cryptoKey.getBytes()));
ecipher = Cipher.getInstance("DES");
dcipher = Cipher.getInstance("DES");
ecipher.init(Cipher.ENCRYPT_MODE, k);
dcipher.init(Cipher.DECRYPT_MODE, k);
}
}catch (Exception e) {
throw new DesEncrypterException(e);
}
}
public static DesEncrypterPdys getInstance() throws DesEncrypterException{
if (desEncrypter == null || ecipher == null || dcipher == null) {
desEncrypter = new DesEncrypterPdys();
}
return desEncrypter;
}
public String encrypt(String str) throws DesEncrypterException{
try {
if(str == null) return null;
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
} catch (javax.crypto.BadPaddingException e) {
throw new DesEncrypterException(e);
} catch (IllegalBlockSizeException e) {
throw new DesEncrypterException(e);
}
catch (java.io.IOException e) {
throw new DesEncrypterException(e);
}
}
public String decrypt(String str) throws DesEncrypterException{
try {
if(str == null) return null;
// Decode base64 to get bytes
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (javax.crypto.BadPaddingException e) {
throw new DesEncrypterException(e);
} catch (IllegalBlockSizeException e) {
throw new DesEncrypterException(e);
} catch (java.io.IOException e) {
throw new DesEncrypterException(e);
}
}
}
Im tried to use something like this:
import pyDes as pds
import base64 as b64
cryptoKey = "RAMPOLO S.O. Plokity Lopiokiujhygh ;)";
data = b" " # here should be a data for decrypting
k = pds.des(cryptoKey, pds.ECB, b"\0\0\0\0\0\0\0\0", pad=None, padmode=pds.PAD_PKCS5)
d = k.encrypt(data)
print("Encrypted: %r" % d)
print("Decrypted: %r" % k.decrypt(d))
print(b64.b64encode(d))
but unfortunately no luck:/
What I want to receive is a short program to encrypt and decode passwords. unfortunately I encountered a problem with too long cryptokey(?). Once I was able to translate an analogous piece of code but it was probably a matter of luck that old case was corresponded to the case from the pydes documentation. Now is different...
Can some one help me with translation this java code to python?
When running your code the error returned is:
>>> import pyDes as pds
>>> import base64 as b64
>>> cryptoKey = "RAMPOLO S.O. Plokity Lopiokiujhygh ;)"
>>> data = b"qwertyuiop" # Random gibberish
>>> k = pds.des(cryptoKey, pds.ECB, b"\0\0\0\0\0\0\0\0", pad=None, padmode=pds.PAD_PKCS5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/pyDes.py", line 400, in __init__
raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
ValueError: Invalid DES key size. Key must be exactly 8 bytes long.
if you take a look at the Github documentation for pyDES it specifies that for DES encryption the key should be 8bytes long, yours is way longer. You need to use a shorter key, then it works fine.
>>> k = pds.des('12345678', pds.ECB, b"\0\0\0\0\0\0\0\0", pad=None, padmode=pds.PAD_PKCS5)
>>> k
<pyDes.des object at 0x7fc4ade1f9d0>
Important sidenote, from a security point of view you should avoid using DES as it's completely broken for today's standards, even the Wikipedia page (which is not a great source of cryptographic knowledge) shows that DES breaking is feasible. Also ECB mode is broken as it doesn't provide any security guarantees because each block is encrypted in the exact same way, check this.
I am trying to encrypt a file using the RSA algorithm with a public key (which I build using a given modulus and exponent).
My code works fine with Android 4.3, and I get all I need. But I am trying to make it work for other Android versions such (2.3.3) and there is no way. It complains about a "input too large for RSA cipher".
If I have understood the theory well the block cipher size is relative to the publicKey.size(), therefore if this don't change from one version of Android to another I should get the same result, should't I?
Here is my code:
package com.example.rsa_ex;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import android.app.Activity;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
public class MainActivity extends Activity
{
private byte[] mKeyModulus = {...};
private byte[] mKeyExponent = {...};
private String tag = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String token = "";
try
{
token = readTokenFromFile("token.base64");
}
catch (IOException e)
{
Log.d(tag, "Failed to open property file");
}
byte[] encodedBytes = Base64.encode(token.getBytes(), 0);
Log.d(tag, "Encrypt Token"+ onEncrypt(encodedBytes));
}
public String readTokenFromFile(String fileName) throws IOException
{
String token = "empty";
try
{
AssetManager assetManager = getAssets();
InputStream iS = assetManager.open(fileName);
byte[] buffer = new byte[iS.available()];
iS.read(buffer);
ByteArrayOutputStream oS = new ByteArrayOutputStream();
oS.write(buffer);
token = oS.toString();
oS.close();
iS.close();
Log.d(tag, "token ==> "+token);
}
catch (IOException e)
{
Log.d(tag, "Failed to open property file");
}
return token;
} // readTokenFromFile end
public String onEncrypt(byte[] token)
{
Log.d(tag,"onEncrypt entry");
String encryptedTranspherable = "";//null;
// get the publicKey
try
{
BigInteger m = new BigInteger(mKeyModulus);
BigInteger e = new BigInteger(mKeyExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
Key pubKey = fact.generatePublic(new RSAPublicKeySpec(m, e));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted = blockCipher(token,cipher);
encryptedTranspherable = Base64.encodeToString(encrypted, Base64.DEFAULT);
}
catch (Exception e)
{
throw new RuntimeException("serialisation error got issue here !! ==>"+ e.getMessage(), e);
}
return encryptedTranspherable;
}// onEncrypt end
private byte[] blockCipher(byte[] bytes,Cipher cipher) throws IllegalBlockSizeException, BadPaddingException
{
Log.d(tag,"ISecurityProvider blockCipher entry");
Log.d(tag,"ISecurityProvider byteArray =>"+ Arrays.toString(bytes));
byte[] scrambled = new byte[0];
// toReturn will hold the total result
byte[] toReturn = new byte[0];
int length = 256;
// another buffer. this one will hold the bytes that have to be modified in this step
byte[] buffer = new byte[Math.min(bytes.length, length)];//(bytes.length > length ? length : bytes.length)];
for (int i=0; i< bytes.length; i++)
{
// if we filled our buffer array we have our block ready for de- or encryption
if ((i > 0) && (i % length == 0))
{
Log.d(tag,"ISecurityProvider blockCipher processing block i ="+i);
scrambled = cipher.doFinal(buffer);
toReturn = append(toReturn,scrambled);
// here we calculate the length of the next buffer required
int newlength = length;
// if newlength would be longer than remaining bytes in the bytes array we shorten it.
if (i + length > bytes.length)
{
newlength = bytes.length - i;
}
// clean the buffer array
buffer = new byte[newlength];
}
// copy byte into our buffer.
buffer[i%length] = bytes[i];
}
// this step is needed if we had a trailing buffer. should only happen when encrypting.
// example: we encrypt 110 bytes. 100 bytes per run means we "forgot" the last 10 bytes. they are in the buffer array
scrambled = cipher.doFinal(buffer);
// final step before we can return the modified data.
toReturn = append(toReturn,scrambled);
return toReturn;
}
private byte[] append(byte[] prefix, byte[] suffix)
{
byte[] toReturn = new byte[prefix.length + suffix.length];
int prefixSize = prefix.length;
int suffixSize = suffix.length;
for (int i=0; i< prefixSize; i++)
{
toReturn[i] = prefix[i];
}
for (int i=0; i< suffixSize; i++)
{
toReturn[i+prefixSize] = suffix[i];
}
return toReturn;
}
}
Any suggestion or advise will be more than welcome.
Thanks a lot.
From Android 4.2 enhancements:
Cryptography - Modified the default implementations of SecureRandom and Cipher.RSA to use OpenSSL. Added SSL Socket support for TLSv1.1 and TLSv1.2 using OpenSSL 1.0.1
This means that they are using another provider, and probably another default RSA padding mechanism. You should always provide a complete algorithm string and not rely on provider defaults, e.g. try "RSA/ECB/OAEPWithSHA1AndMGF1Padding" for new applications and "RSA/ECB/PKCS1Padding" for compatibility with older libraries.
Note that you usually do not encrypt a file directly using RSA. Usually you encrypt the file with a random data key using e.g. AES, a symmetric cipher. Then you encrypt that AES key with the RSA public key and send the result with the ciphertext. This way you can encrypt an (almost) arbitrary number of bytes. This is called hybrid cryptography.
Using the Apple's Game Center authentication verification steps outlined here, the verification logic below has been implemented using Java. However, this always fails.
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.xml.bind.DatatypeConverter;
public class Verifier {
public static void main(String[] args) {
verify1();
}
public static void verify1() {
try {
byte[] playerID = "G:90082947".getBytes("UTF-8");
byte[] bundleID = "com.appledts.GameCenterSamples".getBytes("UTF-8");
long ts = 1392078336714L;
final ByteBuffer tsByteBuffer = ByteBuffer.allocate(8);
tsByteBuffer.order(ByteOrder.BIG_ENDIAN);
tsByteBuffer.putLong(ts);
byte[] timestamp = tsByteBuffer.array();
byte[] salt = DatatypeConverter.parseBase64Binary("xmvbZQ==");
byte[] sigToCheck = DatatypeConverter.parseBase64Binary("AmyNbm+7wJOjXv6GXI/vAEcl6gSX1AKxPr3GeExSYCiaxVaAeIvC23TWtp1/Vd/szfq1r1OzwrvkHeSSiskWMsMXaGQWUmiGtCnf9fqBU75T5PwNLCj4H9Nd5QENCMV/CFgVyGEi4X6Wlp18kqJPk/ooS6jLJwcWIe6DyrR1bQHl6YzKTfB4ACl2JEccBDz8dArKTrh4vFcQF4a+DtERm283Y2ue1DwG8lqWrYhsRO5v7vrW3lVpn5t25QXc+Y35zJ/il+lZJxKAgASwrKaq3G8RStdkeXCER23fSYhTmbLFqkFRWnmzu38hmLt5/iivUbm8NgELXP0SyQoYLMvfmA==");
ByteBuffer dataBuffer = ByteBuffer.allocate(playerID.length+bundleID.length+8+salt.length)
.put(playerID)
.put(bundleID)
.put(timestamp)
.put(salt);
Certificate cert = CertificateFactory.getInstance("X.509")
.generateCertificate(new URL("https://sandbox.gc.apple.com/public-key/gc-sb.cer").openConnection().getInputStream());
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(cert);
sig.update(dataBuffer);
final boolean verify = sig.verify(sigToCheck);
System.out.println("signature verifies: " + verify);
} catch (Exception e) {
e.printStackTrace();
}
}
}
There were no loss of bits in transferring data from the iOS 7 client to the server. This was verified by writing the binary bits to a file both from xCode and Java, generating their hex, and seeing if there were any diffs (note, the diffs just show the file name diffs):
$ xxd -i salt_Java.txt salt_java.xxd
$ xxd -i salt_xcode.txt salt_xcode.xxd
$ xxd -i sigToCheck_Java.txt sigToCheck_java.xxd
$ xxd -i sigToCheck_xcode.txt sigToCheck_xcode.xxd
$ diff salt_java.xxd salt_xcode.xxd
1c1
< unsigned char salt_Java_txt[] = {
---
> unsigned char salt_xcode_txt[] = {
4c4
< unsigned int salt_Java_txt_len = 4;
---
> unsigned int salt_xcode_txt_len = 4;
$ diff sigToCheck_java.xxd sigToCheck_xcode.xxd
1c1
< unsigned char sigToCheck_Java_txt[] = {
---
> unsigned char sigToCheck_xcode_txt[] = {
25c25
< unsigned int sigToCheck_Java_txt_len = 256;
---
> unsigned int sigToCheck_xcode_txt_len = 256;
$
I believe this fails because of the underlying Java libraries that Signature class uses, since the Objective-C solution listed here appears to successfully verify the same credentials.
My next attempt was to use the Java's [Cipher] and [MessageDigest] libraries instead of the [Signature] library, but this too fails. I suspect there are other steps missing before the signature digest bits can be checked with the provided signature bits.
final MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] digest = md.digest(dataBuffer.array());
// RSA decrypt
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, cert);
byte[] decrypted = cipher.doFinal(sigToCheck);
System.out.println("signature verifies: " + Arrays.equals(digest, decrypted));
Are there alternatives to verifying the digital signature or any gaps in the solutions posted above?
The problem appears to be with the ByteBuffer you're passing to Signature.update(). If you pass the underlying array by changing
sig.update(dataBuffer);
to
sig.update(dataBuffer.array());
the verification appears to succeed. Based on the documentation for Signature.update(ByteBuffer), I suspect it's because it's trying to read from the last position you wrote to in the buffer, and not finding any data.
Still not sure why Signature.verify fails, but found a work around for now: decrypt the signature to check, and unpad the SHA1 hash from the decrypted hash, and compare with the data buffer digest. If the two match, then it validates the game center user credentials, otherwise it does not. See sample code below.
final MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(dataBuffer.array());
Cipher c2 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c2.init(Cipher.DECRYPT_MODE, cert.getPublicKey());
byte[] decrypted2 = c2.doFinal(sigToCheck);
final byte[] unpaddedSHA1 = Utils.unpadSHA1(decrypted2);
System.out.println("signature verifies: " + Arrays.equals(digest, unpaddedSHA1));
Where the upadSHA1 is defined as follows:
private static final String SHA1_PAD = "3021300906052b0e03021a05000414";
private static final byte[] sha1pad = DatatypeConverter.parseHexBinary(SHA1_PAD);
public static byte[] unpadSHA1(byte[] padded) throws BadPaddingException {
int k = 0;
if (padded.length < sha1pad.length) {
throw new BadPaddingException("Padding string too short");
}
while (true) {
if (padded[k] != sha1pad[k]) {
break;
}
k++;
if (k == sha1pad.length) {
break;
}
}
int n = padded.length - k;
if (n > 256) {
throw new BadPaddingException("Padding string too short");
}
byte[] data = new byte[n];
System.arraycopy(padded, padded.length - n, data, 0, n);
return data;
}
Is there any method to generate MD5 hash of a string in Java?
The MessageDigest class can provide you with an instance of the MD5 digest.
When working with strings and the crypto classes be sure to always specify the encoding you want the byte representation in. If you just use string.getBytes() it will use the platform default. (Not all platforms use the same defaults)
import java.security.*;
..
byte[] bytesOfMessage = yourString.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] theMD5digest = md.digest(bytesOfMessage);
If you have a lot of data take a look at the .update(xxx) methods which can be called repeatedly. Then call .digest() to obtain the resulting hash.
You need java.security.MessageDigest.
Call MessageDigest.getInstance("MD5") to get a MD5 instance of MessageDigest you can use.
The compute the hash by doing one of:
Feed the entire input as a byte[] and calculate the hash in one operation with md.digest(bytes).
Feed the MessageDigest one byte[] chunk at a time by calling md.update(bytes). When you're done adding input bytes, calculate the hash with
md.digest().
The byte[] returned by md.digest() is the MD5 hash.
If you actually want the answer back as a string as opposed to a byte array, you could always do something like this:
String plaintext = "your text here";
MessageDigest m = MessageDigest.getInstance("MD5");
m.reset();
m.update(plaintext.getBytes());
byte[] digest = m.digest();
BigInteger bigInt = new BigInteger(1,digest);
String hashtext = bigInt.toString(16);
// Now we need to zero pad it if you actually want the full 32 chars.
while(hashtext.length() < 32 ){
hashtext = "0"+hashtext;
}
You might also want to look at the DigestUtils class of the apache commons codec project, which provides very convenient methods to create MD5 or SHA digests.
Found this:
public String MD5(String md5) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(md5.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < array.length; ++i) {
sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3));
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException e) {
}
return null;
}
on the site below, I take no credit for it, but its a solution that works!
For me lots of other code didnt work properly, I ended up missing 0s in the hash.
This one seems to be the same as PHP has.
source: http://m2tec.be/blog/2010/02/03/java-md5-hex-0093
Here is how I use it:
final MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(string.getBytes(Charset.forName("UTF8")));
final byte[] resultByte = messageDigest.digest();
final String result = new String(Hex.encodeHex(resultByte));
where Hex is: org.apache.commons.codec.binary.Hex from the Apache Commons project.
I've found this to be the most clear and concise way to do it:
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(StandardCharsets.UTF_8.encode(string));
return String.format("%032x", new BigInteger(1, md5.digest()));
I just downloaded commons-codec.jar and got perfect php like md5. Here is manual.
Just import it to your project and use
String Url = "your_url";
System.out.println( DigestUtils.md5Hex( Url ) );
and there you have it.
No need to make it too complicated.
DigestUtils works fine and makes you comfortable while working with md5 hashes.
DigestUtils.md5Hex(_hash);
or
DigestUtils.md5(_hash);
Either you can use any other encryption methods such as sha or md.
Found this solution which is much cleaner in terms of getting a String representation back from an MD5 hash.
import java.security.*;
import java.math.*;
public class MD5 {
public static void main(String args[]) throws Exception{
String s="This is a test";
MessageDigest m=MessageDigest.getInstance("MD5");
m.update(s.getBytes(),0,s.length());
System.out.println("MD5: "+new BigInteger(1,m.digest()).toString(16));
}
}
The code was extracted from here.
Another implementation:
import javax.xml.bind.DatatypeConverter;
String hash = DatatypeConverter.printHexBinary(
MessageDigest.getInstance("MD5").digest("SOMESTRING".getBytes("UTF-8")));
Another option is to use the Guava Hashing methods:
Hasher hasher = Hashing.md5().newHasher();
hasher.putString("my string");
byte[] md5 = hasher.hash().asBytes();
Handy if you are already using Guava (which if you're not, you probably should be).
I have a Class (Hash) to convert plain text in hash in formats: md5 or sha1, simillar that php functions (md5, sha1):
public class Hash {
/**
*
* #param txt, text in plain format
* #param hashType MD5 OR SHA1
* #return hash in hashType
*/
public static String getHash(String txt, String hashType) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance(hashType);
byte[] array = md.digest(txt.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < array.length; ++i) {
sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3));
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException e) {
//error action
}
return null;
}
public static String md5(String txt) {
return Hash.getHash(txt, "MD5");
}
public static String sha1(String txt) {
return Hash.getHash(txt, "SHA1");
}
}
Testing with JUnit and PHP
PHP Script:
<?php
echo 'MD5 :' . md5('Hello World') . "\n";
echo 'SHA1:' . sha1('Hello World') . "\n";
Output PHP script:
MD5 :b10a8db164e0754105b7a99be72e3fe5
SHA1:0a4d55a8d778e5022fab701977c5d840bbc486d0
Using example and Testing with JUnit:
public class HashTest {
#Test
public void test() {
String txt = "Hello World";
assertEquals("b10a8db164e0754105b7a99be72e3fe5", Hash.md5(txt));
assertEquals("0a4d55a8d778e5022fab701977c5d840bbc486d0", Hash.sha1(txt));
}
}
Code in GitHub
https://github.com/fitorec/java-hashes
My not very revealing answer:
private String md5(String s) {
try {
MessageDigest m = MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
BigInteger i = new BigInteger(1,m.digest());
return String.format("%1$032x", i);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
There is a DigestUtils class in Spring also:
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/util/DigestUtils.html
This class contains the method md5DigestAsHex() that does the job.
You can try following. See details and download codes here: http://jkssweetlife.com/java-hashgenerator-md5-sha-1/
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Example {
public static void main(String[] args) throws Exception {
final String inputString = "Hello MD5";
System.out.println("MD5 hex for '" + inputString + "' :");
System.out.println(getMD5Hex(inputString));
}
public static String getMD5Hex(final String inputString) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(inputString.getBytes());
byte[] digest = md.digest();
return convertByteToHex(digest);
}
private static String convertByteToHex(byte[] byteData) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
}
Bombe's answer is correct, however note that unless you absolutely must use MD5 (e.g. forced on you for interoperability), a better choice is SHA1 as MD5 has weaknesses for long term use.
I should add that SHA1 also has theoretical vulnerabilities, but not as severe. The current state of the art in hashing is that there are a number of candidate replacement hash functions but none have yet emerged as the standard best practice to replace SHA1. So, depending on your needs you would be well advised to make your hash algorithm configurable so it can be replaced in future.
Another implementation: Fast MD5 Implementation in Java
String hash = MD5.asHex(MD5.getHash(new File(filename)));
I do not know if this is relevant for anyone reading this, but I just had the problem that I wanted to
download a file from a given URL and
compare its MD5 to a known value.
I wanted to do it with JRE classes only (no Apache Commons or similar). A quick web search did not show me sample code snippets doing both at the same time, only each task separately. Because this requires to read the same file twice, I figured it might be worth the while to write some code which unifies both tasks, calculating the checksum on the fly while downloading the file. This is my result (sorry if it is not perfect Java, but I guess you get the idea anyway):
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.security.DigestOutputStream; // new
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
void downloadFile(String fromURL, String toFile, BigInteger md5)
throws IOException, NoSuchAlgorithmException
{
ReadableByteChannel in = Channels.newChannel(new URL(fromURL).openStream());
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
WritableByteChannel out = Channels.newChannel(
//new FileOutputStream(toFile)); // old
new DigestOutputStream(new FileOutputStream(toFile), md5Digest)); // new
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024); // 1 MB
while (in.read(buffer) != -1) {
buffer.flip();
//md5Digest.update(buffer.asReadOnlyBuffer()); // old
out.write(buffer);
buffer.clear();
}
BigInteger md5Actual = new BigInteger(1, md5Digest.digest());
if (! md5Actual.equals(md5))
throw new RuntimeException(
"MD5 mismatch for file " + toFile +
": expected " + md5.toString(16) +
", got " + md5Actual.toString(16)
);
}
import java.security.*;
import javax.xml.bind.*;
byte[] bytesOfMessage = yourString.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytesOfDigest = md.digest(bytesOfMessage);
String digest = DatatypeConverter.printHexBinary(bytesOfDigest).toLowerCase();
Unlike PHP where you can do an MD5 hashing of your text by just calling md5 function ie md5($text), in Java it was made little bit complicated. I usually implemented it by calling a function which returns the md5 hash text.
Here is how I implemented it, First create a function named md5hashing inside your main class as given below.
public static String md5hashing(String text)
{ String hashtext = null;
try
{
String plaintext = text;
MessageDigest m = MessageDigest.getInstance("MD5");
m.reset();
m.update(plaintext.getBytes());
byte[] digest = m.digest();
BigInteger bigInt = new BigInteger(1,digest);
hashtext = bigInt.toString(16);
// Now we need to zero pad it if you actually want the full 32 chars.
while(hashtext.length() < 32 ){
hashtext = "0"+hashtext;
}
} catch (Exception e1)
{
// TODO: handle exception
JOptionPane.showMessageDialog(null,e1.getClass().getName() + ": " + e1.getMessage());
}
return hashtext;
}
Now call the function whenever you needed as given below.
String text = textFieldName.getText();
String pass = md5hashing(text);
Here you can see that hashtext is appended with a zero to make it match with md5 hashing in PHP.
For what it's worth, I stumbled upon this because I want to synthesize GUIDs from a natural key for a program that will install COM components; I want to syhthesize so as not to manage GUID lifecycle. I'll use MD5 and then use the UUID class to get a string out of it. (http://stackoverflow.com/questions/2190890/how-can-i-generate-guid-for-a-string-values/12867439 raises this issue).
In any case, java.util.UUID can get you a nice String from the MD5 bytes.
return UUID.nameUUIDFromBytes(md5Bytes).toString();
MD5 is perfectly fine if you don't need the best security, and if you're doing something like checking file integrity then security is not a consideration. In such as case you might want to consider something simpler and faster, such as Adler32, which is also supported by the Java libraries.
this one gives the exact md5 as you get from mysql's md5 function or php's md5 functions etc. This is the one I use (you can change according to your needs)
public static String md5( String input ) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(input.getBytes( "UTF-8" ));
StringBuffer sb = new StringBuffer();
for (int i = 0; i < array.length; i++) {
sb.append( String.format( "%02x", array[i]));
}
return sb.toString();
} catch ( NoSuchAlgorithmException | UnsupportedEncodingException e) {
return null;
}
}
import java.security.MessageDigest
val digest = MessageDigest.getInstance("MD5")
//Quick MD5 of text
val text = "MD5 this text!"
val md5hash1 = digest.digest(text.getBytes).map("%02x".format(_)).mkString
//MD5 of text with updates
digest.update("MD5 ".getBytes())
digest.update("this ".getBytes())
digest.update("text!".getBytes())
val md5hash2 = digest.digest().map(0xFF & _).map("%02x".format(_)).mkString
//Output
println(md5hash1 + " should be the same as " + md5hash2)
You can generate MD5 hash for a given text by making use of the methods in the MessageDigest class in the java.security package. Below is the complete code snippet,
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.xml.bind.DatatypeConverter;
public class MD5HashGenerator
{
public static void main(String args[]) throws NoSuchAlgorithmException
{
String stringToHash = "MyJavaCode";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(stringToHash.getBytes());
byte[] digiest = messageDigest.digest();
String hashedOutput = DatatypeConverter.printHexBinary(digiest);
System.out.println(hashedOutput);
}
}
The output from the MD5 function is a 128 bit hash represented by 32 hexadecimal numbers.
In case, if you are using a database like MySQL, you can do this in a more simpler way as well. The query Select MD5(“text here”) will return the MD5 hash of the text in the bracket.
try this:
public static String getHashMD5(String string) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
BigInteger bi = new BigInteger(1, md.digest(string.getBytes()));
return bi.toString(16);
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(MD5Utils.class
.getName()).log(Level.SEVERE, null, ex);
return "";
}
}
This is what I came here for- a handy scala function that returns string of MD5 hash:
def md5(text: String) : String = java.security.MessageDigest.getInstance("MD5").digest(text.getBytes()).map(0xFF & _).map { "%02x".format(_) }.foldLeft(""){_ + _}
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* MD5 encryption
*
* #author Hongten
*
*/
public class MD5 {
public static void main(String[] args) {
System.out.println(MD5.getMD5("123456"));
}
/**
* Use md5 encoded code value
*
* #param sInput
* clearly
* # return md5 encrypted password
*/
public static String getMD5(String sInput) {
String algorithm = "";
if (sInput == null) {
return "null";
}
try {
algorithm = System.getProperty("MD5.algorithm", "MD5");
} catch (SecurityException se) {
}
MessageDigest md = null;
try {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte buffer[] = sInput.getBytes();
for (int count = 0; count < sInput.length(); count++) {
md.update(buffer, 0, count);
}
byte bDigest[] = md.digest();
BigInteger bi = new BigInteger(bDigest);
return (bi.toString(16));
}
}
There is an article on Codingkit about that. Check out: http://codingkit.com/a/JAVA/2013/1020/2216.html
You could try using Caesar.
First option:
byte[] hash =
new Hash(
new ImmutableMessageDigest(
MessageDigest.getInstance("MD5")
),
new PlainText("String to hash...")
).asArray();
Second option:
byte[] hash =
new ImmutableMessageDigest(
MessageDigest.getInstance("MD5")
).update(
new PlainText("String to hash...")
).digest();