So I think I have encrypted my secret key and String well but decryption is becoming the problem for me. Below is my code:
package ReadFileExample;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.KeyStore;
public class generatekey {
static Cipher cipher;
public static void main(String[] args) throws Exception {
// generating a symmetric key using the AES algorithm
KeyGenerator generator = KeyGenerator.getInstance("AES");
// 128 bit key
generator.init(256);
//generates a secret key
SecretKey secretkey = generator.generateKey();
// returns an AES cipher
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//print key
System.out.println("Key: " + cipher);
String plainText = "Hello World";
// call to method encrypt
String hexEncryptedByteText = encrypt(plainText, secretkey);
// print orignial text and encrypted text
System.out.println("Plain Text: " + plainText);
System.out.println("Encrypted Text: " + hexEncryptedByteText);
int plainTextlength = plainText.length();
System.out.println("length of text: " + plainTextlength);
// allows to write data to a file
FileOutputStream fos = null;
// write bytes to file
BufferedOutputStream bos = null;
// create file to which data needs to be written
String fileName = "C:/Users/******/newFile.txt";
try{
// allows written data to go into the written path
fos = new FileOutputStream(fileName);
// converts written data into bytes
bos = new BufferedOutputStream(fos);
// writes the encrypted text into file
bos.write(hexEncryptedByteText.length());
System.out.println("encryptedText has been written successfully in "
+fileName);
// allows to catch bug in code
} catch (IOException e) {
e.printStackTrace();
} finally {
try{
// check for null exception
if (bos != null){
bos.close();
}
// check for null exception
if (fos != null){
fos.close();
}
} catch (IOException e){
e.printStackTrace();
}
}
// creates a file input stream by opening a path to the file needed
FileInputStream fin = new FileInputStream("C:/Users/*****/public.cert");
// implements the X509 certificate type
CertificateFactory f = CertificateFactory.getInstance("X.509");
// initalizes data found in the file
X509Certificate certificate = (X509Certificate)f.generateCertificate(fin);
// gets public key from this certificate
PublicKey pk = certificate.getPublicKey();
System.out.println(pk);
String hexEncryptedByteKey = encryptedKey(pk, secretkey);
System.out.println("Encrypted Key: " + hexEncryptedByteKey);
System.out.println("Encrypted Key length: " + hexEncryptedByteKey.length());
// allows to write data to a file
FileOutputStream newFos = null;
// write bytes to file
BufferedOutputStream newBos = null;
// create file to which data needs to be written
String fileNameKey = "C:/Users/****/symmetric.txt";
try{
// allows written data to go into the written path
newFos = new FileOutputStream(fileNameKey);
// converts written data into bytes
newBos = new BufferedOutputStream(newFos);
// writes the encrypted text into file
newBos.write(hexEncryptedByteKey.length());
System.out.println("encryptedKey has been written successfully in "
+fileNameKey);
// allows to catch bug in code
} catch (IOException e) {
e.printStackTrace();
} finally {
try{
// check for null exception
if (newBos != null){
newBos.close();
}
// check for null exception
if (newFos != null){
newFos.close();
}
} catch (IOException e){
e.printStackTrace();
}
}
// load keystore to get private key
KeyStore ks = KeyStore.getInstance("JKS");
String password = "*****";
char[] passwordChar = password.toCharArray();
System.out.println("password: " + passwordChar);
// locate file
try (FileInputStream fis = new FileInputStream("C:/Users/*****/keystore.jks")) {
ks.load(fis, passwordChar);
}
// protect password for keystore
KeyStore.ProtectionParameter protParam = new KeyStore.PasswordProtection(passwordChar);
// get private key from keystore
KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry)
ks.getEntry("*****", protParam);
PrivateKey myPrivateKey = pkEntry.getPrivateKey();
System.out.println("private key: " + myPrivateKey);
//method declaration
String decryptedKey = decryptedKey(myPrivateKey, hexEncryptedByteKey);
System.out.println("decrypted Key: " + decryptedKey);
String hexDecryptedByteText = decryptedTextHex(decryptedKey, hexEncryptedByteText);
System.out.println("key: " + hexDecryptedByteText);
}
public static String encrypt(String plainText, SecretKey secretkey) throws Exception {
//Encodes the string into a sequence of bytes
byte[] plainTextByte = plainText.getBytes();
//intialize cipher to encryption mode
cipher.init(Cipher.ENCRYPT_MODE, secretkey);
//data is encrypted
byte[] encryptedByte = cipher.doFinal(plainTextByte);
//Base64.Encoder encoder = Base64.getEncoder();
//encodes bytes into a string using Base64
byte[] encryptedByteText = Base64.getEncoder().encode(plainTextByte);
String hexEncryptedByteText = DatatypeConverter.printHexBinary(plainTextByte);
// return the string encrypted text to the main method
return hexEncryptedByteText;
}
public static String encryptedKey(PublicKey pk, SecretKey secretkey) throws Exception {
// data written to byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// writes data types to the output stream
ObjectOutputStream writter = new ObjectOutputStream(baos);
//specific object of secretkey is written to the output stream
writter.writeObject(secretkey);
//creates a byte array
byte[] plainTextByteKey = baos.toByteArray();
//creates a cipher using the RSA algorithm
Cipher cipher = Cipher.getInstance("RSA");
// initalizes cipher for encryption using the public key
cipher.init(Cipher.ENCRYPT_MODE, pk);
//encrypts data
//byte[] encryptedByteKey = Base64.getEncoder().encode(plainTextByteKey);
String hexEncryptedByteKey = DatatypeConverter.printHexBinary(plainTextByteKey);
//Base64.Encoder encoderKey = Base64.getEncoder();
// encodes the byte array into a string.
//String encryptedTextKey = new String(encryptedByteKey);
return hexEncryptedByteKey;
}
private static String decryptedKey(PrivateKey myPrivateKey, String hexEncryptedByteKey) throws Exception {
//ByteArrayOutputStream baosDecrypt = new ByteArrayOutputStream();
//ObjectOutputStream writterDecrypt = new ObjectOutputStream(baosDecrypt);
//writterDecrypt.writeObject(hexEncryptedByteKey);
//byte[] byteKeyDecrypt = baosDecrypt.toByteArray();
Cipher cipher;
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, myPrivateKey);
//cipher.doFinal();
//byte [] decryptedKey = Base64.getDecoder().decode(byteKeyDecrypt);
//String decryptedTextKey = new String(byteKeyDecrypt);
byte[] decodedHex = DatatypeConverter.parseHexBinary(hexEncryptedByteKey);
System.out.println("decoded hex key: " + decodedHex);
String decryptedKey = new String(decodedHex, "UTF-8");
return decryptedKey;
}
private static String decryptedTextHex(String decryptedKey, String hexEncryptedByteText) throws Exception {
byte[] decryptedTextByte = decryptedKey.getBytes();
byte[] textString = hexEncryptedByteText.getBytes();
SecretKey key = new SecretKeySpec(decryptedTextByte, 0, decryptedTextByte.length, "AES");
Cipher cipher;
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//IvParameterSpec iv = new IvParameterSpec(cipher.getIV());
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decodedTextHex = cipher.doFinal(textString);
byte[] decoded = Base64.getDecoder().decode(decodedTextHex);
String hexDecryptedByteText = DatatypeConverter.printHexBinary(decoded);
return hexDecryptedByteText;
}
}
This is the error I am getting:
Exception in thread "main" java.security.InvalidKeyException: Parameters missing
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:469)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:313)
at javax.crypto.Cipher.implInit(Cipher.java:802)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1249)
at javax.crypto.Cipher.init(Cipher.java:1186)
at ReadFileExample.generatekey.decryptedTextHex(generatekey.java:289)
at ReadFileExample.generatekey.main(generatekey.java:202)
I am not sure what is wrong. If my decryption of key is wrong or just the decryption of the String. I get no errors when it comes to the decryption of the key however.
For some more information: I generated a secret key, encrypted a String with the secret key and then encrypted the secret key with a generated public key. Then I decrypted the secret key with the private key and lastly I need to decrypt the String with the decrypted secret key.
Help is greatly appreciated. I have been working on this for so long and I just don't know what to do anymore. ]
EDIT: That other question has nothing to do with my question. I don't even have that same error message and I have already downloaded the JCE as that solution has stated.
You have to be carefull with the key size, AES is a 128-bit block cipher supporting keys of 128, 192, and 256 bits so if your key size is any different you will get exceptions, also if you keysize is greater than 128 then the code wont work unless you have Unlimited policy files. Basically there is quite a bit detail to this. If you want to see some working code check out this link: https://github.com/Jsondb/jsondb-core/blob/master/src/main/java/io/jsondb/crypto/DefaultAESCBCCipher.java
Related
My aim is to take in a message containing a challenge and an origin. On receiving this msg a rsa keypair must be generated which is then used to manipulate the data as shown below. Certain part of the data is encrypted using the generated public key. During authorization, that data must be decrypted with the private key. However, when i try to decrypt it is shows a decryption error. I have even printed different parts of the code just to check if the desired output is achieved which is why i know the private key taken from file is correct. I am unable to solve the decryption error. The specifications for the task require the use of rsa and not hybrid encryption. i have tried padding but that didnt help. please advice on how to solve this problem
package pam;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.xml.bind.DatatypeConverter;
import com.sun.jersey.core.util.Base64;
class Test
{
public static void kpgen(int numBits, String s) throws Exception
{
if(s.length()!=64)
{
System.out.println("invalid entry");
}
else
{
KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = keygen.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
System.out.println("pk: "+privateKey);
System.out.println("pubk: "+publicKey);
String fileBase = "f:\\tempKey"; //WRITING PVT KEY TO FILE
try (FileOutputStream out = new FileOutputStream(fileBase + ".key"))
{
out.write(keyPair.getPrivate().getEncoded());
}
try (FileOutputStream out = new FileOutputStream(fileBase + ".pub"))
{
out.write(keyPair.getPublic().getEncoded());
}
System.out.println("Key pair : " + Base64.encode(String.valueOf(keyPair)));
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(keyPair.toString().getBytes(StandardCharsets.UTF_8));
String sha256 = DatatypeConverter.printHexBinary(digest).toLowerCase();
System.out.println("Hash value: "+sha256);
String ch = s.substring(0,32);
String or = s.substring(32,64);
System.out.println("Challenge: "+ch);
System.out.println("Origin: "+or);
MessageDigest md1 = MessageDigest.getInstance("SHA-256");
byte[] digest1 = md1.digest(privateKey.toString().getBytes(StandardCharsets.UTF_8));
String sha = DatatypeConverter.printHexBinary(digest1).toLowerCase();
or = or + sha;
System.out.println("String kh: "+or);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] keyHandle = cipher.doFinal(or.getBytes());
System.out.println("Key Handle: "+keyHandle);
String f = "f:\\keyList.pub";
Key pub = getKeyFromFile(f);
System.out.println("Attestation Public Key: "+pub);
PrivateKey pk = (PrivateKey) getPvtKey("f:\\keyList.key");
Signature rsa = Signature.getInstance("SHA1withRSA");
rsa.initSign(pk);
rsa.update(ch.getBytes());
byte[] sc = rsa.sign();
System.out.println("Signed challenge: "+sc);
String rm = publicKey.toString() + pub + sc + keyHandle;
System.out.println("Response Msg: " +rm);
}
}
public static Key getKeyFromFile(String fileName) throws Exception
{
byte[] bytes = Files.readAllBytes(Paths.get(fileName));
X509EncodedKeySpec ks = new X509EncodedKeySpec(bytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pub = kf.generatePublic(ks);
return pub;
}
public static PrivateKey getPvtKey(String s) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException
{
byte[] bytes = Files.readAllBytes(Paths.get(s));
PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(bytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey pvt = kf.generatePrivate(ks);
return pvt;
}
public static void auth(String s) throws NoSuchAlgorithmException, Exception, IOException
{
String chal = s.substring(0, 32);
String origin = s.substring(32,64);
String kh = s.substring(64);
byte[] kh1 = kh.getBytes();
PrivateKey pvtKey = getPvtKey("f:\\tempKey.key"); //READING THE PRIVATE KEY MADE IN KPGEN
System.out.println("pk: "+pvtKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pvtKey);
byte[] keyHandle = cipher.doFinal(kh1);
String or = keyHandle.toString().substring(0, (kh.length()/2));
String pk = keyHandle.toString().substring(kh.length()/2);
int c = 0;
if(or.equals(origin))
{
c++;
}
else
{
System.out.println("Bad Key Handle: Invalid Origin");
}
if(pk.equals(pvtKey.toString()))
{
c++;
}
else
{
System.out.println("Bad Key Handle: invalid private key");
}
if(c==2)
{
Signature rsa = Signature.getInstance("SHA1withRSA");
rsa.initSign((PrivateKey) pvtKey);
rsa.update(chal.getBytes());
byte[] sc = rsa.sign();
System.out.println("Signed Challenge: "+sc);
}
else
System.out.println("error");
}
}
You have multiple (many) issues in your code with the encryption
First - encode properly your data, String in Java is to represent printable characters. As soon as you work with encryption (working on byte[] level), you need to encode or decode the values.
Example - your code will print the "keyHandle", it's a byte array object hash, not really the encrypted data itself
byte[] keyHandle = cipher.doFinal(or.getBytes());
System.out.println("Key Handle: "+keyHandle);
...
String rm = publicKey.toString() + pub + sc + keyHandle;
Use at hex or base64 encoding to print out the output. The same applies to the signature.
I am unable to solve the decryption error.
String kh = s.substring(64);
byte[] kh1 = kh.getBytes();
..
byte[] keyHandle = cipher.doFinal(kh1);
And you simply assume you can decrypt some random substring? Encrypting using RSA will produce output of size of the key (e.g. 2048 bits) and you have to store and decrypt as whole, not any substring.
As a learning exercise - try to simply encrypt and decrypt, encode, decode to learn the primitives you can (re)use.
I'm completely new with encryption stuff and i have some questions about encryption in java
I use to do this for RSA encryption in java
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPTION_MODE,publicKey);
byte result = cipher.doFinal(data);
and same way to use AES and I use this code to generate a AES key
SecureRandom random = new SecureRandom();
byte [] key = new byte [16];
random.nextByte(key);
SecretKeySpec secretKey = new SecretKeySpec(key,"AES");
but as i saw in other programs code this is not how they use encryption i always see they use something as IV param in AES and they never use "AES" or "RSA" to get a cipher instance.
Is the way i using to encrypt data safe?
I'm sure that i missing something
UPDATE:
I also have a question about changing data size in AES encryption in the way that i use to encrypt data with AES it changes the data size from 1024 to 1040
byte key [] = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(key);
SecretKeySpec keySpec = new SecretKeySpec(key,"AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE,keySpec);
FileInputStream in = new FileInputStream("test.txt");
byte [] buffer = new byte[1024];
byte [] encrypted;
while (in.read()>0){
encrypted = c.doFinal(buffer);
System.out.println(encrypted.length);
}
the out put is:
1040
1040
.
.
1040
Simply encrypted data size is always 16 bytes more than original data
Do i have to deal with this or it's because i using Cipher.getInstance("AES");
This is not the recommended way and you will need to change it. You may want to have a better look on StackOverflow. Your question is (in)directly answered in this post How to encrypt String in Java.
Make sure you look further down to all the answers. For example this one will probably help you to understand more.
The most simple is to use the below class
package com.toptal.gif_downloader.tools;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class CipherDecrypt {
private static SecretKeySpec secretKey;
private static byte[] key;
public static void setKey(final String myKey) {
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String Encrypt(final String strToEncrypt, final String secret) {
try {
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder()
.encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
} catch (Exception e) {
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
public static String Decrypt(final String strToDecrypt, final String secret) {
try {
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder()
.decode(strToDecrypt)));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
}
I am trying to encrypt and decrypt the image file with my Helper class
RSAHelper.java
package com.lib;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
/**
* Created by shobhan.
*/
public class RSAHelper {
private final static String RSA = "RSA";
private PublicKey publicKey;
private PrivateKey privateKey;
public RSAHelper() throws Exception {
/**
* generate RSA keys
*/
generateKey();
}
private void generateKey() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
publicKey = keyPair.getPublic();
privateKey = keyPair.getPrivate();
}
/**
* Used to do encryptFile the file
*
* #param srcPath File path to be encrypted
* #param destPath Encrypts the file in srcPath and creates a file in destPath
* #return true if encryption success, false otherwise
*/
public boolean encryptFile(String srcPath, String destPath) {
try {
FileInputStream fileInputStream = new FileInputStream(srcPath);
FileOutputStream fileOutputStream = new FileOutputStream(destPath);
// byte[] key = "12345678".getBytes();
// SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.PUBLIC_KEY, publicKey);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// CipherOutputStream cipherOutputStream = new
// CipherOutputStream(fileOutputStream, cipher);
byte[] buf = new byte[117];
byte[] encryptedData = null;
int read;
while ((read = fileInputStream.read(buf)) > 0) {
encryptedData = cipher.doFinal(buf);
fileOutputStream.write(encryptedData);
// cipherOutputStream.write(buf);
}
fileInputStream.close();
fileOutputStream.flush();
// cipherOutputStream.close();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* Used to do decryptFile the file
*
* #param srcPath File path to be decrypted
* #param destPath Decrypts the file in srcPath and creates a file in destPath
* #return true if encryption success, false otherwise
*/
public boolean decryptFile(String srcPath, String destPath) {
try {
FileInputStream fileInputStream = new FileInputStream(srcPath);
FileOutputStream fileOutputStream = new FileOutputStream(destPath);
// byte[] key = "12345678".getBytes();
// SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.PRIVATE_KEY, privateKey);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// cipher.init(Cipher.DECRYPT_MODE, keySpec);
// CipherOutputStream cipherOutputStream = new
// CipherOutputStream(fileOutputStream, cipher);
byte[] buf = new byte[128];
byte[] decryptedData = null;
int read;
while ((read = fileInputStream.read(buf)) > 0) {
decryptedData = cipher.doFinal(buf);
fileOutputStream.write(decryptedData);
// cipherOutputStream.write(buf);
}
fileInputStream.close();
fileOutputStream.flush();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
in Activity I used like
RSAHelper rsaHelper = null;
try {
rsaHelper = new RSAHelper();
} catch (Exception e) {
e.printStackTrace();
}
String src = getExternalFilesDir("sdcard").getAbsolutePath() + "/mother.png";
String dest = src.replace("mother","motherEnc");
String decrypted = dest.replace("motherEnc","motherDec");
rsaHelper.encryptFile(src, dest);
rsaHelper.decryptFile(dest, decrypted);
but the decrypted file is not original(means corrupted).
Same code working when I execute in windows desktop.
Am I doing anything wrong?
Thanks in advance.
Continuing the list begun in the comment by #zaph:
You are initializing the cipher twice, luckily with identical modes so it's just confusing but not the source of your bug.
You are relying on defaults for the Cipher transformation, a frequent source of non-portability, bugs and/or confusion. Always explicitly specify the Cipher transformation fully, like Cipher.getInstance("RSA/ECB/PKCS1PADDING") or Cipher.getInstance("RSA/ECB/NOPADDING").
You are incorrectly assuming that InputStream.read() will always return a full buffer of bytes if they are available. That might be how current implementations of FileInputStream actually work, but that is just luck. That behavior can change without warning at any time. InputStream implementors only need to obey the InputStream API contract, and so that is all you should rely on. You do record the number of bytes actually read, but then you don't do anything with it.
You are flushing your OutputStream but not closing it. You should close it instead.
Your bug is caused by number 5. Since there is no guarantee that your input file is an exact multiple of 117 bytes, the last block will likely be less than 117 bytes. However, you then encrypt the full 117 byte block, of which the trailing bytes are simply what was leftover from the previous read. So your decrypted file will be a multiple of 117, and will match up to the original length, with the trailing bytes being identical to the trailing bytes of the previous block.
I am writing a Spring mvc app and it needs to send an email with a link and encrypted params. The user will click on the link and I need to decrypt the params in the new page. So I am writing a util class to encrypt and decrypt along with encoding and decoding the parameters.
When I run my stand alone java class(using for testing) - I get the following error when the decryption is called(encrypting, encoding, decoding works fine).
java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:430)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:217)
at javax.crypto.Cipher.implInit(Cipher.java:791)
at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
at javax.crypto.Cipher.init(Cipher.java:1348)
My class is below
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class TestEncryptionEncode2 {
private String keyString = "asdfgh";
public static void main(String[] args) throws Exception {
TestEncryptionEncode2 api = new TestEncryptionEncode2();
String input = "abcdwer.comq1234";
try {
String[] encrypted = api.encryptObject(input);
// url may differ.. based upon project initial context
System.out.println("http://localhost:8080/view?d="+encrypted[0]+"&v="+encrypted[1]);
Object obj = api.decryptObject(encrypted[0], encrypted[1]);
System.out.println("Object Decrypted: "+obj.toString());
}catch(Exception e) {
//logger.debug("Unable to encrypt view id: "+id, e);
e.printStackTrace();
}
System.out.println("DONEE ");
}
private String[] encryptObject(Object obj) throws Exception {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ObjectOutput out = new ObjectOutputStream(stream);
try {
// Serialize the object
out.writeObject(obj);
byte[] serialized = stream.toByteArray();
System.out.println("serialized "+serialized[0]);
// Setup the cipher and Init Vector
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize()];
System.out.println("cipher.getBlockSize() "+cipher.getBlockSize());
System.out.println("iv.length "+iv.length);
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// Hash the key with SHA-256 and trim the output to 128-bit for the key
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(keyString.getBytes());
byte[] key = new byte[16];
System.arraycopy(digest.digest(), 0, key, 0, key.length);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// encrypt
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
// Encrypt & Encode the input
byte[] encrypted = cipher.doFinal(serialized);
byte[] base64Encoded = Base64.encodeBase64(encrypted);
String base64String = new String(base64Encoded);
String urlEncodedData = URLEncoder.encode(base64String,"UTF-8");
// Encode the Init Vector
byte[] base64IV = Base64.encodeBase64(iv);
String base64IVString = new String(base64IV);
String urlEncodedIV = URLEncoder.encode(base64IVString, "UTF-8");
System.out.println("urlEncodedData.length "+urlEncodedData.length());
System.out.println("urlEncodedIV.length "+urlEncodedIV.length());
return new String[] {urlEncodedData, urlEncodedIV};
}finally {
stream.close();
out.close();
}
}
/**
* Decrypts the String and serializes the object
* #param base64Data
* #param base64IV
* #return
* #throws Exception
*/
public Object decryptObject(String base64Data, String base64IV) throws Exception {
System.out.println("decryptObject "+base64Data);
System.out.println("decryptObject "+base64IV);
// Decode the data
byte[] encryptedData = Base64.decodeBase64(base64Data.getBytes());
// Decode the Init Vector
byte[] rawIV = Base64.decodeBase64(base64IV.getBytes());
System.out.println("rawIV "+rawIV.length);
for (int i=0;i < rawIV.length;i++ ){
System.out.println("---------"+rawIV[i]);
}
// Configure the Cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(rawIV);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(keyString.getBytes());
byte[] key = new byte[16];
System.arraycopy(digest.digest(), 0, key, 0, key.length);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); //////////////////////////////This is the error line
// Decrypt the data..
byte[] decrypted = cipher.doFinal(encryptedData);
// Deserialize the object
ByteArrayInputStream stream = new ByteArrayInputStream(decrypted);
ObjectInput in = new ObjectInputStream(stream);
Object obj = null;
try {
obj = in.readObject();
System.out.println("objobj "+obj);
}
catch(Exception e) {
//logger.debug("Unable to encrypt view id: "+id, e);
e.printStackTrace();
}finally {
stream.close();
in.close();
}
return obj;
}
}
This is the error line - cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
Do I need to add anything else? Thanks.
EDIT: Thanks, thanks worked. For some reason, I thought I am already decoding the data.
EDIT2: If the text to decode has + in it, then the decoded text will have space instead of +. So I had to replace all the spaces with + back when decrypting. Hopefully this helps someone.
The decryption process should be just reverse of encryption process.
Here in encryption you are doing URL encoding before returning it to user. So you must do URL decoding first in your decryption process.
Here is complete decryption process working with URL decoding:
public Object decryptObject(String base64Data, String base64IV) throws Exception {
System.out.println("decryptObject " + base64Data);
System.out.println("decryptObject " + base64IV);
String urlDecodedData=URLDecoder.decode(base64Data,"UTF-8");
// Decode the data
byte[] encryptedData = Base64.decodeBase64(urlDecodedData.getBytes());
String urlDecodedIV=URLDecoder.decode(base64IV,"UTF-8");
// Decode the Init Vector
byte[] rawIV = Base64.decodeBase64(urlDecodedIV.getBytes());
System.out.println("rawIV " + rawIV.length);
// Configure the Cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(rawIV);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(keyString.getBytes());
byte[] key = new byte[16];
System.arraycopy(digest.digest(), 0, key, 0, key.length);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); //////////////////////////////This is the error line
// Decrypt the data..
byte[] decrypted = cipher.doFinal(encryptedData);
// Deserialize the object
ByteArrayInputStream stream = new ByteArrayInputStream(decrypted);
ObjectInput in = new ObjectInputStream(stream);
Object obj = null;
try {
obj = in.readObject();
System.out.println("objobj " + obj);
} catch (Exception e) {
//logger.debug("Unable to encrypt view id: "+id, e);
e.printStackTrace();
} finally {
stream.close();
in.close();
}
return obj;
}
I would like to store an encrypted password in a Java file.
I saw at a solution using javax.crypto, but the problem with that was that the key
was being generated on the fly and it was random.
This password will be then taken and decrypted in the Java program in runtime.
Given that I am going to store an already encrypted password in a file - I want to get the
right text when decrypting it.
Is there a way to tell the javax.crypto method:
key = KeyGenerator.getInstance(algorithm).generateKey()
Can this be replaced with my own key generated once based on some private key?
Can anyone point me to some resources on how to do this?
Symmetric Key Cryptography : Symmetric key uses the same key for encryption and decryption. The main challenge with this type of cryptography is the exchange of the secret key between the two parties sender and receiver.
Example : The following example uses symmetric key for encryption and decryption algorithm available as part of the Sun's JCE(Java Cryptography Extension). Sun JCE is has two layers, the crypto API layer and the provider layer.
DES (Data Encryption Standard) was a popular symmetric key algorithm. Presently DES is outdated and considered insecure. Triple DES and a stronger variant of DES. It is a symmetric-key block cipher. There are other algorithms like Blowfish, Twofish and AES(Advanced Encryption Standard). AES is the latest encryption standard over the DES.
Steps :
Add the Security Provider : We are using the SunJCE Provider that is available with the JDK.
Generate Secret Key : Use KeyGenerator and an algorithm to generate a secret key. We are using DESede (DESede is descriptive name for 3DES implementation: DESede = DES-Encrypt-Decrypt-Encrypt = Triple DES).
Encode Text : For consistency across platform encode the plain text as byte using UTF-8 encoding.
Encrypt Text : Instantiate Cipher with ENCRYPT_MODE, use the secret key and encrypt the bytes.
Decrypt Text : Instantiate Cipher with DECRYPT_MODE, use the same secret key and decrypt the bytes.
All the above given steps and concept are same, we just replace algorithms.
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class EncryptionDecryptionAES {
static Cipher cipher;
public static void main(String[] args) throws Exception {
/*
create key
If we need to generate a new key use a KeyGenerator
If we have existing plaintext key use a SecretKeyFactory
*/
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // block size is 128bits
SecretKey secretKey = keyGenerator.generateKey();
/*
Cipher Info
Algorithm : for the encryption of electronic data
mode of operation : to avoid repeated blocks encrypt to the same values.
padding: ensuring messages are the proper length necessary for certain ciphers
mode/padding are not used with stream cyphers.
*/
cipher = Cipher.getInstance("AES"); //SunJCE provider AES algorithm, mode(optional) and padding schema(optional)
String plainText = "AES Symmetric Encryption Decryption";
System.out.println("Plain Text Before Encryption: " + plainText);
String encryptedText = encrypt(plainText, secretKey);
System.out.println("Encrypted Text After Encryption: " + encryptedText);
String decryptedText = decrypt(encryptedText, secretKey);
System.out.println("Decrypted Text After Decryption: " + decryptedText);
}
public static String encrypt(String plainText, SecretKey secretKey)
throws Exception {
byte[] plainTextByte = plainText.getBytes();
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedByte = cipher.doFinal(plainTextByte);
Base64.Encoder encoder = Base64.getEncoder();
String encryptedText = encoder.encodeToString(encryptedByte);
return encryptedText;
}
public static String decrypt(String encryptedText, SecretKey secretKey)
throws Exception {
Base64.Decoder decoder = Base64.getDecoder();
byte[] encryptedTextByte = decoder.decode(encryptedText);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedByte = cipher.doFinal(encryptedTextByte);
String decryptedText = new String(decryptedByte);
return decryptedText;
}
}
Output:
Plain Text Before Encryption: AES Symmetric Encryption Decryption
Encrypted Text After Encryption: sY6vkQrWRg0fvRzbqSAYxepeBIXg4AySj7Xh3x4vDv8TBTkNiTfca7wW/dxiMMJl
Decrypted Text After Decryption: AES Symmetric Encryption Decryption
Source
Example: Cipher with two modes, they are encrypt and decrypt. we have to start every time after setting mode to encrypt or decrypt a text.
Here is a solution using the javax.crypto library and the apache commons codec library for encoding and decoding in Base64 that I was looking for:
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.apache.commons.codec.binary.Base64;
public class TrippleDes {
private static final String UNICODE_FORMAT = "UTF8";
public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
byte[] arrayBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public TrippleDes() throws Exception {
myEncryptionKey = "ThisIsSpartaThisIsSparta";
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = skf.generateSecret(ks);
}
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
public static void main(String args []) throws Exception
{
TrippleDes td= new TrippleDes();
String target="imparator";
String encrypted=td.encrypt(target);
String decrypted=td.decrypt(encrypted);
System.out.println("String To Encrypt: "+ target);
System.out.println("Encrypted String:" + encrypted);
System.out.println("Decrypted String:" + decrypted);
}
}
Running the above program results with the following output:
String To Encrypt: imparator
Encrypted String:FdBNaYWfjpWN9eYghMpbRA==
Decrypted String:imparator
KeyGenerator is used to generate keys
You may want to check KeySpec, SecretKey and SecretKeyFactory classes
http://docs.oracle.com/javase/1.5.0/docs/api/javax/crypto/spec/package-summary.html
You may want to use the jasypt library (Java Simplified Encryption), which is quite easy to use. ( Also, it's recommended to check against the encrypted password rather than decrypting the encrypted password )
To use jasypt, if you're using maven, you can include jasypt into your pom.xml file as follows:
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>1.9.3</version>
<scope>compile</scope>
</dependency>
And then to encrypt the password, you can use StrongPasswordEncryptor
public static String encryptPassword(String inputPassword) {
StrongPasswordEncryptor encryptor = new StrongPasswordEncryptor();
return encryptor.encryptPassword(inputPassword);
}
Note: the encrypted password is different every time you call encryptPassword but the checkPassword method can still check that the unencrypted password still matches each of the encrypted passwords.
And to check the unencrypted password against the encrypted password, you can use the checkPassword method:
public static boolean checkPassword(String inputPassword, String encryptedStoredPassword) {
StrongPasswordEncryptor encryptor = new StrongPasswordEncryptor();
return encryptor.checkPassword(inputPassword, encryptedStoredPassword);
}
The page below provides detailed information on the complexities involved in creating safe encrypted passwords.
http://www.jasypt.org/howtoencryptuserpasswords.html
Simple and easy solution :- jasypt library.
To use this library in your maven project include below dependency
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>1.9.3</version>
<scope>compile</scope>
</dependency>
There is one security advantage of using this library is it does not provide any direct method to decrypt the password.
User can not pick any ecrypted password and pass it to library to get original password. To validate any user jasypt library provide a method which accept 2 parameters i.e. input password and encrypted password, the method will return true if the password is correct and false if password is wrong.
Just create a Utility Class like this.
public class EncryptDecryptPassword {
public static String encryptPassword(String inputPassword) {
StrongPasswordEncryptor encryptor = new StrongPasswordEncryptor();
return encryptor.encryptPassword(inputPassword);
}
public static boolean checkPassword(String inputPassword, String encryptedStoredPassword) {
StrongPasswordEncryptor encryptor = new StrongPasswordEncryptor();
return encryptor.checkPassword(inputPassword, encryptedStoredPassword);
}
}
Use these static methods from anywhere from your project to perform encryption.
Here is a sample I made a couple of months ago
The class encrypt and decrypt data
import java.security.*;
import java.security.spec.*;
import java.io.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class TestEncryptDecrypt {
private final String ALGO = "DES";
private final String MODE = "ECB";
private final String PADDING = "PKCS5Padding";
private static int mode = 0;
public static void main(String args[]) {
TestEncryptDecrypt me = new TestEncryptDecrypt();
if(args.length == 0) mode = 2;
else mode = Integer.parseInt(args[0]);
switch (mode) {
case 0:
me.encrypt();
break;
case 1:
me.decrypt();
break;
default:
me.encrypt();
me.decrypt();
}
}
public void encrypt() {
try {
System.out.println("Start encryption ...");
/* Get Input Data */
String input = getInputData();
System.out.println("Input data : "+input);
/* Create Secret Key */
KeyGenerator keyGen = KeyGenerator.getInstance(ALGO);
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.init(56,random);
Key sharedKey = keyGen.generateKey();
/* Create the Cipher and init it with the secret key */
Cipher c = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING);
//System.out.println("\n" + c.getProvider().getInfo());
c.init(Cipher.ENCRYPT_MODE,sharedKey);
byte[] ciphertext = c.doFinal(input.getBytes());
System.out.println("Input Encrypted : "+new String(ciphertext,"UTF8"));
/* Save key to a file */
save(sharedKey.getEncoded(),"shared.key");
/* Save encrypted data to a file */
save(ciphertext,"encrypted.txt");
} catch (Exception e) {
e.printStackTrace();
}
}
public void decrypt() {
try {
System.out.println("Start decryption ...");
/* Get encoded shared key from file*/
byte[] encoded = load("shared.key");
SecretKeyFactory kf = SecretKeyFactory.getInstance(ALGO);
KeySpec ks = new DESKeySpec(encoded);
SecretKey ky = kf.generateSecret(ks);
/* Get encoded data */
byte[] ciphertext = load("encrypted.txt");
System.out.println("Encoded data = " + new String(ciphertext,"UTF8"));
/* Create a Cipher object and initialize it with the secret key */
Cipher c = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING);
c.init(Cipher.DECRYPT_MODE,ky);
/* Update and decrypt */
byte[] plainText = c.doFinal(ciphertext);
System.out.println("Plain Text : "+new String(plainText,"UTF8"));
} catch (Exception e) {
e.printStackTrace();
}
}
private String getInputData() {
String id = "owner.id=...";
String name = "owner.name=...";
String contact = "owner.contact=...";
String tel = "owner.tel=...";
final String rc = System.getProperty("line.separator");
StringBuffer buf = new StringBuffer();
buf.append(id);
buf.append(rc);
buf.append(name);
buf.append(rc);
buf.append(contact);
buf.append(rc);
buf.append(tel);
return buf.toString();
}
private void save(byte[] buf, String file) throws IOException {
FileOutputStream fos = new FileOutputStream(file);
fos.write(buf);
fos.close();
}
private byte[] load(String file) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[fis.available()];
fis.read(buf);
fis.close();
return buf;
}
}
public class GenerateEncryptedPassword {
public static void main(String[] args){
Scanner sc= new Scanner(System.in);
System.out.println("Please enter the password that needs to be encrypted :");
String input = sc.next();
try {
String encryptedPassword= AESencrp.encrypt(input);
System.out.println("Encrypted password generated is :"+encryptedPassword);
} catch (Exception ex) {
Logger.getLogger(GenerateEncryptedPassword.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
If you use a static key, encrypt and decrypt always give the same result;
public static final String CRYPTOR_KEY = "your static key here";
byte[] keyByte = Base64.getDecoder().decode(CRYPTOR_KEY);
key = new SecretKeySpec(keyByte, "AES");