我正在生成一个密钥,并需要将其存储在数据库中,因此我将其转换为字符串,但是要从字符串中获取密钥,有哪些可能的方法可以实现?
我的代码如下:
SecretKey key = KeyGenerator.getInstance("AES").generateKey();
String stringKey=key.toString();
System.out.println(stringKey);
我该如何从字符串中获取键值?
我正在生成一个密钥,并需要将其存储在数据库中,因此我将其转换为字符串,但是要从字符串中获取密钥,有哪些可能的方法可以实现?
我的代码如下:
SecretKey key = KeyGenerator.getInstance("AES").generateKey();
String stringKey=key.toString();
System.out.println(stringKey);
我该如何从字符串中获取键值?
您可以将 SecretKey
转换为字节数组 (byte[]
),然后对其进行 Base64 编码以获得一个 String
。要将其转换回 SecretKey
,请对该字符串进行 Base64 解码,并在 SecretKeySpec
中使用它来重建原始的 SecretKey
。
将 SecretKey 转换为 String:
// create new key
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
// get base64 encoded version of the key
String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());
将字符串转换为SecretKey:
// decode the base64 encoded string
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
// rebuild key using SecretKeySpec
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
注意1:你可以跳过Base64编码/解码部分,直接将byte[]
存储在SQLite中。不过,执行Base64编码/解码并不是一项昂贵的操作,你可以在几乎任何DB中存储字符串而不会出现问题。
注意2:早期的Java版本没有在java.lang
或java.util
包中提供Base64。但可以使用来自Apache Commons Codec、Bouncy Castle或Guava的编解码器。
将SecretKey转换为字符串:
// CREATE NEW KEY
// GET ENCODED VERSION OF KEY (THIS CAN BE STORED IN A DB)
SecretKey secretKey;
String stringKey;
try {secretKey = KeyGenerator.getInstance("AES").generateKey();}
catch (NoSuchAlgorithmException e) {/* LOG YOUR EXCEPTION */}
if (secretKey != null) {stringKey = Base64.encodeToString(secretKey.getEncoded(), Base64.DEFAULT)}
字符串转SecretKey:
// DECODE YOUR BASE64 STRING
// REBUILD KEY USING SecretKeySpec
byte[] encodedKey = Base64.decode(stringKey, Base64.DEFAULT);
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
时,我会收到错误提示需要SecretKey,提供的是SecretKeySpec
。请更新解决方案。谢谢。 - Because i hate myselfpublic static SecretKey generateAESKey(int keysize)
throws InvalidParameterException {
try {
if (Cipher.getMaxAllowedKeyLength("AES") < keysize) {
// this may be an issue if unlimited crypto is not installed
throw new InvalidParameterException("Key size of " + keysize
+ " not supported in this runtime");
}
final KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(keysize);
return keyGen.generateKey();
} catch (final NoSuchAlgorithmException e) {
// AES functionality is a requirement for any Java SE runtime
throw new IllegalStateException(
"AES should always be present in a Java SE runtime", e);
}
}
public static SecretKey decodeBase64ToAESKey(final String encodedKey)
throws IllegalArgumentException {
try {
// throws IllegalArgumentException - if src is not in valid Base64
// scheme
final byte[] keyData = Base64.getDecoder().decode(encodedKey);
final int keysize = keyData.length * Byte.SIZE;
// this should be checked by a SecretKeyFactory, but that doesn't exist for AES
switch (keysize) {
case 128:
case 192:
case 256:
break;
default:
throw new IllegalArgumentException("Invalid key size for AES: " + keysize);
}
if (Cipher.getMaxAllowedKeyLength("AES") < keysize) {
// this may be an issue if unlimited crypto is not installed
throw new IllegalArgumentException("Key size of " + keysize
+ " not supported in this runtime");
}
// throws IllegalArgumentException - if key is empty
final SecretKeySpec aesKey = new SecretKeySpec(keyData, "AES");
return aesKey;
} catch (final NoSuchAlgorithmException e) {
// AES functionality is a requirement for any Java SE runtime
throw new IllegalStateException(
"AES should always be present in a Java SE runtime", e);
}
}
public static String encodeAESKeyToBase64(final SecretKey aesKey)
throws IllegalArgumentException {
if (!aesKey.getAlgorithm().equalsIgnoreCase("AES")) {
throw new IllegalArgumentException("Not an AES key");
}
final byte[] keyData = aesKey.getEncoded();
final String encodedKey = Base64.getEncoder().encodeToString(keyData);
return encodedKey;
}
getEncoded()
方法),则存储/检索密钥可能无法正常工作。 - Maarten Bodewes实际上,Luis提出的方法对我没有用。我不得不想出另一种方法。这是帮助我的方法,也可能对您有所帮助。 链接:
*.getEncoded(): https://docs.oracle.com/javase/7/docs/api/java/security/Key.html
编码器信息:https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html
解码器信息:https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html
代码片段: 用于编码:
String temp = new String(Base64.getEncoder().encode(key.getEncoded()));
解码:
byte[] encodedKey = Base64.getDecoder().decode(temp);
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "DES");
将SecretKeySpec转换为字符串和反之:
您可以使用SecretKeySpec
中的getEncoded()
方法,它将给出byteArray
,从中您可以使用encodeToString()
来获取Base64
对象中SecretKeySpec
的string
值。
在将SecretKeySpec
转换为String
时:使用Base64
中的decode()
将给出byteArray
,从中您可以创建一个实例,参数为byteArray
,以重现您的SecretKeySpec
。
String mAesKey_string;
SecretKeySpec mAesKey= new SecretKeySpec(secretKey.getEncoded(), "AES");
//SecretKeySpec to String
byte[] byteaes=mAesKey.getEncoded();
mAesKey_string=Base64.encodeToString(byteaes,Base64.NO_WRAP);
//String to SecretKeySpec
byte[] aesByte = Base64.decode(mAesKey_string, Base64.NO_WRAP);
mAesKey= new SecretKeySpec(aesByte, "AES");
您不想使用.toString()
。
请注意,SecretKey继承自java.security.Key,后者又继承自Serializable。因此,关键(无意双关语)是将密钥序列化为ByteArrayOutputStream,获取byte[]数组并将其存储到数据库中。反向过程是从数据库获取byte[]数组,创建一个ByteArrayInputStream,并对其进行反序列化以获取SecretKey...
...或者更简单地,只需使用继承自java.security.Key的.getEncoded()
方法(它是SecretKey的父接口之一)。该方法返回Key/SecretKey的编码byte[]数组,您可以将其存储或从数据库中检索。
所有这些都假定您的SecretKey实现支持编码。否则,getEncoded()
将返回null。
您应该查看Key/SecretKey javadocs(在谷歌页面的开头就可找到):
http://download.oracle.com/javase/6/docs/api/java/security/Key.html
或者这个来自CodeRanch(也可以通过相同的谷歌搜索找到):
http://www.coderanch.com/t/429127/java/java/Convertion-between-SecretKey-String-or
尝试这个,它可以在没有Base64的情况下工作(Base64仅包含在JDK 1.8中),这段代码也可以在之前的Java版本中运行 :)
private static String SK = "Secret Key in HEX";
// To Encrupt
public static String encrypt( String Message ) throws Exception{
byte[] KeyByte = hexStringToByteArray( SK);
SecretKey k = new SecretKeySpec(KeyByte, 0, KeyByte.length, "DES");
Cipher c = Cipher.getInstance("DES","SunJCE");
c.init(1, k);
byte mes_encrypted[] = cipher.doFinal(Message.getBytes());
String MessageEncrypted = byteArrayToHexString(mes_encrypted);
return MessageEncrypted;
}
// To Decrypt
public static String decrypt( String MessageEncrypted )throws Exception{
byte[] KeyByte = hexStringToByteArray( SK );
SecretKey k = new SecretKeySpec(KeyByte, 0, KeyByte.length, "DES");
Cipher dcr = Cipher.getInstance("DES","SunJCE");
dc.init(Cipher.DECRYPT_MODE, k);
byte[] MesByte = hexStringToByteArray( MessageEncrypted );
byte mes_decrypted[] = dcipher.doFinal( MesByte );
String MessageDecrypeted = new String(mes_decrypted);
return MessageDecrypeted;
}
public static String byteArrayToHexString(byte bytes[]){
StringBuffer hexDump = new StringBuffer();
for(int i = 0; i < bytes.length; i++){
if(bytes[i] < 0)
{
hexDump.append(getDoubleHexValue(Integer.toHexString(256 - Math.abs(bytes[i]))).toUpperCase());
}else
{
hexDump.append(getDoubleHexValue(Integer.toHexString(bytes[i])).toUpperCase());
}
return hexDump.toString();
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2)
{
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
String
实例,而键对象和字节数组可以被清除。这意味着键可能会在内存中保持更长时间。最好使用(受密码保护的)KeyStore
,最好是由运行时系统/操作系统或甚至硬件支持的KeyStore
。 - Maarten Bodewes