このトピックには多くの質問がありますが、同じ解決策ですが、これはうまくいきません。暗号化を使用した簡単なテストがあります。暗号化/復号化自体は機能します(このテストを文字列としてではなく、バイト配列自体で処理する限り)。問題は、それをバイト配列としてではなくストリングとして処理したいのですが、バイト配列をストリングにエンコードしてから戻すと、結果のバイト配列が元のバイト配列と異なるため、復号化が機能しなくなります。対応する文字列メソッドで次のパラメーターを試しました:UTF-8、UTF8、UTF-16、UTF8。それらのどれも動作しません。結果のバイト配列は元の配列とは異なります。これがなぜそうなのでしょうか?
暗号化者:
public class NewEncrypter
{
private String algorithm = "DESede";
private Key key = null;
private Cipher cipher = null;
public NewEncrypter() throws NoSuchAlgorithmException, NoSuchPaddingException
{
key = KeyGenerator.getInstance(algorithm).generateKey();
cipher = Cipher.getInstance(algorithm);
}
public byte[] encrypt(String input) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] inputBytes = input.getBytes("UTF-16");
return cipher.doFinal(inputBytes);
}
public String decrypt(byte[] encryptionBytes) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] recoveredBytes = cipher.doFinal(encryptionBytes);
String recovered = new String(recoveredBytes, "UTF-16");
return recovered;
}
}
これは私がそれを試すテストです:
public class NewEncrypterTest
{
@Test
public void canEncryptAndDecrypt() throws Exception
{
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
System.out.println("encryptedByteArray:" + encryptedByteArray);
String decoded = new String(encryptedByteArray, "UTF-16");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("UTF-16");
System.out.println("encoded:" + encoded);
String decryptedText = encrypter.decrypt(encoded); //Exception here
System.out.println("decryptedText:" + decryptedText);
assertEquals(toEncrypt, decryptedText);
}
}
暗号化されたデータは、任意のバイナリデータではなく、人間が読み取れるテキスト用であるため、文字列に保存することはお勧めできません。バイナリデータの場合は、byte[]
。
ただし、mustを行う場合は、バイトと文字の間に1-to-1マッピングを持つエンコードを使用する必要があります、つまり、すべてのバイトシーケンスを一意の文字シーケンスにマッピングしたり、戻したりすることができます。そのようなエンコーディングの1つはISO-8859-1です。つまり:
String decoded = new String(encryptedByteArray, "ISO-8859-1");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("ISO-8859-1");
System.out.println("encoded:" + Java.util.Arrays.toString(encoded));
String decryptedText = encrypter.decrypt(encoded);
データを失わない他の一般的なエンコーディングはhexadecimalとbase64ですが、残念ながらそれらのヘルパーライブラリが必要です。標準APIはそれらのクラスを定義しません。
UTF-16では、プログラムは次の2つの理由で失敗します。
String
にš, ž, ć, Ō, ō, Ū
などの一般的でない文字が含まれている場合、承認されたソリューションは機能しません。
次のコードは私にとってはうまくいきました。
byte[] myBytes = Something.getMyBytes();
String encodedString = Base64.encodeToString(bytes, Base64.NO_WRAP);
byte[] decodedBytes = Base64.decode(encodedString, Base64.NO_WRAP);
今、私も別の解決策を見つけました...
public class NewEncrypterTest
{
@Test
public void canEncryptAndDecrypt() throws Exception
{
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
String encoded = String.valueOf(Hex.encodeHex(encryptedByteArray));
byte[] byteArrayToDecrypt = Hex.decodeHex(encoded.toCharArray());
String decryptedText = encrypter.decrypt(byteArrayToDecrypt);
System.out.println("decryptedText:" + decryptedText);
assertEquals(toEncrypt, decryptedText);
}
}
問題は、任意のバイト配列からUTF-16(またはその他のエンコーディング)文字列を構築できないことです(ウィキペディアの TF-16 を参照)。ただし、暗号化されたバイト配列を損失することなくシリアル化および逆シリアル化して、たとえば永続化して後で使用するのはユーザー次第です。以下は、バイト配列で実際に何が起こっているかについての洞察を提供する修正されたクライアントコードです。
_public static void main(String[] args) throws Exception {
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
System.out.println("encryptedByteArray:" + Arrays.toString(encryptedByteArray));
String decoded = new String(encryptedByteArray, "UTF-16");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("UTF-16");
System.out.println("encoded:" + Arrays.toString(encoded));
String decryptedText = encrypter.decrypt(encryptedByteArray); // NOT the "encoded" value!
System.out.println("decryptedText:" + decryptedText);
}
_
これは出力です:
_encryptedByteArray:[90, -40, -39, -56, -90, 51, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decoded:<some garbage>
encoded:[-2, -1, 90, -40, -1, -3, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decryptedText:FOOBAR
_
元のdecryptedText
から復元した場合、encryptedByteArray
は正しいです。 byte[] -> String("UTF-16")->byte[]
変換中のデータ損失のため、encoded
値はencryptedByteArray
と同じではないことに注意してください。