2011-02-08 141 views
34

我正在尋找將Java char數組轉換爲字節數組而不創建中間String,因爲char數組包含密碼。我擡頭一對夫婦的方法,但他們都忽視了:將char數組轉換爲字節數組並再次返回

char[] password = "password".toCharArray(); 

byte[] passwordBytes1 = new byte[password.length*2]; 
ByteBuffer.wrap(passwordBytes1).asCharBuffer().put(password); 

byte[] passwordBytes2 = new byte[password.length*2]; 
for(int i=0; i<password.length; i++) { 
    passwordBytes2[2*i] = (byte) ((password[i]&0xFF00)>>8); 
    passwordBytes2[2*i+1] = (byte) (password[i]&0x00FF); 
} 

String passwordAsString = new String(password); 
String passwordBytes1AsString = new String(passwordBytes1); 
String passwordBytes2AsString = new String(passwordBytes2); 

System.out.println(passwordAsString); 
System.out.println(passwordBytes1AsString); 
System.out.println(passwordBytes2AsString); 
assertTrue(passwordAsString.equals(passwordBytes1) || passwordAsString.equals(passwordBytes2)); 

斷言總是失敗(並且,重要的是,當代碼中,該密碼將被拒絕使用),但打印報表三次打印出密碼。爲什麼passwordBytes1AsStringpasswordBytes2AsStringpasswordAsString不同,但看起來完全相同?我錯過了一個空終止符或什麼?我能做些什麼來使轉換和非轉換工作?

+0

爲什麼你要避免創建一箇中間串? – KarlP 2011-02-08 10:53:38

+12

Sun建議將其作爲最佳實踐:http://download.oracle.com/javase/1.5.0/docs/guide/security/jce/JCERefGuide.html#PBEEx字符串是不可變的,因此無法像清零一樣字符數組 - 相反,您的密碼在內存中停留的時間不確定。 – Scott 2011-02-08 11:09:30

回答

12

問題是您使用的String(byte[])構造函數,它使用平臺默認編碼。這幾乎是從來沒有你應該做的 - 如果你傳遞「UTF-16」作爲字符編碼工作,你的測試可能會通過。目前我懷疑passwordBytes1AsStringpasswordBytes2AsString每個長度爲16個字符,每個其他字符是U + 0000。

+0

我剛剛嘗試過(即`字符串passwordBytes1AsString =新的字符串(passwordBytes1,「UTF-16」);`),並沒有改變。我也嘗試檢查字符串的長度 - `String.length()`返回8.它會計算U + 0000個字符嗎? – Scott 2011-02-08 10:33:50

+0

@Scott:嘗試打印出字符串的長度和單個字符(如int值)。這會告訴你差異在哪裏。 – 2011-02-08 10:36:17

+0

112,97,115,115,119,111,114,100原始和轉換的。 – Scott 2011-02-08 10:41:48

4

如果要使用ByteBuffer和CharBuffer,請不要執行簡單的.asCharBuffer(),它只是執行UTF-16(LE或BE,取決於您的系統),您可以使用order方法設置字節順序)轉換(因爲Java字符串,因此您的char[]內部使用此編碼)。

使用Charset.forName(charsetName),然後將其encodedecode法或newEncoder/newDecoder

將字節[]轉換爲字符串時,還應指明編碼(它應該是同一個)。

4

我會做的是使用循環轉換爲字節和另一個轉換回字符。

char[] chars = "password".toCharArray(); 
byte[] bytes = new byte[chars.length*2]; 
for(int i=0;i<chars.length;i++) { 
    bytes[i*2] = (byte) (chars[i] >> 8); 
    bytes[i*2+1] = (byte) chars[i]; 
} 
char[] chars2 = new char[bytes.length/2]; 
for(int i=0;i<chars2.length;i++) 
    chars2[i] = (char) ((bytes[i*2] << 8) + (bytes[i*2+1] & 0xFF)); 
String password = new String(chars2); 
2

你應該利用的getBytes()代替toCharArray()

與char和字節之間

byte[] password = "password".getBytes(); 
11

轉換替換行

