I want to acquire a password string from the user on Android.
I do not want this string to be stored in a Java String at any point in the process from when the user types it, to where it arrives in my code in a char or byte array.
The reason for this is Sun's injunction against using Java Strings for sensitive data.
"Objects of type String are immutable, i.e., there are no methods defined that allow you to change (overwrite) or zero out the contents of a String after usage. This feature makes String objects unsuitable for storing security sensitive information such as user passwords. You should always collect and store security sensitive information in a char array instead."
http://docs.oracle.com/javase/1.5.0/docs/guide/security/jce/JCERefGuide.html#PBEEx
So I can't use EditText, because that uses Strings internally (even though it returns an Editable which could conceivably be backed by char[] or Char[]?).
What is the simplest way to accept a char array from the user? I'm guessing a Canvas on which I listen for key events?
I don't see the EditText.java (API 17) using the String internally. It is merely 2 pages long code. Of course, TextView.java from which EditText has inherited has 9k lines in the file. You still won't see TextView.java using the String internally but with its own implementation of CharWrapper for CharSequence. (TextView.java line #8535 API 17). Here you have the method call getChars. As you will notice that buf is copied over from mChars which is char[] and not String.
private char[] mChars;
public void getChars(int start, int end, char[] buf, int off) {
if (start < 0 || end < 0 || start > mLength || end > mLength) {
throw new IndexOutOfBoundsException(start + ", " + end);
}
System.arraycopy(mChars, start + mStart, buf, off, end - start);
}
Now all you have to do is call getChar and pass along the char[] to be filled in.
int pl = mPasswordEt.length();
char[] password = new char[pl];
mPasswordEt.getText().getChars(0, pl, password, 0);
You have the desired char[] password without using String. After you have finish working with it you can clear it from memory as follow.
Arrays.fill(password, ' ');
There are two possible situations your application may encounter:
All applications are properly sand-boxed in their environment. In this case you should not worry about your passwords, because other processes cannot access your process memory, no matter if there are Strings or byte[] arrays in there.
There's a rogue application with superuser access. In this case you should not worry about Strings either, because there are too many places to intercept your passwords, so Strings should be close to the bottom of the list of things to worry about.
Editable implements CharSequence, which, according to the docs, is a "readable sequence of char values".
Related
Can anyone tell me:
1. How to get the characters from EditText into a char array without using a string
and
2. How to display the contents of a char array in TextView or EditText, again without using a string.
I can do it if I use a string as an intermediate step, but I don't want to do that for security reasons.
To read an EditText directly into charArray without using String.
val password:CharArray = CharArray(editText.length())
editText.text.getChars(0, editText.length(), password, 0)
To write into an editText without using String
editText.setText(password, password.size, 0)
The relation between the strings is stored in the array data, which will be eventually GC-ed (or you may even overwrite it by filling up the array with null, so the array memory will no longer contain references to particular String instances).
So if somebody will find the strings content in String pool memory (that may be kept intact for much longer time, than the GC managed memory pool), they will have to figure out which 10 strings were together in the array - from the strings content itself.
Then again if your code is creating and assigning the array and 10 strings in the same place and time, it's very likely the String pool memory will allocate all 10 strings near each other, so even without the array data it may be easy to spot those 10 strings belong to each other (especially if the content of sensitive strings is distinct from other ordinary strings you use in the code).
You may consider creating your own "SecureString" class implementing CharSequence interface over char[], and having destructor cleaning up the content.. oh wait, this is stupid Java, not C++. Hmmm... bad luck then, you would have to call some .wipe() method manually to make sure those "SecureString" instances get wiped out as soon, as you want it.
And of course you would have to guard yourself to not cast those into String ... which may happen quite easily if you implement CharSequence, so that's probably not very clever idea, from security point of view it's maybe best to have some SecureString class which doesn't implement any common interface (and overloads toString to return something non-sensitive, maybe even removing reference address, maybe just few asterisks for fun). And just endure the pain of using it as such.
To create passwords that can be cleaned up safely, use char[] and fill it with 0 as soon as password verification is done and you no longer need the password.
//example password
char[] pass = {'t', 'e', 's', 't', '_', 'p', 'a', 's', 's'};
//validate your password here
boolean isValid = validatePass(pass);
//now explicitly override the password's characters
for (int c = 0; c < pass.length; c++)
pass[c] = 0;
//do something depending isValid
if (isValid) {
//show logged user or whatever
} else {
//show error message or whatever
}
I FINALLY have the map and points(arrays) working for my app. Quick question: I have a fatal exception with substring(), a "stringIndexOutOfBoundException"
In general, what is that referring to?
An I going past the end of a string using substring()?
Thanks,
testing.substring(1,2);
(I want to parse each character to find specific characters)
I wouldn't use substring() for grabbing 1-length strings (which is just a single character), but rather charAt(int) for specific positions. If you need to go over all characters in the string, you're probably better off with by converting the whole thing to a char[] first (using toCharArray()) and iterate over that.
Yes, you're going past the end of your strings bounds generally.
The Java API even tells you so...
IndexOutOfBoundsException - if beginIndex is negative or larger than the length of this String object.
You should get used to using the API. It tells you what exceptions a method throws and why.
Try printing the Strings length and value before attempting substring. That'll help you see the problem.
For example...
String testing = "Hello StackOverflow";
System.out.println("Length of testing = " + testing.length);
System.out.println("Value of testing = " + testing);
testing.substring(1,2);
Like stated in the official doc here:
public String substring(int beginIndex)
Returns a new string that is a substring of this string. The substring begins with the character at the specified index and extends to the end of this string.
Throws: IndexOutOfBoundsException - if beginIndex is negative or
larger than the length of this String object.
I have a very basic TCP socket connection to a remote device that I can poll for status.
Aside from the socket programming, which I have mostly figured out through asynctask, I'm trying to come up with a way to parse out the returning string.
I query the device with something like "VOL?"
The device responds with the Volume of 12 different audio outputs with this:
"VOL:33,0,21,12,0,43,0,0,0,0,20,0"
The ":" character always and only comes back after the echo of the initial command, so I can use whatever comes before the colon to flag what sort of answer is coming in. (VOL, BAS, MUT, TRE, BAL, etc)
In the case of VOL, I simply want to chunk out everything that comes between the commas, so I can chop up and place into an array the volumes of all zones.
The only thing I can think of is to grab the length of the string, then run a for loop through it searching for commas one by one, but it seems ridiculously messy:
int oldPos = 0; //used in the upcoming 'if clause' to mark where the last comma was found
int y = 0; //used to old the resulting value's array position
String strIncoming; = //the incoming TCP string
for(int x = 0; x <= strIncoming.length(); x++){
if(",".equals(strIncoming[x]){
volzoneVal[y] = strIncoming.subString(oldPos,x);
oldPos = x;
y++;
}
}
there has GOT to be a better way, (and I'm not even sure this is going to work, I'm typing it here for the first time as I brainstorm this problem, so it's not been run or compiled)
Is there a better way to scan through a string looking for hits?
strIncoming.split(":")[0] will give you what was before first colon
strIncoming.split(":")[1].split(",") will give you array of individual strings
First, split the string on the colon, and then split[0] is your type. Then take split[1] and split it on the comma, and you'll have all your 12 different outputs ready to go (just convert them to integers).
Use Java's string split function and split on the comma as the delimiter. You will then have an array of your parameters. If you append some kind of "end string" character to each response, you will know the start and end based on the colon for the start and your end character for the end.
I have a number (123456) converted to a hash key and stored in SharedPrefs using:
String correctMd5 = passwdfile.getString(PhoneFinder.PASSWORD_PREF_KEY, null);
I then retreive the number from a string:
String[] tokens = msg.getMessageBody().split(":");
String md5hash = PhoneFinder.getMd5Hash(tokens[1]);
and compare the two:
if (correctMd5 == md5hash) {
Toast.makeText(context, "Hash OK: " + md5hash, Toast.LENGTH_SHORT).show();
}
However, this check does not complete succesfully.
If I convert to strings and display them, the hashes are the same, however if I convert to bytes the 4 right most bytes are different. I assume some special character is hidden in there somewhere, how do I check and kill it?
You should probably use correctMd5.equals(md5hash) instead of the correctMd5 == md5hash.
Is it solving the problem ?
The problem appears to be:
correctMd5 == md5hash
Because a String is an Object in Java (Android) this will compare the Object, not its String value. For instance, if you have two different variables they might be in 2 different memory locations, or they might be references to the same memory location.
On the other hand, if you want to find out if the VALUES stored by the memory location are equal, you should use
correctMd5.equals(md5hash)
In your conditional, you could have two strings "1000" and "1000" but stored at different memory locations. In my conditional, it will still be true regardless of memory location, if the String values are equal.
If == is true, .equals() should be true (in most cases, if not all). But if .equals() is true, there is no guarantee that == is true.
The Javadoc for this says:
Only the lower two bytes of the integer oneChar are written.
What effect, if any, does this have on writing non-utf8 encoded chars which have been cast to an int?
Update:
The code in question receives data from a socket and writes it to a file. (A lot of things happen between receiving and writing, so I can't just use the string I get using BufferedReader#readLine()). I was using Writer#write(char[]) but this meant I had to create a new char array each time. To get around creating an array everytime, I had a single char array which is filled with -1 (cast to a char).
I then use TextUtils#getChars to fill it, expanding the array if necessary. For writing, I loop through the array, writing to the Writer until char[i] == (char) -1 == true.
Internally, write(int) will just cast its parameter to char, so write(i) is equivalent to write((char)i).
Now in Java, internally char is just an integer type, with the range 0-65535 (i.e. 16 bit). The cast int -> char is a "narrowing primitive conversion" (Java Language spec, 5.1.3), and int is a signed integer, hence:
A narrowing conversion of a signed
integer to an integral type T simply
discards all but the n lowest order
bits, where n is the number of bits
used to represent type T. In addition
to a possible loss of information
about the magnitude of the numeric
value, this may cause the sign of the
resulting value to differ from the
sign of the input value.
That's why the Javadoc says that only the lower two bytes are written.
Now, what this means in terms of characters depends on how you want to interpret the int values. A char in Java represents a Unicode code point in UTF-16, that is the 16 bit number represented by the char is interpreted as the number of the Unicode code point. So if each of your int values is the number of a 16 bit code point, you're fine (actually, this is only true for characters in the BMP; if you use characters in the supplementary planes, each Unicode code point will be encoded into two chars). If it's anything else (including a code point with more than 16 bit, or a negative number, or something else entirely), you'll get garbage.
What effect, if any, does this have on
writing non-utf8 chars which have been
cast to an int?
There is no such thing as a "non-utf8 char". UTF-8 is an encoding, that is a way to represent a Unicode code point, so the question as posed is meaningless. Maybe you could explain what your code does?