Converting Secret Key into a String and Vice Versa
You can convert the SecretKey
to a byte array (byte[]
), then Base64 encode that to a String
. To convert back to a SecretKey
, Base64 decode the String and use it in a SecretKeySpec
to rebuild your original SecretKey
.
For Java 8
SecretKey to String:
// create new key
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
// get base64 encoded version of the key
String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());
String to 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");
For Java 7 and before (including Android):
NOTE I: you can skip the Base64 encoding/decoding part and just store the byte[]
in SQLite. That said, performing Base64 encoding/decoding is not an expensive operation and you can store strings in almost any DB without issues.
NOTE II: Earlier Java versions do not include a Base64 in one of the java.lang
or java.util
packages. It is however possible to use codecs from Apache Commons Codec, Bouncy Castle or Guava.
SecretKey to String:
// 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)}
String to 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");
Converting string to SecretKey
I would recommend to read Java 256-bit AES Password-Based Encryption. But here a quick overview. You will have to create a SecretKeyFactory, a KeySpec and then you can generate your SecretKey based on the KeySpec. From there, you can specify that your key is an AES through SecretKeySpec. There are some magic numbers here that you will have to consider. One is the password iterations, and the other the key sizes. For the example that I created below, I am using a 128 key instead of a 256. Sorry, I am not an expert in security topics so I used some examples that I have.
public static void main(String[] args) {
try {
//message to be encrypted
final String message = "This is a test message";
final String password = "password";
// Here the magic numbers
final int pswdIterations = 65536;
final int keySize = 128;
final byte[] saltBytes = {0, 1, 2, 3, 4, 5, 6};
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, pswdIterations, keySize);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
// Encrypt
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] encrypted = cipher.doFinal(message.getBytes());
System.out.println("Original string: " + message);
System.out.println("Encrypted string: " + Arrays.toString(encrypted));
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, secret);
byte[] decrypt_original = cipher.doFinal(encrypted);
String decrypt_originalString = new String(decrypt_original);
System.out.println("Decrypt string: " + decrypt_originalString);
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) {
Logger.getLogger(Sandbox.class.getName()).log(Level.SEVERE, null, ex);
}
}
Hopefully, this will help you.
String to Secret key conversion/Vice Versa
Just follow below steps.
From key to string
`SecretKey secretKey = KeyGenerator.getInstance("ALGO_SECRET_KEY_GENERATOR").generateKey();
// Crate base64 string
String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());`
From string to key
`// decode base64 string
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
// rebuild key using SecretKeySpec
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "ALGO_SECRET_KEY_GENERATOR"); `
It is available from api version 8
`SecretKey secretKey = null;
try {
secretKey = KeyGenerator.getInstance("AES").generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte encoded[] = secretKey.getEncoded();
String str = android.util.Base64.encodeToString(encoded , 0);
byte decoded[] = android.util.Base64.decode(str , 0);
SecretKey originalKey = new SecretKeySpec(decoded, 0, decoded.length, "AES");'
Converting String to Secret Key
You've got a few issues here:
sb.getBytes()
does not do what you think it does. What you are expecting is a byte array containing{ 0xff, 0xfe, 0x7a, 0x50 }
. What you are getting is a byte array containing{ 0x46, 0x46, 0x46, 0x45, 0x37, 0x61, 0x35, 0x30 }
(assuming you're using UTF-8).Your choices here would be to either initialise the byte array manually, like so:
byte[] b = new byte[]{ (byte) 0xff, (byte) 0xfe, (byte) 0x7a, (byte) 0x50 };
Or to parse the string correctly. I'm not sure exactly how you'd go about that, but it should be doable (and there will likely be an open-source library to do it for you).
The output
javax.crypto.spec.SecretKeySpec@183a2
is not writing out the value of the key. All you're seeing is thetoString
output of theSecretKeySpec
, which follows the format<fully-qualified-class-name>@<hashcode>
(see here for details on the toString() method), so the numbers you're seeing,183a2
, are just the hashcode of theSecretKeySpec
object, which must not be the key - that would very insecure. To see the value of the key it's holding, you'd need to call theSecretKeySpec#getEncoded()
method.Note that just calling
System.out.println(byteArray)
will not display the content of the byte array - it will just display the class name and hashcode again. You'll need to either iterate through the array and print out the elements one-by-one, or use another tool to compare the two arrays.
Converted SecretKey into bytes, how to convert it back to a SecretKey?
This should work
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
byte[] data = key.getEncoded();
SecretKey key2 = new SecretKeySpec(data, 0, data.length, "DES");
How to properly recreate SecretKey from string
One of your problems is here:
String encryptedText = new String(encryptedBytes, "UTF8");
Generally, many byte sequences in cipher text are not valid UTF-8–encoded characters. When you try to create a String
, this malformed sequences will be replaced with the "replacement character", and then information from the the cipher text is irretrievably lost. When you convert the String
back to bytes and try to decrypt it, the corrupt cipher text raises an error.
If you need to represent the cipher text as a character string, use base-64 encoding, just as you do for the key.
The other principal problem is that you are aren't specifying the full transformation. You should specify the "mode" and "padding" of the cipher explicitly, like "DESede/ECB/PKCS5Padding".
The correct mode will depend on your assignment. ECB is generally not secure, but more secure modes add a bit of complexity that may be outside the scope of your assignment. Study your instructions and clarify the requirements with your teacher if necessary.
Related Topics
How to View Bytecode of Class File
Formatting a String to a Currency Format in Jasper Report
How to Convert a Date to Milliseconds
Fetch First Element of Stream Matching the Criteria
When Does the Main Thread Stop in Java
Spring Security 5:There Is No Passwordencoder Mapped for the Id "Null"
Does the Preparedstatement Avoid SQL Injection
How to Escape Reserved Words in Hibernate's Hql
Java/Jdk for the Apple M1 Chip
Intellij - Convert a Java Project/Module into a Maven Project/Module
Last Iteration of Enhanced for Loop in Java
Java Bigdecimal: Round to the Nearest Whole Value
Is Concurrenthashmap Totally Safe
Java Import VS Code Performance
Best Practice for Passing Many Arguments to Method
How to Get Which Jradiobutton Is Selected from a Buttongroup