This is a brainstorming question about what\'s possible in Java (or not). I want to know if it is possible to hide a secret within a class and prevent anymore from accessing
No, it's not safe from other Java code. Your secret could be retrieved from an instance of Safe
like this:
Field field = safe.getClass().getDeclaredField("secret");
field.setAccessible(true);
String secret = (String) field.get(safe);
Update: If you control the loading of the other Java code that you want to hide the secret from you can probably use a custom SecurityManager
or ClassLoader
to prevent access to it. You need to control the environment that this runs in to work though, e.g. a server you restrict access to.
Your edited question however mentions that the code can run on any desktop or device. In that case there's really nothing you can do to protect the secret from other processes that could do just about anything. Even if you encrypt it in memory another process can just intercept the key or even the plaintext secret as its passed around.
If you don't control the environment that you need something to be secure in then you likely need to consider a different approach. Perhaps you can avoid storing the secret in memory altogether?
Assuming information passed to method calls are safe, a key is a good solution. The key doesn't need to be stored anywhere in the app, and because of this, the information can't be accessed through Java only. It gets interesting if you want a way to share the secret with others without giving them your key, which is what the shareSecret method is for below. However, it becomes tricky managing this. One process could be:
1) The secret seeker requests access, entering a temp key that is stored
2) The secret keeper grants access with their key, the temp key is deleted, and a temp Safe object is created that works for the temp key.
3) The secret seeker enters the temp key and a permanent key, the temp Safe object is deleted, and a new permanent Safe object is created that can be accessed with the permanent key.
Again, assuming parameters passed to method calls are safe, the main problem with the above procedure is that someone could have hijacked the temp key between 1 and 2 and use it to view the temp secret between steps 2 and 3. However, it would make it tougher to crack than storing it in a plain-text string.
public final class Safe {
private String secret;
public Safe(String secret, String key){
this.secret = encode(secret, key}
public String getSecret(String key){
return decode(this.secret, credentials);
}
public Safe shareSecret(String fromKey, String toKey){
return new Safe(decode(this.secret, fromKey), toKey);
}
private String encode(String secret, String key){
//Code to encode the secret based on key here...
}
private String decode(String secret, String key){
//Code to decode the secret based on key here...
}
}
You can make a secret "hard" to access but you can't make it impossible. There's a saying (Bruce Schneier I believe): Against a casual user, anything works. Against a determined cracker nothing works.
If you need to run untrusted code, the best way to do this is to run it in a separate JVM. This way the untrusted code can be given the strictest limitations and even be killed if for example you have a run away CPU or crash the JVM. The only access the code can make is via the means you provide it an use reflection will not give you access to classes in another JVM no matter what you do.
You can even construct a jail or maze for your application. This can allow the untrusted code to run in what appears to be a real system, but in reality it is not. (The purpose of which is to tie you any would be hacker long enough to see what they are doing)
You could even run the JVM in its own virtual machine so it looks like you have complete (but dummy) system to "invade". A virtual machine can be saved for analysis and wiped to a preset state very easily.
The ultimate solution is to place the untrusted code on a dummy LAN of its own. (Something they did to analyse the stuxnet worm. ;)
No, you cannot embed a secret in a class that way. If you compiled that, then I could just run strings
on the resulting class file and it might be in there.
One way you may go about doing this if you're only trying to validate it is by storing a hash in it. Once you want to validate the input, hash that and compare the hashes.
Adding a comment for anyone else who stumbles across this old thread....
Sometimes all you need is a 'signed' value, not an 'encrypted' value. E.g., (encoded) license tokens need to be signed but they shouldn't need to be encrypted. In these cases you can distribute the PUBLIC key and it's not an issue that anyone can see it. You're still protected since nobody else can create the signed token.
You can also use the public key to send encrypted messages to the server.
Of course this won't stop a knowledgeable and determined attacker - see the other responses. In this case someone could simply replace your public key with their own, esp. if you don't validate the cert chain. But if you can use signatures instead of encryption it will save you major headaches.