Obfuscate Strings - java

we use some Networkcredentials in out App. I just decompiled the app and was able to see the Credentials like Name and Password. I do not really get how to prevent this. I think the word "obfuscator" is the direction which I have to go.
We test proguard but it does not have string encryption or am I wrong?
Is there an easy and free way to do this?
Thank you.

Sorry, but this simply does't work no matter what you'll try. If you obfuscate / encrypt the credentials, the program still must be able to decrypt them at run-time. Therefore, the encryption keys must also be in the generated bytecode somewhere and therefore it's possible to take them, and decrypt the credentials manually outside the program (or just step through the program and read the credentials once they're decrypted).
What you're trying to do is Security by Obscurity and it doesn't work.
Whatever you do, if the program can obtain the credentials at run-time without any external help, a skilled attacker can do the same given enough time.
What you should do:
Store the credentials in plain-text in a property file. Don't bother with encryption, it's pointless. You must make sure the db user you're using is read-only or add-only or something similar so you prevent any damage.
Let the user input the password. If it's not stored in the bytecode, it's safe. He could e.g. input his password and have an account in the db...
Use a safe and known authentication mechanism. Plaintext login+password is not that.
Don't let your application go anywhere near a DB. Set up a service somewhere, with an API, which would hold the read DB conenction. Your application could connect to that and get data via this API. This way, an attacker can't directly access your DB. He could call anything in the new service, though, so you must make sure there's no sensitive data accessible in there.

You should consider to encipher the username and the password: How to encrypt String in Java.
// bytes to encrypt
byte[] input;
// the key and the initialization vector
byte[] keyBytes;
byte[] ivBytes;
// initialize the Cipher
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
// encryption
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted= new byte[cipher.getOutputSize(input.length)];
int enc_len = cipher.update(input, 0, input.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);
// decryption
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(encrypted, 0, enc_len, decrypted, 0);
dec_len += cipher.doFinal(decrypted, dec_len);
Usually, the key (bytes array) should be stored in a file that is only accessible on the specific instance where the server is running and not coded into the app source file.
Otherwise you can use hash (e.g: md5 or sha1) and store the fingerprint instead of the plain string:
// SHA1("hello world") -> "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed
This is a simple method which allows you to calculate the SHA1 hash of a string:
public static String SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(text.getBytes("iso-8859-1"));
byte[] hash = md.digest();
Formatter formatter = new Formatter();
for (byte b : hash)
formatter.format("%02x", b);
return formatter.toString();
}
Import java.io.UnsupportedEncodingException, java.security.MessageDigest and java.security.NoSuchAlgorithmException are required.

Your issue is related to encryption and not obfuscation. You may use this library to store the credentials in an encrypted way: http://www.jasypt.org/encrypting-configuration.html
There are different ways to pass the encryption key to it.
Otherwise, depending on your context, consider using different authentication mechanisms (SSO like) instead of login/password.

Related

How to destroy SecretKey in Java 14?