char[] password = "password".toCharArray(); 

是字符集編碼和解碼。我更願意在合作中儘可能地表達清楚德。它並不真正意味着額外的代碼量:

Charset latin1Charset = Charset.forName("ISO-8859-1"); 
charBuffer = latin1Charset.decode(ByteBuffer.wrap(byteArray)); // also decode to String 
byteBuffer = latin1Charset.encode(charBuffer);     // also decode from String 

旁白:

java.nio中的類java.io和讀/寫器類使用的ByteBuffer & CharBuffer的(使用的byte []和炭[ ]作爲後備陣列)。如果您直接使用這些類,那麼通常更可取。但是,您始終可以這樣做:

byteArray = ByteBuffer.array(); byteBuffer = ByteBuffer.wrap(byteArray); 
byteBuffer.get(byteArray);  charBuffer.put(charArray); 
charArray = CharBuffer.array(); charBuffer = ByteBuffer.wrap(charArray); 
charBuffer.get(charArray);  charBuffer.put(charArray); 
2

這是Peter Lawrey的答案的擴展。爲了向後(字節到字符)轉換正常工作的一系列字符,代碼應該如下:

char[] chars = new char[bytes.length/2]; 
for (int i = 0; i < chars.length; i++) { 
    chars[i] = (char) (((bytes[i*2] & 0xff) << 8) + (bytes[i*2+1] & 0xff)); 
} 

我們需要使用(& 0xff)之前「unsign」字節。否則,所有可能的char值的一半將無法正確恢復。例如,[0x80..0xff]範圍內的字符將受到影響。

1

從Java中的字符串使用GetBytes時,返回結果將取決於計算機設置的默認編碼(例如:StandardCharsetsUTF-8或StandardCharsets.ISO_8859_1etc ...)。

所以,只要你想從一個字符串對象getBytes。確保提供編碼。像:

String sample = "abc"; 
Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_8); 

讓我們來看看代碼發生了什麼。 在java中,名爲sample的字符串按Unicode存儲。字符串中的每個字符都以2個字節存儲。

sample : value: "abc" in Memory(Hex): 00 61 00 62 00 63 
     a -> 00 61 
     b -> 00 62 
     c -> 00 63 

但是,當我們的getBytes從一個字符串,我們有

Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_8) 
//result is : 61 62 63 
//length: 3 bytes 

Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_16BE) 
//result is : 00 61 00 62 00 63   
//length: 6 bytes 

爲了得到字符串的oringle字節。我們可以只讀取字符串的內存,並獲得String.Below的每個字節是示例代碼:

public static byte[] charArray2ByteArray(char[] chars){ 
    int length = chars.length; 
    byte[] result = new byte[length*2+2]; 
    int i = 0; 
    for(int j = 0 ;j<chars.length;j++){ 
     result[i++] = (byte)((chars[j] & 0xFF00) >> 8); 
     result[i++] = (byte)((chars[j] & 0x00FF)) ; 
    } 
    return result; 
} 

用途:

String sample = "abc"; 
//First get the chars of the String,each char has two bytes(Java). 
Char[] sample_chars = sample.toCharArray(); 
//Get the bytes 
byte[] result = charArray2ByteArray(sample_chars). 

//Back to String. 
//Make sure we use UTF_16BE. Because we read the memory of Unicode of 
//the String from Left to right. That's the same reading 
//sequece of UTF-16BE. 
String sample_back= new String(result , StandardCharsets.UTF_16BE); 
4
public byte[] charsToBytes(char[] chars){ 
     Charset charset = Charset.forName("UTF-8"); 
     ByteBuffer byteBuffer = charset.encode(CharBuffer.wrap(chars)); 
     return Arrays.copyOf(byteBuffer.array(), byteBuffer.limit()); 
    } 

    public char[] bytesToChars(byte[] bytes){ 
     Charset charset = Charset.forName("UTF-8"); 
     CharBuffer charBuffer = charset.decode(ByteBuffer.wrap(bytes)); 
     return Arrays.copyOf(charBuffer.array(), charBuffer.limit());  
    } 
相關問題