I'm implementing 3DES EDE3 with 3 different keys each of 56 bits, EK3 DK2 Ek1 plain text, 168 bits. But i don't know how to do it. i'm bigger to java.
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESedeEncryption {
public static void main(String[] args) {
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
SecretKey k3 = generateDESkey();
String firstEncryption = desEncryption("plaintext", k1);
String decryption = desDecryption(firstEncryption, k2);
String secondEncryption = desEncryption(decryption, k3);
System.out.println("secondEncryption: "+secondEncryption);
System.out.println("firstEncryption: "+firstEncryption);
System.out.println("decryption: "+decryption);
}
public static SecretKey generateDESkey() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (Exception ex) {
ex.printStackTrace();
}
keyGen.init(168); // key length 112 for two keys, 168 for three keys
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
public static String desEncryption(String strToEncrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
String encryptedString = new BASE64Encoder().encode(cipher.doFinal(strToEncrypt.getBytes()));
return encryptedString;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String desDecryption(String strToDecrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
String decryptedString = new String(cipher.doFinal(new BASE64Decoder().decodeBuffer(strToDecrypt)));
return decryptedString;
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
I'm getting following error. tell me what wrong in this. plz help us.
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java
:294)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at DESedeEncryption.desDecryption(DESedeEncryption.java:55)
at DESedeEncryption.main(DESedeEncryption.java:17)
java.lang.NullPointerException
at DESedeEncryption.desEncryption(DESedeEncryption.java:41)
at DESedeEncryption.main(DESedeEncryption.java:18)
secondEncryption: null
firstEncryption: sP0ySxvxaUP+0a9l5qWSLQ==
decryption: null
Thanks in advance
Your code of your main() method doesn't make any sense. You're trying to decrypt with k2 a message which has been encrypted with k1. That can't work.
You don't seem to understand what DESede does. You don't need to encrypt then decrypt then encrypt with 3 different keys. You simply provide a key that has 3 times the length of a simple DES key, and the DESede cipher splits the key in 3 parts and does the three steps for you. So, as with any other Cipher, to check that it works fine, you should simply do:
SecretKey key = generateKey();
byte[] clearText = ...;
byte[] encrypted = encrypt(clearText, key);
byte[] decrypted = decrypt(encrypted, key);
// decrypted should contain the same bytes as clearText
You seem to misunderstand 3DES. The internal algorithm performs the encrypt/decrypt/encrypt operations already, so you don't need to perform that yourself. Just pass a key that's 168 bits long (3 × 56-bit) into your cipher and it'll do the job.
Additionally, the conversion to Base64 is failing. When you're running this:
String encryptedString = new BASE64Encoder().encode(cipher.doFinal(strToEncrypt.getBytes()));
You're actually calling toString() on the result of doFinal() before it's passed to encode(), causing it to return the Java reference rather than the actual contents of the array. Someone else made exactly the same mistake in an earlier question.
That said, you're using ECB for your block cipher mode. ECB (or Electronic Codebook) mode works by running each cryptographic transform on every block independently, resulting in the same ciphertext block being produced for identical ciphertext blocks when the same key is used. Wikipedia's entry on this has a great visual representation of how this kind of cryptographic process is broken.
I suggest using Cipher Block Chaining (CBC) mode instead, which combines each plaintext block with the previous ciphertext block. This ensures that equal blocks of plaintext don't produce the same blocks of ciphertext. The additional requirement here is an Initialisation Vector (IV), which is a unique random value used as the "previous block" for the first plaintext block (which obviously can't have a previous ciphertext block - it's the first one). The IV doesn't have to be secret, so you just generate it, use it, and prepend it to the encrypted message. On decryption you strip that value off, then use it to decrypt along with the key.
Related
Currently i've created a SecretKey for use in a RC4 encryption for my assignment. After the RC4 encryption i would then convert this key into string and send it to a server via UDP however when i rebuild it on the server side using SecretKeySpec, it would produce a completely different secret key.
I've looked around stackoverflow for solutions but the end would still result in a rebuilt SecretKey being different from my original SecretKey.
I've tried rebuilding the Secret Key from the String format on the client code and the result would still be a different SecretKey compared to the original so i doubt my UDP tranmission has anything to do with the result.
Below are how i went about creating the initial SecretKey for use in a RC4 encryption:
KeyGenerator keygen = KeyGenerator.getInstance("RC4");
SecretKey originalSecretKey = keygen.generateKey();
How i converted the SecretKey to String and rebuilt using SecretKeySpec:
String k = Base64.getEncoder().encodeToString(originalSecretKey.getEncoded());
byte[] decodedKey = Base64.getDecoder().decode(k);
SecretKey rebuiltSK = new SecretKeySpec(decodedKey, "RC4");
When I println "originalSecretKey" and "rebuiltSK" for checking, this is where I realised the rebuilt values are completely different and therefore i wouldnt be able to decrypt any originalSecretKey-encrypted message using the rebuiltSK.
Edit1: Silly me, thank you to "A Developer" and "Daniel" for pointing out that the actual .getEncoded() values of "originalSecretKey" and "rebuiltSK" are the same.
Apologies if I'm missing something extremely basic regarding Key generation and java's cryptography as this is my first time using them. Thank you in advance for your help !
Edit2:
Below is the code i'm currently using for my RC4 encryption and decryption:
public static byte[] encryptRC4(byte[] b, SecretKey k) throws Exception
{
Cipher cipher = Cipher.getInstance("RC4");
cipher.init(Cipher.ENCRYPT_MODE, k);
//Cipher.DECRYPT_MODE when on server program
byte[] encrypted = cipher.doFinal(b);
return encrypted;
}
The code above is the reason why I'm trying to rebuild the secretKey on the server end after receiving it in byte[] from the client.
I've tried running the decryption with the "rebuiltSK" as the SecretKey argument however it doesn't produce the correct plaintext although I've checked the packet.getData() on both client and server to be the same.
Your rebuilding of the SecretKey works like expected and the encryption followed by the decryption retrieves the
original plaintext.
I can only that argue (same as #Daniel) that the key was changed during transmission or the (byte[] with the)
ciphertext was not fully transmitted to the server.
The below full example code shows a complete round with key generation, encryption and decryption.
This is the result:
plaintext equals decryptedtext: true
decryptedtext: my secret
Security warning: The code below uses an UNSECURE algorithm 'RC4' or 'ARCFOUR'.
Please do not copy below code or use it in production - it is for educasional purposes only.
The code does not have any proper exception handling !
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("https://stackoverflow.com/questions/63185927/java-secretkey-to-string-and-rebuilding-back-to-secretkey-produces-different-de");
// security warning: the algorithm 'RC4' or 'ARCFOUR' is unsecure and
// should be used for educational purposes only
// do not use this code in production
// key generation
KeyGenerator keygen = KeyGenerator.getInstance("RC4");
SecretKey originalSecretKey = keygen.generateKey();
// encryption
byte[] plaintext = "my secret".getBytes(StandardCharsets.UTF_8);
byte[] ciphertext = encryptRC4(plaintext, originalSecretKey);
// decryption
String k = Base64.getEncoder().encodeToString(originalSecretKey.getEncoded());
byte[] decodedKey = Base64.getDecoder().decode(k);
SecretKey rebuiltSK = new SecretKeySpec(decodedKey, "RC4");
byte[] decryptedtext = decryptRC4(ciphertext, rebuiltSK);
// output
System.out.println("plaintext equals decryptedtext: " + Arrays.equals(plaintext, decryptedtext));
System.out.println("decryptedtext: " + new String(decryptedtext));
}
public static byte[] encryptRC4(byte[] b, SecretKey k) throws Exception
{
Cipher cipher = Cipher.getInstance("RC4");
cipher.init(Cipher.ENCRYPT_MODE, k);
byte[] encrypted = cipher.doFinal(b);
return encrypted;
}
public static byte[] decryptRC4(byte[] b, SecretKey k) throws Exception
{
Cipher cipher = Cipher.getInstance("RC4");
cipher.init(Cipher.DECRYPT_MODE, k);
byte[] decrypted = cipher.doFinal(b);
return decrypted;
}
}
I'm trying to make an encryption-decryption app. I've got two classes - one with functions to generate the key, encrypt and decrypt, second one for JavaFX GUI. In the GUI class I've got 4 textareas: 1st to write text to encrypt, 2nd for encrypted text, 3rd for the key (String encodedKey = Base64.getEncoder().encodeToString(klucz.getEncoded());) and 4th for decrypted text.
The problem is, I am not able to decrypt the text. I'm trying to recreate the SecretKey like this:
String encodedKey = textAreaKey.getText();
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
SecretKey klucz = new SecretKeySpec(decodedKey, "DESede");
When I encrypt the key looks like this: com.sun.crypto.provider.DESedeKey#4f964d80 and when I try to recreate it: javax.crypto.spec.SecretKeySpec#4f964d80 and I'm getting javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher
Here is my 1st class:
public class Encryption {
public static SecretKey generateKey() throws NoSuchAlgorithmException {
Security.addProvider(new com.sun.crypto.provider.SunJCE());
KeyGenerator keygen = KeyGenerator.getInstance("DESede");
keygen.init(168);
SecretKey klucz = keygen.generateKey();
return klucz;
}
static byte[] encrypt(byte[] plainTextByte, SecretKey klucz)
throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, klucz);
byte[] encryptedBytes = cipher.doFinal(plainTextByte);
return encryptedBytes;
}
static byte[] decrypt(byte[] encryptedBytes, SecretKey klucz)
throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, klucz);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return decryptedBytes;
}
}
edit
btnEncrypt.setOnAction((ActionEvent event) -> {
try {
String plainText = textAreaToEncrypt.getText();
SecretKey klucz = Encryption.generateKey();
byte[] plainTextByte = plainText.getBytes();
byte[] encryptedBytes = Encryption.encrypt(plainTextByte, klucz);
String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
textAreaEncryptedText.setText(encryptedText);
byte[] byteKey = klucz.getEncoded();
String stringKey = Base64.getEncoder().encodeToString(byteKey);
textAreaKey.setTextstringKey
} catch (Exception ex) {
ex.printStackTrace();
}
});
btnDecrypt.setOnAction((ActionEvent event) -> {
try {
String stringKey = textAreaKey.getText();
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
SecretKey klucz2 = new SecretKeySpec(decodedKey, "DESede");
String encryptedText = textAreaEncryptedText.getText();
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText.getBytes());
byte[] decryptedBytes = Encryption.decrypt(encryptedBytes, klucz2;
String decryptedText = Base64.getEncoder().encodeToString(decryptedBytes);
textAreaDecryptedText.setText(decryptedText);
} catch (Exception ex) {
ex.printStackTrace();
}
});
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.
There are two main issues:
You should not use user entered password as a key (there are difference between them). The key must have specific size depending on the cipher (16 or 24 bytes for 3des)
Direct 3DES (DESede) is a block cipher encrypting 8 bytes at once. To encrypt multiple blocks, there are some methods defined how to do that properly. It is calls Block cipher mode.
For proper encryption you need to take care of a few more things
Creating a key from the password
Let's assume you want to use DESede (3des). The key must have fixed size - 16 or 24 bytes. To properly generate a key from password you should use PBKDF. Some people are sensitive to "must use", however neglecting this step really compromises the encryption security mainly using user-entered passwords.
For 3DES you can use :
int keySize = 16*8;
int iterations = 800000;
char[] password = "password".toCharArray();
SecureRandom random = new SecureRandom();
byte[] salt = random.generateSeed(8);
SecretKeyFactory secKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(password, salt, iterations, keySize);
SecretKey pbeSecretKey = secKeyFactory.generateSecret(spec);
SecretKey desSecret = new SecretKeySpec(pbeSecretKey.getEncoded(), "DESede");
// iv needs to have block size
// we will use the salt for simplification
IvParameterSpec ivParam = new IvParameterSpec(salt);
Cipher cipher = Cipher.getInstance("DESEde/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desSecret, ivParam);
System.out.println("salt: "+Base64.getEncoder().encodeToString(salt));
System.out.println(cipher.getIV().length+" iv: "+Base64.getEncoder().encodeToString(cipher.getIV()));
byte[] ciphertext = cipher.doFinal("plaintext input".getBytes());
System.out.println("encrypted: "+Base64.getEncoder().encodeToString(ciphertext));
if you can ensure that your password has good entropy (is long and random enough) you may be good with a simple hash
MessageDigest dgst = MessageDigest.getInstance("sha-1");
byte[] hash = dgst.digest("some long, complex and random password".getBytes());
byte[] keyBytes = new byte[keySize/8];
System.arraycopy(hash, 0, keyBytes, 0, keySize/8);
SecretKey desSecret = new SecretKeySpec(keyBytes, "DESede");
The salt serves to randomize the output and should be used.
The output of the encryption should be salt | cipthertext | tag (not necessarily in this order, but you will need all of these for proper encryption).
To decrypt the output, you will need to split the output to salt, ciphertext and the tag.
I see zero vectors ( static salt or iv ) very often in examples from StackOverflow, but in many cases it may lead to broken ciphers revelaling key or plaintext.
The initialization vector iv is needed for block chain modes (encrypting longer input than a single block), we could use the salt from the key as well
when having the same size ( 8 bytes in our case). For really secure solution the password salt should be longer.
The tag is an authentication tag, to ensure that nobody has manipulated with the ciphertext. You could use HMAC of the plaintext or ciphertext. It is important you should use different key for HMAC than for encryption. However - I believe in your case your homework will be ok even without the hmac tag
I have done Encryption with ,
public static String encrypt(String plainText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedString = Base64.encodeToString(cipherText, Base64.NO_WRAP);
return encryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
it's working well.
but part of Decryption gives Error like,
W/System.err: javax.crypto.BadPaddingException: pad block corrupted
W/System.err: at com.android.org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:701)
W/System.err: at javax.crypto.Cipher.doFinal(Cipher.java:1111)
decrypt Code like,
public static String decrypt(String encryptedText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] cipherText = Base64.decode(encryptedText,Base64.NO_WRAP);
String decryptedString = new String(cipher.doFinal(cipherText),"UTF-8");
return decryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
here what is the problem? How can i solve this Issue?
It is likely that your secret_key value contains bytes which are not well represented in the ambiguous encoding you're using. When you call String#getBytes() without specifying an encoding, you get the system default, which can vary.
You should use hexadecimal encoding whenever you represent your key as a String. This will be consistent across serialization/deserialization on every platform. There are many standard implementations of this encoding/decoding process available (i.e. org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEFFEDCBA9876543210"); or org.apache.commons.codec.binary.Hex.decodeHex("0123456789ABCDEFFEDCBA9876543210".toCharArray()); which both return the raw byte[]).
Some side notes:
You are using ECB mode of operation, which is extremely susceptible to frequency analysis for cryptanalysis and is effectively deprecated aside from toy crypto demonstrations. I suggest you use CBC, CTR, or GCM.
You do not provide an initialization vector (IV), so the same message encrypted with the same key will always yield identical cipher text. Use a unique and non-predictable IV for every encryption operation by generating 16 bytes from SecureRandom and populating it into an IvParameterSpec. You can prepend the IV bytes to the cipher text and transport/store it in the clear.
Your cipher text is not authenticated, allowing for malicious users to both manipulate encrypted data and to attempt decryption via padding oracle/CCA attacks. Use an authenticated encryption with associated data (AEAD) mode like GCM, or use an HMAC/SHA-256 message authentication code (MAC) over the cipher text, and verify it using a constant-time equals method before attempting any decryption.
You do not need to provide the mode of operation or padding scheme when instantiating a key. SecretKey key = new SecretKeySpec(keyData, "AES"); is sufficient.
3des with 2 different keys in java getting null.
import java.security.spec.*;
import javax.crypto.*;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESedeEncryption {
public static void main(String[] args) {
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
String firstEncryption = desEncryption("plaintext", k1);
System.out.println("firstEncryption Value : "+firstEncryption);
String decryption = desDecryption(firstEncryption, k2);
System.out.println("decryption Value : "+decryption);
String secondEncryption = desEncryption(decryption, k1);
System.out.println("secondEncryption Value : "+secondEncryption);
}
public static SecretKey generateDESkey() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (Exception ex) {
}
keyGen.init(112); // key length 56
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
public static String desEncryption(String strToEncrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
BASE64Encoder base64encoder = new BASE64Encoder();
byte[] encryptedText = cipher.doFinal(strToEncrypt.getBytes());
String encryptedString =base64encoder.encode(encryptedText);
return encryptedString;
} catch (Exception ex) {
}
return null;
}
public static String desDecryption(String strToDecrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(strToDecrypt);
byte[] plainText = cipher.doFinal(encryptedText);
String decryptedString= bytes2String(plainText);
return decryptedString;
} catch (Exception ex) {
}
return null;
}
private static String bytes2String(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i <bytes.length; i++) {
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
}
while i'm running the above code i'm getting null values. plz help.
output:
firstEncryption Value : jAihaGgiOzBSFwBWo3gpbw==
decryption Value : null
secondEncryption Value : null
getting error:
firstEncryption Value : ygGPwCllarWvSH8td55j/w==
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:
294)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at DESedeEncryption.desDecryption(DESedeEncryption.java:145)
at DESedeEncryption.main(DESedeEncryption.java:107)
decryption Value : null
java.lang.NullPointerException
at DESedeEncryption.desEncryption(DESedeEncryption.java:130)
at DESedeEncryption.main(DESedeEncryption.java:109)
secondEncryption Value : null
Symmetric ciphers work by encrypting and decrypting with the same key, hence the name symmetric. (And for most modes also the same IV, but the IV doesn't need to be secret.) You're encrypting with one key and decrypting with an independent key which is different with overwhelming probability (i.e. it might the same once a zillion quillion eternities). That won't work.
Perhaps you are confused by the description of Triple-DES also known as 3DES DESede or TDEA. The original DES (or DEA) cipher uses a 56-bit key (in 8 bytes) which was secure in the 1960s but not now. Triple-DES was defined using DES as a building block but with a bundle of 3 keys (k1,k2,k3) which can also be treated as a combined 168-bit key (in 24 bytes); if k3=k1 the key is described as 112-bits although it is still stored as 24 bytes. Your call to KeyGenerator "DESede" .init(112) does exactly that; it generates a 24-byte bundle with k3=k1 and k2 different. For convenience in the past Triple-DES is defined to use single-DES to encrypt with k1, decrypt with k2, and encrypt with k3, and the reverse when decrypting, hence the name DES-EDE or DESede. See http://en.wikipedia.org/wiki/Triple_DES .
If you really want, you can implement Triple-DES yourself in Java using Cipher "DES" by doing E,D,E (or reverse D,E,D if used) and then wrapping the mode around that, see Java Triple DES encryption with 2 different keys . But it's much easier to just use Cipher "DESede", which it does the lot for you, treating DESede like any other symmetric cipher primitive, as answered in that question.
Also, mode ECB is dangerous. It is an exaggeration to say it is always insecure as some people do, but historically very many applications using it designed by non-experts are insecure. Unless you know much more than is evident in your question, or are following (or interfacing to) a design by someone who does, use a better established mode like CBC or CTR.
First of all I wanna say thank you...
I wrote a program which one is doing encryption and decryption with Enum.
Enum has AES,BlowFish,DESede. My program will support these 3 encryption algorithm.
Then I wanted to Generate a SecretKey with SecretKeyFactory.But I think,I made a mistake to generate a key. (Obviously I loose myself in code.I have no idea about what can I do...)
My Code is below. This program's purpose is;
Users will write encryption and decryption method parameters. (Text,Encryption Algorithm)
Algorithm type will choose in Enum type. (Enum has 3 algorithm format)
According to the entered Encryption Type,program will encrypt entered text.
I know my code is really terrible. It has lots of unnecessary declaration and logical mistakes.
Code is working fine sometimes,sometimes will crash.
EDIT = Question is my code doesnt work always. Sometimes gives error. Error is = javax.crypto.BadPaddingException: Given final block not properly padded
Thank you for answering.
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class SymetricAlgorithms {
private static enum algorithms { //Enum declaration 3 encryption types here
AES, BlowFish, DESede;
}
private static String data = "HOWCANISOLVETHIS"; //this will be encrypt and decrypt
public static void main(String[] args) throws Throwable {
SecretKey kgen = GenerateKey(); // Create a key.
String encrypText = encrypt(kgen, data, algorithms.AES); //encrypt method calling here.
String decrypText = dencypt(kgen, encrypText, algorithms.AES);//decrypt method calling here.
System.out.println("plaintext = " + data + " key = " + kgen
+ "\nEncryptedText = " + encrypText
+ "\nDecryptedText = " + decrypText);
}
public static String dencypt(SecretKey inKey, String text, algorithms eValue)throws Throwable {//decryption
try {
byte[] text2 = text.getBytes(); //convert from parameters TEXT to Bytes
Cipher cipher = Cipher.getInstance("AES"); //Cipher initialize and choose encryption method (AES)
cipher.init(Cipher.DECRYPT_MODE, inKey); //cipher process
byte plainTextByte[] = new byte[20]; //Creating byte array
plainTextByte =cipher.doFinal(text2);//using byte array to assign ciphers result
System.out.println(plainTextByte);
return new String(plainTextByte);
} catch (Exception e) {
System.err.println("Data Cant Decrypted !");
e.printStackTrace();
}
return null;
}
public static String encrypt(SecretKey inKey, String text, algorithms eValue)
throws Throwable {
try {
Cipher cipher = null; //cipher declaration
switch (eValue) {//Enum. 3 types here and control structure for Users choosing encryption type is acceptable
case AES:cipher = Cipher.getInstance("AES");
break;
case BlowFish:Cipher cipher2 = Cipher.getInstance("BlowFish");
cipher = cipher2;
break;
case DESede:Cipher cipher3 = Cipher.getInstance("DESede");
cipher=cipher3;
break;
default:
System.out.println("Unexpectable value input.");
break;
}
System.out.println(inKey);
//Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, inKey);
byte[] ciphertext = cipher.doFinal(text.getBytes("UTF-8"));//cipher result is assign to byte array
System.out.println(ciphertext);
return new String(ciphertext);
} catch (Exception e) {
System.err.println("Unexpectable algorithm type !");
e.printStackTrace();
}
return null;
}
public static SecretKey GenerateKey() throws Throwable {//Generate a key for using crypt
//could sb explain these? =D I loose myself. I combined codes from finding internet...Failed...
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
byte bytes[] = new byte[20];
prng.nextBytes(bytes);
String passwordTemp = prng.toString();
String saltTemp = passwordTemp;
char[] password = passwordTemp.toCharArray();
byte[] salt = saltTemp.getBytes();
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
return secret;
} catch (Exception e) {
System.err.println("Key cant be generated !");
e.printStackTrace();
}
return null;
}
}
The theme of the problem is misunderstanding of the relationship between Strings and bytes. At the end of the encrypt method, what do you think these two lines do:
byte[] ciphertext = cipher.doFinal(...
return new String(ciphertext);
The last line takes the encrypted bytes, which could be almost anything, and attempts to interpret those bytes as encoding some characters of a string. Using what encoding? String constructor with no character encoding argument uses system default encoding, which depends on JVM/OS/Locale. Lets say it is UTF-8. Are you guaranteed that there will actually be some character for the encrypted bytes? Answer: NO. Will you get the same bytes back, when you take the resulting string and call .getBytes("UTF-8"). Answer: No, there are mutliple byte sequences encoding the same characters, thus new String(bytes, "UTF-8").getBytes("UTF-8") is not guaranteed to return the bytes you started with.
In summary, don't attempt to interpret arbitrary bytes as a string. Make your encrypt method return byte[], and your decryp method take an array of bytes to decode-- then it will work.
It is not necessary to make your program work, but if you must represent the encrypted bytes as a string, consider base64 encoding, or hexadecimal encoding -- these encodings uniquely map every possible byte (or sequence of bytes) to a string.
UPDATE: here is a more concise generateKey() method. It allows you to pass the password in as an argument.
public static SecretKey generateKey(String password) {
try {
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
byte saltBytes[] = new byte[20];
secureRandom.nextBytes(saltBytes);
KeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey secretKey = factory.generateSecret(spec);
return new SecretKeySpec(secretKey.getEncoded(), "AES");
} catch (Exception e) {
throw new IllegalStateException("Key cant be generated !");
}
}