I am trying to clear my Secretkey after decrypting.
From what I've read, SecretKeys can be destroyed via the destroy method since Java 8.
I am using Java 14 so it should be possible.
However, whenever I use the destroy method on a key, a DestroyFailedException is thrown.
I've also seen that people ignore that Exception in their code, however, if I were to do that, I am able to print the Key after calling the destroy method on it.
Here my Decryption method:
private byte[] decrypt(byte[] encryptedText, char[] password) throws InvalidKeyException,
InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, DestroyFailedException {
ByteBuffer bb = ByteBuffer.wrap(encryptedText);
byte[] iv = new byte[ivLengthByte];
bb.get(iv);
byte[] salt = new byte[saltLengthByte];
bb.get(salt);
byte[] cipherText = new byte[bb.remaining()];
bb.get(cipherText);
SecretKey key;
key = crypto.getAESKeyFromPassword(password, salt);
Cipher cipher;
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(tagLengthBit, iv));
byte[] plainText = cipher.doFinal(cipherText);
Main.clearArray(password, null);
Main.clearArray(null, iv);
Main.clearArray(null, salt);
Main.clearArray(null, cipherText);
key.destroy();
cipher = null;
return plainText;
}
After calling the destroy method, I am, as said, (assuming I ignore the Exception) able to print the key via String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded());
EDIT:
After using my Clear method on the array, I can still print it:
byte[] temp = key.getEncoded();
Main.clearArray(null, temp);
Clear Array:
protected static void clearArray(char[] chars, byte[] bytes) {
if (chars != null) {
for (int i = 0; i < chars.length; i++) {
chars[i] = '\0';
}
}
if (bytes != null) {
for (int i = 0; i < bytes.length; i++) {
bytes[i] = 0;
}
}
}
getAESKey:
protected SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return secret;
}
Final Edit:
The best solution was to switch frim PBKDF2 to argon2.
https://github.com/kosprov/jargon2-api
Argon2 allows to use raw Hashes, then you may store that byte array in a SecureKeySpec as mentioned above, since it allows destroying of the Spec, and clear the raw Hash Array.
Actually, there is no easy solution to this. The problem is that the destroy method is an "optional" method. Not all implementations of SecretKey implement it. If you are using a SecretKey type that doesn't implement the method, you get this exception and there is no simple solution.
Unfortunately, you can't just implement the method yourself because (typically) the class it belongs on is provided by the Java SE libraries.
And even if you do figure out how to destroy the key, there is also the problem of the String containing the password1. (And this problem is more of a security risk, since trawling for a String containing a password is likely to be easier than trawling for an unknown byte sequence.)
Options:
Forget the problem. Don't destroy they key / password in memory. (See below for an explanation as to why this is not as bad as it sounds.)
Look for alternative JSSE crypto libraries where the SecretKey implementation for AES secret keys does implement destroy. I am guessing that the Bouncy Castle libraries might. (And if they don't, you always have the option of downloading the source code and patching them.)
Nasty reflection. You could figure out which actual class implements the secret key, and look at its code to work out how it represents the key internally. Then you could use reflection to break the abstraction and access its private state and ... write zeros over the key.
Why is not destroying the key not a disaster?
So some security experts may disagree with this, but I still think it is a valid viewpoint.
When you zero a key or password in memory, you are (ostensibly) protecting against the following kinds of attack:
Attaching a Java debugger to the JVM process and using that to locate and read the key.
Reading the JVM processes memory.
Reading memory pages that have been written to disk.
How easy are these attacks? Well the first two require that the hacker has already gotten into the host and escalated to (probably) root privilege. In the third case you could do it that way, but the hacker could also just steal the hard drive where the swap pages are written.
In all cases, the hacker then has to find the secret key. Unlike (say) a C / C++ program, the key won't be stored at a fixed location. Instead the hacker has to find it by pattern matching, or by finding reference chains. (A Java debugger would make it easier, provided that the key object is still reachable.) And the flip-side is once the key has been garbage collected, the copy in memory will be gone, and the copy in swap will go the next time that the OS writes out the (now) dirty page where the key object once lived. After that ... it is "gone" for all practical purposes.
So rewind a bit. I said that in order to carry off this kind of attack, the hacker already needs root access. (Or the hard drives, which most likely amounts to the same thing.) Now if they have that, there are other ways they can steal the secret key. For example:
Use the debugger to set a breakpoint on (say) the destroy method, and grab the key before it is destroyed.
Use the debugger to capture the password before you create the key.
Steal the private key for the server's SSL cert (or whatever) so that they can pick up the password from network traffic.
Install a software keystroke logger.
Replace your application code with a version that leaks the key or password via some side channel.
And of course they can install backdoors, etc. In short, if the hacker has compromised the system to the degree needed to pull off a "steal stuff out of memory" attack against a JVM, that is probably the least of your worries.
Now a security expert may say that it is "best practice" to have a layered defense against hackers. There is some truth to that. However, if security is that important to you, you should do a proper security analysis (not just a "tick the boxes" audit) and figure out what the real risks are. This will (probably2) tell you that it is better to focus on making the system secure than worrying if someone (with root privilege) can steal keys out of memory.
1 - Though not in your case, because I see that you are using a char[] ... which can be cleared. Except that this still vulnerable to all of the other attacks that I talk about.
2 - Or maybe it won't. But you need to do the analysis!
You must implement the destroy method yourself. The documentation explains this.
https://docs.oracle.com/javase/8/docs/api/javax/crypto/SecretKey.html
I may have found a solution, I tried using this: https://github.com/dbsystel/SecureSecretKeySpec
The only problem is that the key would have to be a byte array, and doing this:
protected SecureSecretKeySpec getAESKeyFromPassword(char[] password, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory= SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
byte[] temp = factory.generateSecret(spec).getEncoded();
SecureSecretKeySpec sec= new SecureSecretKeySpec(temp, "AES");
Main.clearArray(null, temp);
return sec;
}
Might not be good, since there is a getEncoded called on the SecretKey, so there might be a SecretKey in the Memory?

Java PBEWITHSHA1ANDDESEDE encryption equivalent in node.js?

I have inheritied Java web application and am supposed to convert that to node.js.
Part of that is encryption of data. In Java it is done like in attached code. How would I do that in node using crypto?
I am not strong in cryptography at all, sorry if this is really basic question and thanks in advance.
private final String ALGORITHM = "PBEWITHSHA1ANDDESEDE";
private final int ITERATION_COUNT = 20;
private final byte[] SALT = {
(byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
(byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
};
and later...
PBEKeySpec pbeKeySpec = new PBEKeySpec("password".toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(ALGORITHM);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(SALT, ITERATION_COUNT);
// Create PBE Cipher
Cipher pbeCipher = Cipher.getInstance(ALGORITHM);
// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
byte[] encrypted = pbeCipher.doFinal("text to be encrypted");
Edit:
This is my failed attempt to implement that in node.js based on various searches:
var crypto = require('crypto');
var SALT = new Buffer('c773218c7ec8ee99', 'hex');
var pass = new Buffer('password');
//digest is by default SHA-1 thats what I need
var key = crypto.pbkdf2Sync(pass, SALT, 20, 56); //[, digest]
//var cipher = crypto.createCipher('des-ede-cbc', key);
var cipher = crypto.createCipher('des-ede', key);
//var cipher = crypto.createCipheriv('des-ede-cbc', key, new Buffer('00000000', 'binary'));
cipher.update(new Buffer('This is to be encoded'));
var encrypted = cipher.final('binary');
console.log(encrypted);
fs.writeFileSync('encrypted.file', encrypted);
When I am trying to use crypto.createCipheriv I have no idea what to put there as IV.
When used without IV, it produces some 'encrypted' gibberish however when saved to file, it can't be read and decoded on Java side. Sigh.
The reason that you can't get these to interoperate is the Java side is using PBE and the node.js side is using PBKDF2, which serve similar purposes and come from the same standards document (PKCS #5), but have very different mechanisms under the hood.
Because these are different key generation algorithms, you generate different keys on each end, and thus get different results when you decrypt.
There is also something of an mismatch in the level of abstraction between java and node.js in your code. the Java API you are using is very high level, and uses OpenSSL-like constructs. Meanwhile, the node.js code is at a much lower level, gluing pieces together bit-by-bit. This can cause issues when, for example, the java code introduces a particular padding structure or cipher mode of operation.
If this is just for learning or something non-critical, I would recommend changing the java code to be at the same lower level as the node.js code, and put pieces together one by one: generate the key on both sides and make sure they are the same, ecrypt on both sides and get the same output, etc. If you can't change the java code, use something like forge to get your node.js code at the same higher level as the java code. But as the comments on this question indicate, you may not be able to do PBE anyways.
If this is for something "real" where you actually want the files saved to be secure, call out to an external program like gpg to handle the encryption, instead of "rolling your own" file encryption system.

Implimentation for MD5 Message Digest for Passwords in Java

We made a system for a school project and our professor told us not to have passwords stored in plain text in the database. She wants us to encrypt these passwords for security since our system will be handling a lot of confidential data. So we decided to use MD5 for making a hash of the passwords the problem is I don't really get how we would implement it in the login process.
Welcome to SO. I think there a post similar to yours has already been answered but I'll give you how I solved it.
private String encryptPassword(String password) throws NoSuchAlgorithmException{
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(password.getBytes());
byte[] digest = md.digest();
StringBuilder stringBuilder = new StringBuilder();
for (byte b : digest) {
stringBuilder.append(String.format("%02x", b & 0xff));
}
return stringBuilder.toString();
}
As you can see the method above that's how I encrypted the password which is a string passed through the parameters. MD5 is a one way encryption so there would be no way for you to decrypt it with Java but there are a few tools.
So what you should do is have the password converted when a user is registering(assuming you can add users in your system) then storing the converted value in the database as as string(varchar or text). Then when you want to login use the same method again then compare the result with whatever password is in the database. These generations aren't random so if you enter like "123" the generated hash will be the same everytime.

Translating Java RSA encryption routine to Objective-C

I am trying to duplicate an encryption process that is working in Java over to iOS/OSX.
My Java code is as follows:
PublicKey publicKey = KeyFactory.getInstance("RSA").
generatePublic(new RSAPublicKeySpec(firstKeyInteger, secondKeyInteger));
// This always results in the public key OpenSSLRSAPublicKey{modulus=2b3b11f044.....58df890,publicExponent=10001}
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING");
String stringToEncode = "EncodeThisString";
byte[] bytesToEncode = stringToEncode.getBytes("UTF-8");
cipher.init(cipher.PUBLIC_KEY, publicKey);
byte[] encrypted = cipher.doFinal(plain);
The first challenge i'm struggling with is how to use the public key in iOS. Can I just dump the modulus into NSData and use it? Or must I store it in the keychain first? (I don't really need to use the keychain unless I must). Or is there a method similar to generatePublic() were I can recreate the public key using the 2 integers?
Then would I use SecKeyEncrypt to encrypt? Whenever I add this to my project I get Implicit declaration warnings even though I import the Security framework.
Thanks
EDIT -----
I think I have managed to get a Base64 encoded public key, which I believe is what is in a PEM certificate. Now, how to use it.

Decrypting a hardcoded file as byte[]

Well this is actually a two-parter...
First I need to
read the contents of the file
crypt them into a byte[]
write the byte[] in a file or whatever...
Then the result from #2 or #3 will go into another project. I'm trying to protect our PEM/DER keys.
For decryption, I need to
read the contents of the crypted file as a byte[]
decrypt them into a byte[]
write the decrypted data to a file OR use it instead of a file
Now, I have some basic crypting code
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// By initializing the cipher in CBC mode, an "initialization vector" has been randomly
// generated. This initialization vector will be necessary to decrypt the encrypted data.
// It is safe to store the initialization vector in plain text for later use. You can obtain
// it's bytes by calling iv.getIV().
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
// IvParameterSpec iv = new IvParameterSpec(IV); //used for the hardcoded one
byte[] encryptedData = cipher.doFinal(data);
and decrypting one as well
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedData);
System.out.println("decrypted: " + new String(decryptedData));
and the question is:
Given a use-case scenario where one would rarely encrypt something and would distribute crypted keys that are to be decrypted at runtime, what do I need to save apart from the cyphertext?
I know I need to save the IV, but when I did decryption wasn't quite good - which leads me to believe that I need to save the secretKey as well.
Could anyone give me any tips, pointers or general security hints to a better solution? If I need to save the key, the IV and the encrypted data, where should I store them? Maybe hardcode the key and store the IV along the encrypted data? Maybe hardcode both the IV and the key and just store encrypted data in the files?
This isn't about theoretical safety, think of this as the biggest nuissance and inconvenience you can cause to someone that is trying to steal your keys. We all know there's no way I can perfectly hide them.
I pretty much need what this guy started with Decrypting an encrypted file and executing in Java
However if there's a better way of feeding secure data into a PemKeyReader, i'm all ears.
Sharing the key and encrypting something are two completely different things. How to share keys
Having said this, AES with 128bit is fairly strong encryption algorithm than 3DES So what you can do is keep PKI infrastructure in place to exchange AES keys and then Encrypt and Decrypt using them.
Why not RSA? RSA needs to be minimum 512 bit to consider it as strongest and if you increase more bits then it increases time required for encryption and decryption.
SO AES is fast and safe.
Use SecretKeySpec to create key from byte[]
public static void main(String[] args) throws Exception
{
// Initialise secret key with predefined byte array [] like below. I
// have used simple string to array method to generate 16 byte array.
// AES Key must be minimum 16 bytes.
// Now you can put this byte array some where is .SO file.
// Generate new Key using this byte []
// Then you can generate a key using device specific information at
// first boot up.
// Use second key to encrypt data and first key to encrypt the second
// key
// I Hope it clears all the doubts
SecretKey key = new SecretKeySpec("ABCDEFGHIJKLMNOP".getBytes(), "AES");
System.out.println(Arrays.toString(key.getEncoded()));
// Initialise Cipher with AES Algorithm
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Set The Encrypt Mode
cipher.init(Cipher.ENCRYPT_MODE, key);
// Encrypt some bytes
byte[] encrypted = cipher.doFinal("ABCDEFGH".getBytes());
// Print it to vefiry
System.out.println(Arrays.toString(encrypted));
// Get The IV
byte[] iv = cipher.getIV();
System.out.println(iv.length);
// Now why storing you can create structure like [16 IV][Encrypted Data]
// And while decrypting you can read first [16] bytes IV and then
// decrypt remaining bytes
//byte[] iv = new byte[16];
// System.arraycopy(encrypted, 0, iv, 0, 16)
//Copy remaining bytes to decrypt
// set cipher to decrypt mode
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(iv));
// decrypt it
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println(new String(decrypted));
}
Now write an algorithm which will generate byte[] from some random data like device name, user name, random seed etc.
You can add more protection to algorithm source code by writing that algorithm in C and create.SO file and get byte [] using Native calls.
What are the advantages of doing all this?
Event if your so is hacked it will need real time environment to run create key out of it.
Even if some one does crack it the damage will be limited i.e. 1 device
Hacker will have to repeat same with each device which is highly impossible to do.
The I/O aspect of your question is best addressed by reading the "Byte Streams" and "Buffered Streams" sections of the Oracle Java tutorial. You can accumulate the bytes in memory by writing them to a ByteArrayOutputStream, and then using the toByteArray() method to get the bytes as a byte[].

Categories