2010-05-26 63 views
4

在类似的问题"Conversion of byte[] into a String and then back to a byte[]"据说不做字节[]来进行字符串和回转换,看起来像适用于大多数情况下,主要是当你不喜欢不知道使用的编码。AES密钥编码字节[]到字符串并返回到字节[]

但是,在我的情况下,我试图保存到数据库的javax.crypto.SecretKey数据,然后恢复它。

该接口提供getEncoded()方法,该方法返回编码为byte []的密钥数据,并且与另一个类我可以使用此byte []来恢复密钥。

所以,问题是,我如何将关键字节写成字符串,然后取回字节[]来重新生成关键字?

回答

6

javax.crypto.SecretKey是二进制数据,所以你不能直接将它转换为字符串。您可以将其编码为十六进制字符串或Base64。

请参阅Apache Commons Codec

更新:如果您不想依赖第三方库(并且不能/不希望存储纯二进制数据,正如Jon所建议的那样),您可以执行一些特殊编码,例如,按照埃里克森的建议:

public static String bytesToString(byte[] b) { 
    byte[] b2 = new byte[b.length + 1]; 
    b2[0] = 1; 
    System.arraycopy(b, 0, b2, 1, b.length); 
    return new BigInteger(b2).toString(36); 
} 

public static byte[] stringToBytes(String s) { 
    byte[] b2 = new BigInteger(s, 36).toByteArray(); 
    return Arrays.copyOfRange(b2, 1, b2.length); 
} 

这是相当丑陋,非标准,并非最佳(在输出尺寸)。但它也非常小,正确,没有依赖关系;它可能是实用的,特别是如果你的数据量很小。

更新:根据GregS的评论,我用字面值(36)替换了Character.MAX_RADIX。它看起来可能不太优雅,但实际上更安全。 (你也可以使用32或16)。

+0

一个很好的解决方案,它甚至占据了领先的零。如果我可能挑剔,我会选择自己的常量,大概32或16,而不是Character.MAX_RADIX。未来版本的java.lang.Character可能会使用比36更大的值。 – 2010-05-26 23:28:18

+0

@GregS:你说得对。顺便说一句,Character.MAX_RADIX目前是363 – leonbloy 2010-05-27 00:55:31

+0

虽然答案是正确的,我想知道:你的意思是“SecretKey是二进制数据”吗?对我而言,在某种程度上,一切都是二进制数据。你能给出一个不是二进制数据的例子吗?谢谢! – 2010-06-02 18:52:30

0

你不能使用String构造函数:字符串(字节[]数据,字符串的charsetName),如:

byte[] data=new byte[1024]; 
String key=new String(data,"UTF-8"); 

,以后你可以这样做:

String key="mymostsecretaeskey"; 
byte[] data=key.getBytes("UTF-8"); 
+6

非常糟糕的想法 - 数据将是任意的二进制数据,并不总是一个有效的UTF-8序列。 – 2010-05-26 17:21:49

+0

啊好吧现在我得到的问题...是的,忘记这个答案:) – fasseg 2010-05-26 17:23:37

+0

但是,当一个调用getEncoded()时,不会使用系统默认字符集编码的密钥?那么你可以调用key.getBytes()和新的字符串(byte []数据)... – fasseg 2010-05-26 17:26:21

3

使用碱基64编码安全地将任意二进制数据转换为字符串并返回。

Apache Commons Codec图书馆为此提供了代码,以及其他各种代码。 (我不是在API到Apache共享编解码器非常敏锐的,诚然,我不知道这个功能任何其他库副手,但我敢肯定,他们的存在。)

编辑:This project提供一个用于快速编码和解码的单个文件,并且具有理智的方法签名 - 以及大量额外的选项。或者,如果要将它保存到数据库,为什么不使用适当的二进制数据字段(图像/ Blob或任何适用于您的数据库)而不是将它作为字符串存储在第一位?

+0

它bec bec我是DB新手,我使用Derby和硬编码SQL(以String作为参数)。 A可以更好地用数据库与时间.. :) – 2010-05-26 17:38:06

+0

@Tom:我会尝试修复SQL而不是转换为字符串,除非Derby不支持二进制数据。如果你确实真的希望坚持一个字符串,Base64肯定会起作用。 – 2010-05-26 17:46:39

+0

Derby sql语句只支持字符串作为参数,我选择使用Apache Commons Code Hex类进行解码,因为我已经对它有所了解了。 – 2010-05-26 18:49:37

1

如何编码为数字而不是字符串?它不像Base-64那么紧凑,但是你可以不用Apache Commons。

/* Store the key. */ 
BigInteger n = new BigInteger(1, key.getEncoded()); /* Store as NUMBER, or … */ 
String s = n.toString(32);       /* … store as VARCHAR. */ 

/* Reconstruct the key (may need to pad most significant bytes with zero). */ 
BigInteger n = new BigInteger(s); /* Load from VARCHAR, or … */ 
byte[] raw = n.toByte();   /* … load from NUMBER. */ 
byte[] original = new byte[16]; 
System.arraycopy(raw, 0, original, 16 - raw.length, raw.length); 
+0

在某些情况下(我用过一次) 。我做了一些更正(例如,我认为这段代码在开始时并不保留零字节)并添加到我的文章中。 – leonbloy 2010-05-26 18:49:52

+0

它通过将值复制到目标数组的最低有效字节中来恢复零字节。 – erickson 2010-05-26 22:23:14