I'm looking for Java's equivalent of .NET's SecureString.aspx. Is there such implementation available in 2018?
OWASP implementation is not exactly the same because it's just a plain char array. While .NET equivalent provides additional features such as the ability to get an instance from/to unmanaged memory and also encryption.
I'm aware of common Java pattern to pass around passwords as char[]
and do Arrays.fill()
them with zeros after use. But it requires building a trivial utility class around char[]
all the time.
Oracle has a GuardedString
implementation. It is the closest match to .NET's SecureString
solution.
Secure string implementation that solves the problems associated with
keeping passwords as java.lang.String
. That is, anything represented
as a String is kept in memory as a clear text password and stays in
memory at least until it is garbage collected.
The GuardedString
class alleviates this problem by storing the
characters in memory in an encrypted form. The encryption key will be
a randomly-generated key.
In their serialized form, GuardedString
s will be encrypted using a
known default key. This is to provide a minimum level of protection
regardless of the transport. For communications with the Remote
Connector Framework it is recommended that deployments enable SSL for
true encryption.
Applications may also wish to persist GuardedString
. In the case of
Identity Manager, it should convert GuardedString
s to
EncryptedData
so that they can be stored and managed using the
Manage Encryption features of Identity Manager. Other applications may
wish to serialize APIConfiguration
as a whole. These applications
are responsible for encrypting the APIConfiguration
blob for an
additional layer of security (beyond the basic default key encryption
provided by GuardedString
).
I modified the OWASP version to randomly pad the char array in memory so the char array at rest is not stored with the actual characters.
import java.security.SecureRandom;
import java.util.Arrays;
/**
* This is not a string but a CharSequence that can be cleared of its memory.
* Important for handling passwords. Represents text that should be kept
* confidential, such as by deleting it from computer memory when no longer
* needed or garbaged collected.
*/
public class SecureString implements CharSequence {
private final int[] chars;
private final int[] pad;
public SecureString(final CharSequence original) {
this(0, original.length(), original);
}
public SecureString(final int start, final int end, final CharSequence original) {
final int length = end - start;
pad = new int[length];
chars = new int[length];
scramble(start, length, original);
}
@Override
public char charAt(final int i) {
return (char) (pad[i] ^ chars[i]);
}
@Override
public int length() {
return chars.length;
}
@Override
public CharSequence subSequence(final int start, final int end) {
return new SecureString(start, end, this);
}
/**
* Convert array back to String but not using toString(). See toString() docs
* below.
*/
public String asString() {
final char[] value = new char[chars.length];
for (int i = 0; i < value.length; i++) {
value[i] = charAt(i);
}
return new String(value);
}
/**
* Manually clear the underlying array holding the characters
*/
public void clear() {
Arrays.fill(chars, '0');
Arrays.fill(pad, 0);
}
/**
* Protect against using this class in log statements.
* <p>
* {@inheritDoc}
*/
@Override
public String toString() {
return "Secure:XXXXX";
}
/**
* Called by garbage collector.
* <p>
* {@inheritDoc}
*/
@Override
public void finalize() throws Throwable {
clear();
super.finalize();
}
/**
* Randomly pad the characters to not store the real character in memory.
*
* @param start start of the {@code CharSequence}
* @param length length of the {@code CharSequence}
* @param characters the {@code CharSequence} to scramble
*/
private void scramble(final int start, final int length, final CharSequence characters) {
final SecureRandom random = new SecureRandom();
for (int i = start; i < length; i++) {
final char charAt = characters.charAt(i);
pad[i] = random.nextInt();
chars[i] = pad[i] ^ charAt;
}
}
}