4

I need to decode a Base64 char array without converting it to a String. The char array is a password, and for security reasons I am not allowed to convert it to a String (this requirement is non-negotiable).

The java.util.Base64.Decoder.decode method accepts byte[], ByteBuffer, and String, but not char[].

Security concerns of using a String to store sensitive data

per Jim Archer's comment

  • Strings are immutable
  • They can only be purged from memory by the Garbage Collector (which cannot be forced to do it)
Groppe
  • 3,819
  • 12
  • 44
  • 68
  • 4
    You can use [this answer](http://stackoverflow.com/a/9670279/1361506) to convert your `char[]` into a `byte[]` without an intermediate `String`, and then invoke `decode(byte[])`. – azurefrog Dec 23 '15 at 17:19
  • Why is the decoding method using a byte array unsuitable? – Raedwald Dec 23 '15 at 17:49
  • 5
    BTW, in case you (or anyone reading) does not know, the security issue arises from the fact that (1) Strings are immutable and (2) they hang around waiting for the GC to reclaim them, and you can't force the GC to do it. StringBuffer, on the other hand, solves this problem because you can destroy the contents when you're done with it. – Jim Archer Dec 23 '15 at 17:50
  • @JimArcher How do you destroy the contents of the `StringBuffer`? Can the same be done with a `CharBuffer`? `CharBuffer` has a `clear()` method but it is not guaranteed to delete the contents. Part of my question is answered here: http://stackoverflow.com/questions/5513144/converting-char-to-byte/9670279#9670279 – Roland Feb 22 '17 at 13:55
  • @Roland Here are two ways off the top of my head: sb.delete(0, sb.length()); and sb.setLength(0); The setLength method will set the contents to \u0000. – Jim Archer Feb 22 '17 at 16:34
  • @Roland also, as you said, the clear() method of CharBuffer DOES NOT destroy or overwrite the data, it just resets the internal pointer. Don't use this for data that must remain secure, use StringBuffer and it's setLength() method instead. I just looked through the docs and didn't see a way to guarantee data destruction with a CharBuffer. – Jim Archer Feb 22 '17 at 16:44
  • @JimArcher clearing a `XXXXBuffer` like `CharBuffer`: `Arrays.fill(charBuffer.array(), (char) 0);` works only if the `CharBuffer` is backed by an array. – Roland Feb 23 '17 at 07:11
  • @JimArcher I just looked into the `StringBuffer` doc. `setLength` will only set new characters to '\u0000' if you are *increasing* the length. – Roland Feb 23 '17 at 07:22

1 Answers1

5

Create a CharBuffer backed by the char[]. Then use Charset.encode to encode the byte buffer into a ByteBuffer. A ByteBuffer is accepted by the Base64 Decoder.

private static final java.util.Base64.Decoder BASE_64_DECODER= new java.util.Base64.Decoder();

private static final String ENCODING = "UTF-8";// Use the correct encoding here.

private byte[] decodePassword(char[] password) {
    CharBuffer charBuffer = CharBuffer.wrap(password);
    ByteBuffer byteBuffer = Charset.forName(ENCODING).encode(charBuffer);
    return BASE_64_DECODER.decode(byteBuffer);
}

Inspired by azurefox's comment and the answer here: https://stackoverflow.com/a/9670279/1361506

Community
  • 1
  • 1
Adam
  • 43,763
  • 16
  • 104
  • 144
  • How do I know which character encoding set to use? It isn't specified in the current implementation. – Groppe Dec 23 '15 at 20:26
  • @Groppe What is the source of the text? – Adam Dec 23 '15 at 20:27
  • It is a password supplied by PasswordCallback.getPassword (javax.security.auth.callback), which is populated in the interface by user input. – Groppe Dec 23 '15 at 20:33
  • 1
    @Groppe I'm not sure. My guess is that it uses the encoding that was used in the form post. You can set that to be UTF-8: http://stackoverflow.com/a/708942/284685 – Adam Dec 23 '15 at 20:37
  • Oh, wow... it's interesting how decode(ByteBuffer) works without copying but encode(ByteBuffer) does not. I had assumed that because encode() wasn't handling it securely, the same would be the case for decode() as well. But I guess things don't have to be completely symmetrical. – Hakanai Apr 13 '18 at 03:50
  • The BASE_64_DECODER should be declared like this: private static final java.util.Base64.Decoder BASE_64_DECODER = java.util.Base64.getDecoder(); – Arthur Borsboom Mar 29 '23 at 08:07