I am learning android and trying to make MY Application secure as much as possible.
I have bought one android app which have below class
public class API {
#SerializedName("sign")
private String sign;
#SerializedName("salt")
private String salt;
#SerializedName("package_name")
private String package_name;
public API(Activity activity) {
String apiKey = "secretkey";
salt = "" + getRandomSalt();
sign = md5(apiKey + salt);
package_name = activity.getApplication().getPackageName();
}
public API(Context context) {
String apiKey = "secretkey";
salt = "" + getRandomSalt();
sign = md5(apiKey + salt);
package_name = context.getApplicationContext().getPackageName();
}
private int getRandomSalt() {
Random random = new Random();
return random.nextInt(900);
}
private String md5(String input) {
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(input.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < messageDigest.length; i++)
hexString.append(String.format("%02x", messageDigest[i]));
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
public static String toBase64(String input) {
byte[] encodeValue = Base64.encode(input.getBytes(), Base64.DEFAULT);
return new String(encodeValue);
}
}
And its using like below
JsonObject jsObj = (JsonObject) new Gson().toJsonTree(new API(Login.this));
jsObj.addProperty("email", sendEmail);
jsObj.addProperty("password", sendPassword);
assert device != null;
jsObj.addProperty("player_id", device.getUserId());
jsObj.addProperty("method_name", "user_login");
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<LoginRP> call = apiService.getLogin(API.toBase64(jsObj.toString()));
I think its securing data with encryption with API class
I am trying to use above method in my application but I am using RequestParams so I am not getting idea how I can use above in my case
RequestParams requestParam=new RequestParams();
requestParam.put("mobile",mobile_number);
requestParam.put("password","password");
AsyncHttpClient client = new AsyncHttpClient();
client.get(BASE_URL+"login.php?",requestParams, new AsyncHttpResponseHandler() {
Let me know if anyone here can help me for solve my issue.
Thanks a lot!
As far as securing data, as long as you are using SSL/TLS, (even with GET), your data is encrypted during transfer.
That being said, the code above has MD5 and Base64 methods to convert the info to a hash. Using those functions, you can have a secret key shared between your app and the server, and use that key to encrypt/decrypt the hash values.
To make the md5 function more secure, you can create a know "salt" value rather than a random one, then update the function like this:
public static String md5(String salt, String plainText)
throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
if (salt != null) {
md.update(salt.getBytes());
}
md.update(plainText.getBytes());
byte byteData[] = md.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16)
.substring(1));
}
return sb.toString();
}
And update the call like this:
requestParam.put("password", md5(md5(SALT_VALUE), md5(password));
Also, as a side-note, with GSON, you do not have to construct your own JSON objects, you can use the actual object reference and call .toJson()
Related
I use this code in PHP to encrypt the password:
return sha1("kD0a1".md5("xA4".$password)."f4A");
Does anyone know an effective replacement for use in Android? I tried different functions for MD5 and SHA1 but in Java it always generates a different HASH than in PHP.
For example, as follows:
public static String passwordHash(String password) {
return sha1("kD0a1"+md5("xA4"+password)+"f4A");
}
public static String md5(String s) {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0; i<messageDigest.length; i++)
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
public static String sha1(String clearString) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
messageDigest.update(clearString.getBytes("UTF-8"));
byte[] bytes = messageDigest.digest();
StringBuilder buffer = new StringBuilder();
for (byte b : bytes) {
buffer.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
}
return buffer.toString();
}
catch (Exception ignored) {
ignored.printStackTrace();
return null;
}
}
However, PHP and Java will return a different HASH string to me.
PASS: test
PHP: 17bf2c08f4b9447cf8316736e13833316d3edc23
JAVA: 8434696e252b89af0db033eb255c88a91a42ce14
However, if I enter "passTest" for example, it will generate a hash correctly
PASS: passTest
PHP: db4aedf1d4072b7b645996394aa74743f14eeb7a
JAVA: db4aedf1d4072b7b645996394aa74743f14eeb7a
And "passwordTest" is wrong again.
PASS: passwordTest
PHP: 1ad47c24d556187f1de5db66ff623bbe08a27f33
JAVA: 0f058b3aea48e69c028a7ee2693a98d6074b10a8
I can't explain that sometimes it works and sometimes it doesn't, and at the same time it just changes the String for the password.
Do you think there could be a problem with coding or something? I thought TextView did it, but even if I enter a String outside of TextView, it behaves the same way.
Thank you in advance for any advice.
I would add that I am testing on SDK 31
M.
following code may help you to achieve what you want, it's a method named hashing and salt password, furthermore, the salt method prevents the collision of passwords that have been registered on your database
public static String hashPassword(String password) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.reset();
md.update(password.getBytes());
byte[] mdArray = md.digest();
StringBuilder sb = new StringBuilder(mdArray.length * 2);
for(byte b : mdArray) {
int v = b & 0xff;
if(v < 16)
sb.append('0');
sb.append(Integer.toHexString(v));
}
return sb.toString();
}
here is the salt method
public static String getSalt() throws NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[32];
sr.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
for further reading about different hashing&salting password, consult the below link, it might help you to solid your understands
https://howtodoinjava.com/java/java-security/how-to-generate-secure-password-hash-md5-sha-pbkdf2-bcrypt-examples/
NB: you should use a strong implementation to hash your password to prevent cracking
I would appreciate your help to understand why my SHA256 encryption function in JAVA and in PYTHON does not produce the result (difference in 1 digit, one more in python):
hash to encrypt is: "thisisatest"
Java results is: a7c96262c21db9a06fd49e307d694fd95f624569f9b35bb3ffacd88044f9787
Python result is:
a7c96262c21db9a06fd49e307d694fd95f624569f9b35bb3ffacd880440f9787
Python code:
import hashlib
def encrypt_string(hash_string):
sha_signature = \
hashlib.sha256(hash_string.encode()).hexdigest()
return sha_signature
hash_string = "thisisatest"
print(encrypt_string(hash_string), end="")
Java code:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class OpenAPIPasswordEncryptor1 {
private static MessageDigest sha256;
// generated password is stored encrypted (using also user name for hashing)
public synchronized static String encrypt(String hash) {
try {
StringBuilder builder = new StringBuilder();
builder.append(hash);
// first time , encrypt user name , password and static key
String encryptedCredentials = encryptionIterator(builder.toString());
return encryptedCredentials;
}
catch (Exception e) {
e.printStackTrace();
}
return "";
}
private static String encryptionIterator(String content) {
try {
sha256 = MessageDigest.getInstance("SHA-256");
// append the static key to each iteration
byte[] passBytes = (content).getBytes();
sha256.reset();
byte[] digested = sha256.digest(passBytes);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < digested.length; i++) {
sb.append(Integer.toHexString(0xff & digested[i]));
}
return sb.toString();
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
return "";
}
// generate password for developers
public static void main(String[] args) {
String hash = "thisisatest";
String encrypt = encrypt(hash);
System.out.println("Your Password Is '" + encrypt + "'");
}
}
The problem is that Integer.toHexString(int) drops the leading zero if the (unsigned) byte value is 15 or less. As a quick workaround, you could use this:
sb.append(String.format("%02x", 0xff & digested[i]));
Although more efficient implementations are certainly possible.
To produce the same bug in Python, use this:
def encrypt_string(hash_string):
sha_signature = hashlib.sha256(hash_string.encode()).digest()
return "".join(["{:x}".format(b) for b in sha_signature])
I have a strange problem in getting equivalent hash code from C# code translated into Java. I don't know, what MessageDigest update method do. It should only update the contents of digest and should compute hash after calling digest.
Same thing I am doing in C# with SHAManaged512.ComputeHash(content). But I am not getting same hash code.
Following is the Java code.
public static String hash(String body, String secret) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(body.getBytes("UTF-8"));
byte[] bytes = md.digest(secret.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
} catch (Exception e) {
throw new RuntimeException();
}
}
Following is C# Code
private byte[] ComputeContentHash(string contentBody)
{
using (var shaM = new SHA512Managed())
{
var content = string.Concat(contentBody, Options.SecretKey);
var hashedValue = shaM.ComputeHash(ToJsonStream(content));
return hashedValue;
}
}
public static Stream ToJsonStream(object obj)
{
return new MemoryStream(Encoding.Unicode.GetBytes(obj.ToString()));
}
I had the exact same issue. Its been two years since you asked but just in case someone comes across this question, here's the solution
public static string encryptHash(string APIkey, string RequestBodyJson)
{
var secretBytes = Encoding.UTF8.GetBytes(APIkey);
var saltBytes = Encoding.UTF8.GetBytes(RequestBodyJson);
using (var sHA256 = new SHA256Managed())
{
byte[] bytes = sHA256.ComputeHash(saltBytes.Concat(secretBytes).ToArray());
//convert to hex
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
builder.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
return builder.ToString();
}
}
The solution was to put first Secret Key and concate it with pay load data.
Encoding.Unicode (which you are using in the C# ToJsonStream method) is not UTF8. It's UTF16. See MSDN. (Also keep in mind that UTF16 can be little or big endian.) You're looking for Encoding.UTF8.
First thing to do is check if the byte array you're hashing is the same.
I have the following JAVA and I am trying to convert into C# using ChilKat (or BouncyCastle) I have a working version in ChilKat, but not sure how to validate Here is the JAVA code:
private SecretKey symmKey = null;
public String encrypt(String strToEncrypt) throws Exception
{
String symmEncryptMode = "DESede";
String encString= null;
KeyGenerator keyGen = KeyGenerator.getInstance(symmEncryptMode);
symmKey = keyGen.generateKey();
byte dataToEncrypt[] = strToEncrypt.getBytes();
Cipher symmCipher = Cipher.getInstance(symmEncryptMode);
symmCipher.init(Cipher.ENCRYPT_MODE, symmKey);
byte[] encrypted = symmCipher.doFinal(dataToEncrypt);
encString= new String(Base64.encode(encrypted));
encString = URLEncoder.encode(encString, "UTF-8");
return(encString);
} //end method create Signature
Here is what I have so far (It returns a value, but I don't know how to validate as this is one of three steps of my encyption process - step 3 works, 1 and 2 are suspect, so I figured I'd ask one at a time...) This uses ChilKat and it returns a value, but I am not sure if it is correct:
private static string EncryptStringSymmetric(string data2Encrypt, ref string passKey)
{
//Init Encryptor
Crypt2 encryptor = new Crypt2();
bool success = encryptor.UnlockComponent("Anything for 30 - day trial");
if (success != true)
{ throw (new Exception("Crypt component unlock failed")); }
//Encryptor Settings
encryptor.CryptAlgorithm = "3des";
encryptor.KeyLength = 192;
encryptor.EncodingMode = "base64";
encryptor.PaddingScheme = 0;
encryptor.Charset = "utf-8";
encryptor.CipherMode = "ecb";
encryptor.RandomizeKey();
passKey = encryptor.GetEncodedKey("base64");
string eStr;
//byte[] bytesToEncrypt = Encoding.ASCII.GetBytes(data2Encrypt);
//eStr = encryptor.EncryptBytesENC(bytesToEncrypt);//??
eStr = encryptor.EncryptStringENC(data2Encrypt);
return eStr;
}
in PHP I have the following function:
base64_encode(hash_hmac('sha256', $data, $secret, false));
I'm trying to create a function in Java that will give the same result for the same "data" and "secret" parameters.
I tried to use this function:
public static String base64sha256(String data, String secret) {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] res = sha256_HMAC.doFinal(data.getBytes());
return Base64.encodeToString(res, Base64.NO_WRAP);
}
But I get different results for the same input
Update: This function works. Enjoy.
public static String base64sha256(String data, String secret) {
String hash = null;
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] res = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
hash = getHex(res);
hash = Base64.encodeToString(hash.getBytes("UTF-8"), Base64.NO_WRAP);
} catch (Exception e){}
return hash;
}
static final String HEXES = "0123456789abcdef";
public static String getHex( byte [] raw ) {
if ( raw == null ) {
return null;
}
final StringBuilder hex = new StringBuilder( 2 * raw.length );
for ( final byte b : raw ) {
hex.append(HEXES.charAt((b & 0xF0) >> 4))
.append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
The output of the php function are lowercase hex digits when the fourth parameter is false. Your second java version however produces uppercase hex digits. Either correct the case difference or you could change the fourth parameter of hash_hmac to true and it will probably match with your first Java version.
If trying to match output of drupal_hmac_base64 with Java 8, you can use the following code:
final String ALGORITHM = "HmacSHA256";
Mac mac = Mac.getInstance(ALGORITHM);
SecretKeySpec secret = new SecretKeySpec(authorizationKey.getBytes(), ALGORITHM);
mac.init(secret);
byte[] digest = mac.doFinal(body.getBytes());
hash = Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
return signature.equals(hash);
Note that drupal returns a hash using raw binary data (3rd parameter TRUE). Also, base64 encoding in PHP matches the URL and Filename safe base64 encoder in Java https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html#url.
For someone who might be facing a slight change (not working) in Java result compared to PHP, my issue was in returning the hash from HmacSHA256 as String, while you should return it and pass to Hex as byte[].
Here are the working methods to simulate PHP's hash_hmac()
public String hashValue(String message) {
byte[] hash = toHmacSHA256(message);
String hashHexed = toHex(hash);
return hashHexed;
}
private String toHex(byte[] value) {
String hexed = String.format("%040x", new BigInteger(1, value));
return hexed;
}
private byte[] toHmacSHA256(String value) {
byte[] hash = null;
try {
SecretKey secretKey = new SecretKeySpec(PRIVATE_KEY.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKey);
hash = mac.doFinal(value.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return hash;
}