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");
Related
I am trying to encrypt a plain text string to integrate with a third-party system using AES encryption. The receiver does not have any document to explain what their encryption algorithm is, and they've simply shared the below Java code to explain how the encryption works:
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class CipherData {
private static final String SALT = "pa(MS"; //Salt. Required for key generation
private static final String ALGORITHM = "AES/CBC/PKCS5Padding"; //Encryption Algorithm/Cipher/Padding
private static String SECRET_KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA1";
private static int KEY_SIZE = 256;
private static final String TOKEN = "Pre123454sk"; //Password. Used for Key Generation
private static String initVector = "pre1234Init12345"; //IV. Required for Key generation
private static int KEY_ITERATIONS = 22123;
public static String encrypt(String value) throws Exception { //Encryption Module
Cipher cipher = Cipher.getInstance(ALGORITHM);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
Key key = generateKey();
cipher.init(1, key, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return base64Encode(encrypted);
}
public static String decrypt(String value) throws Exception { //Decryption module
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
Key key = generateKey();
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(2, key, iv);
byte[] original = cipher.doFinal(base64Decode(value));
return new String(original);
}
private static Key generateKey() throws Exception { //AES Key Generation
byte[] saltBytes = SALT.getBytes("UTF-8");
SecretKeyFactory skf = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
PBEKeySpec spec = new PBEKeySpec(TOKEN.toCharArray(), saltBytes, KEY_ITERATIONS, KEY_SIZE);
SecretKey secretKey = skf.generateSecret(spec);
SecretKeySpec key = new SecretKeySpec(secretKey.getEncoded(), "AES");
return key;
}
private static String base64Encode(byte[] token) {
String encoded = DatatypeConverter.printBase64Binary(token);
return encoded;
}
private static byte[] base64Decode(String token) {
return DatatypeConverter.parseBase64Binary(token);
}
public static void main(String[] args) {
String clearPAN="ABCDE1234F", encrPAN="", decrPAN="";
try {
encrPAN=encrypt(clearPAN);
decrPAN=decrypt(encrPAN);
System.out.println("Clear PAN: " + clearPAN);
System.out.println("Encrypted PAN: " + encrPAN);
System.out.println("Decrypted PAN: " + decrPAN);
}
catch (Exception e) {
System.out.print("Exception Occured in main()");
e.printStackTrace();
}
}
}
I am developing my application in .NET, and I'm unable to get the same string as the receiver gets by using the above Java code, and we're out of ideas on what we should be doing.
Here is my .NET algorithm (I have just inferred this logic from the Java code, and this is my first time with Java, so please be kind if I've made a stupid error):
private static String TOKEN = "Pre123454sk";
private static String initVector = "pre1234Init12345";
private static string Encryption(string plainText)
{
using (var aesProvider = new AesCryptoServiceProvider())
{
//String SALT = "pa(MS";
PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
pdb.IterationCount = 22123;
aesProvider.KeySize = 256;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Key = pdb.GetBytes(16);
aesProvider.IV = Encoding.UTF8.GetBytes(initVector);
Byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
ICryptoTransform encryptor = aesProvider.CreateEncryptor(aesProvider.Key, aesProvider.IV);
using (var memStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
Byte[] cipherTextBytes = memStream.ToArray();
memStream.Close();
memStream.Flush();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
private static string Decryption(string plainText)
{
using (var aesProvider = new AesCryptoServiceProvider())
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
pdb.IterationCount = 22123;
aesProvider.KeySize = 256;
aesProvider.Padding = PaddingMode.Zeros;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Key = pdb.GetBytes(16);
aesProvider.IV = Encoding.UTF8.GetBytes(initVector);
byte[] cipherTextBytes1 = Convert.FromBase64String(plainText);
ICryptoTransform decryptor = aesProvider.CreateDecryptor(aesProvider.Key, aesProvider.IV);
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes1))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
I've already tried replacing the TOKEN value with the IV value in the PasswordDeriveBytes() function, but it's still giving me the same error (the receiving system is unable to decrypt this request).
Here is the plain text that I'm trying to encrypt: CXQPM4656P
Here is what I'm getting from the .NET code: pKjfaKu4AxBEbagiAWoLkg==
Here is what I should be getting: kY8lgWh97fqkm9gS8zgMHg==
I'm out of ideas at this point, and I have no support from the other end. Would be great if someone can help me figure it out.
Thanks to Topaco (first comment in the question), I was able to sort it out.
Here is Topaco's original answer:
The Java code uses PBKDF2 with HMAC/SHA1, the C# code an algorithm based on PBKDF1. For PBKDF2 in the C# code PasswordDeriveBytes has to be replaced by Rfc2898DeriveBytes (with HMAC/SHA1 as default). Note that the .NET implementation expects a minimum 8 bytes salt. Also, Java uses a 32 bytes key, the C# code a 16 bytes key. With consistent key derivation and key size, the generated ciphertexts are identical. –
Topaco
The only thing I had to change was to upgrade from .NET 5 to .NET 6.
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;
}
}
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
private static String decrypt(String cipherString, PrivateKey key) {
byte[] dectyptedText = null;
byte[] stringText = null;
try {
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance(ALGORITHM);
//chiper init in encrypt mode
cipher.init(Cipher.ENCRYPT_MODE, key);
//tried to get bytes out of encrypted string
stringText = cipher.doFinal(cipherString.getBytes());
// decrypt the text using the private key
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(stringText);
} catch (Exception ex) {
ex.printStackTrace();
}
return new String(dectyptedText);
}
I want to convert the cipher text into bytes generated by the encryptor to a string and store in a database. Then get the string and decrypt it whenever its needed. Is there anyone that could help me solving the issue I'm having?
I does not make sense to convert the byte-Array to String.
You have to either save the bytes directly (which would require an appropriate column in the database, for example BLOB), or you could encode the byte-Array, for example using Base64 (I would recommend the latter).
(If your problems are with the "public-crypto thingy", you may want to use the public key to encrypt, but the private key to decrypt. If you dont know what that means, check out some literature about public-key crypto, please.)
Since your problem seems to be with your key, you possibly need a public key and a private key, not only a private key.
Have a look at this simple RSA demo:
encryptionAlgorithm = "RSA/ECB/PKCS1Padding";
algorithm = "RSA";
try {
SecureRandom random = SecRandom.getDefault();
// Since you are working with asymmetric crypto, you need a keypair:
KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm);
kpg.initialize(2048, random);
KeyPair kp = kpg.generateKeyPair();
// encrypting something with asymmetric crypto needs a public key:
Cipher cipher1 = Cipher.getInstance(encryptionAlgorithm);
cipher1.init(Cipher.ENCRYPT_MODE, kp.getPublic());
byte[] text = "This is a test".getBytes("ASCII");
System.out.println("text = " +(new String(text)));
byte[] ciphertext = cipher1.doFinal(text);
// here you could store & load your sipertext
System.out.println("ciphertext = " + ciphertext);
// decrypting something with asymmetric crypto needs a private key:
Cipher cipher2 = Cipher.getInstance(encryptionAlgorithm);
cipher2.init(Cipher.DECRYPT_MODE, kp.getPrivate());
byte[] cleartext = cipher2.doFinal(ciphertext);
System.out.println("cleartext = " +(new String(cleartext)));
} catch (Exception e) {
e.printStackTrace();
}
Why does not this AES encryption work? I've written it in Java to test, but I am not able to decrypt. I get garbage upon decryption. Why? Its so simple - In the main method, print plain text, encrypt, print cipher text, decrypt, print plain text again. Am I doing something wrong? Please help me figure out the problem.
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESTest {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
String cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String cipherText = new String(cipher.doFinal(plainText.getBytes()));
return cipherText;
}
public static String decrypt(String cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String plainText = new String(cipher.doFinal(cipherText.getBytes()));
return plainText;
}
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;
}
}
The output of the cipher is a sequence of random-looking bytes. You have no guarantee that these bytes will be a valid encoding for a character string in whatever is your system's default encoding. So this line:
String cipherText = new String(cipher.doFinal(.....));
is likely to lose information that you'll need for decryption.
Therefore you will not get the right bytes reconstructed in your decrypt operation. For example, if your default encoding is UTF-8, it is overwhelmingly unlikely that the correct ciphertext is something that String.getBytes() is even able to produce.
Two things:
No padding can only work if you use input that is an exact mulitple of your key size, which is 128 bit or 16 bytes. So in your particular case "Hello World!!!!!".getBytes() is actually a multiple of 16, but this is of course not true for arbitrary Strings.
Use "AES/CBC/PKCS5Padding" instead to solve this issue.
Do not turn your encrypted data into a String - this will and change the encrypted output. There's no guarantee that new String(byte[]).getBytes() returns the exact same byte array!
So you should leave the encrypted data as what it is - a stream of bytes. Thus encrypt should return byte[] instead and decrypt should take byte[] as input - this is a working example:
public class NewClass {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
byte[] cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
// -> Hello World!!!!
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return cipher.doFinal(plainText.getBytes());
}
public static String decrypt(byte[] cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return new String(cipher.doFinal(cipherText));
}
You need to create the SecretKeySpec object once and use it for both encrypt and decrypt. Currently the code is creating two different keys for each operation and this will definitely lead to incorrect results.