I'm currently attempting to implement an encryption and decryption function for a basic Java program, using the following methods:
private static String encryptString(PublicKey publicKey, String message) throws Exception {
Cipher encrypt = Cipher.getInstance("RSA/ECB/PKCS1Padding");
encrypt.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypted = encrypt.doFinal(message.getBytes());
return Base64.getEncoder().encodeToString(encrypted);
}
private static String decryptString(PrivateKey privateKey, String message) throws Exception {
Cipher decrypt = Cipher.getInstance("RSA/ECB/PKCS1Padding");
decrypt.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = decrypt.doFinal(message.getBytes());
return Base64.getEncoder().encodeToString(decrypted);
}
Encoding the message using the public key appears to work correctly. However, when attempting to decrypt it using the private key, javax.crypto throws an IllegalBlockSizeException saying data must not be longer than 128 bytes.
Related
I have simple json object as:
json:
{
"bookInfo": {
"type": "book",
"onvan": "کتاب دو",
"tadvin": "دفتر",
"nasher": "بوستان",
"nobat": "اول",
"shabek": "1100",
"gheymat": "2000 تومان",
"tasvirData": ""
}
}
after encryption that on c# with 123 key, i have this result:
hlAkzlLeVE3JdYrk61dy8901K2tXWqx5qxYd1t8zVSZzx12lD6nxqZRlPRI8yX8PECxaHJ5zdueY0/A0J7Lxxlv2DHvdI/H+Qu2bsQI6X/Qc6ISwlY7Q6c0IiwWtKuFm5f8BC9wNSSqPXkBM7J+hwEtHUBAoh+IMzxNXvnA/hZIp3R2FznX4cdJhs4Lnm003WLGiKwJ1fEgzUl55WKBIh2dMwQwqpTlNmLFIo6ovlJYMt4DTaoeET+VAhHcGtX1u10910EZ1hCqb1pcspE1SPQ==
now when I try to decrypt that with 123 key I get this error:
Unsupported key size: 3 bytes
EncryptUtils class:
public class EncryptUtils {
public static SecretKey generateKey(String mySecret) throws NoSuchAlgorithmException, InvalidKeySpecException {
return new SecretKeySpec(mySecret.getBytes(), "AES");
}
public static byte[] encryptMsg(String message, SecretKey secret)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8"));
return cipherText;
}
public static String decryptMsg(byte[] cipherText, SecretKey secret)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
String decryptString = new String(cipher.doFinal(cipherText), "UTF-8");
return decryptString;
}
}
and this is my decrypt code:
String mySecret="123";
SecretKey secretKey = EncryptUtils.generateKey(mySecret);
JSONObject bookObject = new JSONObject(EncryptUtils.decryptMsg(bookContent.getBytes("UTF-8"), secretKey));
The problem is that the string you are passing in to the hexToBytes function is Base64 encoded, it's not a hex string, so reading the first two characters as an integer causes an exception.
Change the line in decrypt to this:
String decryptString = new String(cipher.doFinal(cipherText), "UTF-8");
To:
String decryptString= cipher.doFinal(Base64.decode(cipherText,Base64.DEFAULT));
Happy coding!!
I would try to use RC6 algorithm but i have an error:
RC6 KeyGenerator not available
How can i obtain the keygenerator of the rc6?
Exception in thread "main" java.security.NoSuchAlgorithmException: RC6 KeyGenerator not available
at javax.crypto.KeyGenerator.(KeyGenerator.java:169)
at javax.crypto.KeyGenerator.getInstance(KeyGenerator.java:223)
at RC6.encrypt(RC6.java:27)
at RC6.main(RC6.java:16)
import javax.crypto.spec.*;
import java.security.*;
import javax.crypto.*;
public class Main
{
private static String algorithm = "RC6";
public static void main(String []args) throws Exception {
String toEncrypt = "The shorter you live, the longer you're dead!";
System.out.println("Encrypting...");
byte[] encrypted = encrypt(toEncrypt, "password");
System.out.println("Decrypting...");
String decrypted = decrypt(encrypted, "password");
System.out.println("Decrypted text: " + decrypted);
}
public static byte[] encrypt(String toEncrypt, String key) throws Exception {
// create a binary key from the argument key (seed)
SecureRandom sr = new SecureRandom(key.getBytes());
KeyGenerator kg = KeyGenerator.getInstance(algorithm);
kg.init(sr);
SecretKey sk = kg.generateKey();
// create an instance of cipher
Cipher cipher = Cipher.getInstance(algorithm);
// initialize the cipher with the key
cipher.init(Cipher.ENCRYPT_MODE, sk);
// enctypt!
byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
return encrypted;
}
public static String decrypt(byte[] toDecrypt, String key) throws Exception {
// create a binary key from the argument key (seed)
SecureRandom sr = new SecureRandom(key.getBytes());
KeyGenerator kg = KeyGenerator.getInstance(algorithm);
kg.init(sr);
SecretKey sk = kg.generateKey();
// do the decryption with that key
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, sk);
byte[] decrypted = cipher.doFinal(toDecrypt);
return new String(decrypted);
}
}
RC6 is not an algorithm that is provided by one of the Oracle security providers. The providers provide the algorithm implementations that are behind Cipher and indeed KeyGenerator.
This should work, after adding the Bouncy Castle provider .jar in the classpath:
static {
Security.addProvider(new BouncyCastleProvider());
}
You may also need to install the unlimited cryptography files in your JRE folder.
I've created a RollingFileAppender which encrypts the output to a log4j log file.
Currently it uses AES/ECB/NoPadding, and it works fine.
Here is how we create the cipher
public static Cipher getCipher(boolean encrypt) throws Exception {
//https://en.wikipedia.org/wiki/Stream_cipher
byte[] key = ("sometestkey").getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
Key k = new SecretKeySpec(key,"AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
if (encrypt) {
cipher.init(Cipher.ENCRYPT_MODE, k);
} else {
cipher.init(Cipher.DECRYPT_MODE, k);
}
return cipher;
}
Here's how we create the appender :
public class EncryptingRollingFileAppender extends RollingFileAppender {
private CipherOutputStream s;
private Cipher cipher;
public EncryptingRollingFileAppender() {super();}
public EncryptingRollingFileAppender(Layout layout, String filename, boolean append) throws IOException {super(layout, filename, append);}
public EncryptingRollingFileAppender(Layout layout, String filename) throws IOException {super(layout, filename);}
#Override
protected OutputStreamWriter createWriter(OutputStream outputStream) {
if (cipher==null) {
try {
cipher = DecryptionTools.getCipher(true);
s = new CipherOutputStream(outputStream, cipher);
} catch (Throwable t) {
throw new RuntimeException("failed to initialise encrypting file appender",t);
}
}
OutputStreamWriter out = super.createWriter(s);
return out;
}
}
We can decrypt the file by using
getCipher(false)
to create an appropriate decryption stream.
The issue is that our security team are haggling about key management.
They don't like the use symetric key encryption, and would prefer us to use a key pair rather than a simple password that we have to manage in some way.
Does anyone know of a non-padding ECB encryption technique that would use a key pair, and would be appropriate for this kind of stream encryption and decryption?
The comments suggesting using hybrid encryption and PGP are right.
PGP is the defacto standard for hybrid encryption of files, and it is a much more robust solution that ECB mode AES.
Due to the nature of PGP it is going to work slightly differently to your existing solution.
PGP messages have a header and a footer, so you will want each file to be individually encrypted (you wont just be able to decrypt individual blocks like you can with plain ECB mode encryption).
It looks like you are using log4j 1.2, I have created a working implementation of a PGP encrypting RollingFileAppender.
To use the example, generate an armour encoded PGP public key, run the main class then decrypt the file with any PGP tool (I used GnuPG for creating the keys and decrypting).
The example was built against log4j:log4j:1.2.17 and org.bouncycastle:bcpg-jdk15on:1.56
You are pretty close to what you want to achieve. Using Log4j 1.2 (because you cannot subclass RollingFileAppender directly in Log4j 2) you can generate a password on the fly for each log file, encrypt the password with RSA and store next to it. Then use the password to produce an AES CipherOutputStream for the log appender.
public class EncryptingRollingFileAppender extends RollingFileAppender {
private CipherOutputStream s;
private Cipher cipher;
private byte[] secretKey;
public EncryptingRollingFileAppender(Layout layout, String filename, boolean append) throws IOException {
super(layout, filename, append);
writeKeyFile(filename);
}
public EncryptingRollingFileAppender(Layout layout, String filename) throws IOException {
super(layout, filename);
writeKeyFile(filename);
}
private void writeKeyFile(final String logfilename) throws IOException {
final int dot = logfilename.lastIndexOf('.');
final String keyfilename = (dot == -1 ? logfilename : logfilename.substring(0, dot)) + ".key";
try (FileOutputStream out = new FileOutputStream(keyfilename)) {
out.write(DecryptionTools.encryptPasswordBase64(secretKey).getBytes(ISO_8859_1));
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | KeyStoreException
| CertificateException e) {
}
}
#Override
protected OutputStreamWriter createWriter(OutputStream outputStream) {
System.out.println("createWriter()");
if (cipher == null) {
secretKey = DecryptionTools.generateRandomKey(16).getBytes(ISO_8859_1);
try {
cipher = DecryptionTools.getCipher(true, secretKey);
} catch (InvalidKeyException e) {
System.out.println("InvalidKeyException");
}
s = new CipherOutputStream(outputStream, cipher);
}
OutputStreamWriter out = super.createWriter(s);
return out;
}
}
You´ll need several helper functions for reading the private key from a file or from a Java key store which can be found here.
The test file TestEncryptingRollingFileAppender shows how to write an encrypted log and read it back.
import static com.acme.DecryptionTools.getCipher;
import static com.acme.DecryptionTools.decryptPasswordBase64;
public class TestEncryptingRollingFileAppender {
#SuppressWarnings("deprecation")
#Test
public void testAppender() throws IOException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, KeyStoreException, CertificateException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException {
final File logfile = File.createTempFile("testlog_", ".log");
final String logfilename = logfile.getAbsolutePath();
final Logger lggr = LogManager.getLogger(TestEncryptingRollingFileAppender.class);
final EncryptingRollingFileAppender appender = new EncryptingRollingFileAppender(new SimpleLayout(), logfilename, true);
appender.append(new LoggingEvent(lggr.getClass().getName(), lggr, Priority.INFO, "Test Log Line #1", null));
appender.append(new LoggingEvent(lggr.getClass().getName(), lggr, Priority.INFO, "Test Log Line #1", null));
final int dot = logfilename.lastIndexOf('.');
byte[] key = decryptPasswordBase64(new String(Files.readAllBytes(Paths.get(logfilename.substring(0, dot)+".key"))));
StringBuilder logContent = new StringBuilder();
try (FileInputStream instrm = new FileInputStream(logfilename);
CipherInputStream cistrm = new CipherInputStream(instrm, getCipher(false, key))) {
int c;
while ((c=cistrm.read())!=-1)
logContent.append((char) c);
}
assertEquals("INFO - Test Log Line #1\r\nINFO - Test Log Line #1", logContent.toString());
}
}
I am currently working in a solution, where I have encrypt a password in Java (Running in Jboss server), and then send to Data Power and de-crypt in DataPower. I able to encrypt the password in Java. I dont have much knowledge in DataPower. I have got the code to decrypt in datapower, but not sure how to send the key to datapower. Can anyone please help me out. The code is like below,
Encryption Code (Java)
package test;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.util.ArrayList;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class Test {
private static final String ALGO = "AES";
public static String encrypt(String data,String secretKey) throws Exception {
Key key = generateKey(secretKey);
byte[] ivAES = {(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22,(byte)0x22};
IvParameterSpec ivspec = new IvParameterSpec(ivAES);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, ivspec);
byte[] encVal = cipher.doFinal(data.getBytes());
byte[] finalByte=copyBytes(encVal, ivAES);
String encryptedValue = new BASE64Encoder().encode(finalByte);
return encryptedValue;
}
public static String decrypt(String encryptedData,String secretKey) throws Exception {
Key key = generateKey(secretKey);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
ArrayList<byte[]> al = retreiveIv(decordedValue);
byte[] data = al.get(0);
byte[] iv = al.get(1);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] decValue = cipher.doFinal(data);
String decryptedValue = new String(decValue);
return decryptedValue;
}
private static Key generateKey(String secretKey) throws Exception {
Key key = new SecretKeySpec(secretKey.getBytes(), ALGO);
return key;
}
private static byte[] copyBytes(byte[] encVal, byte[] iv) throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(iv);
os.write(encVal);
return os.toByteArray();
}
private static ArrayList<byte[]> retreiveIv(byte[] combineByte) {
ByteArrayOutputStream iv = new ByteArrayOutputStream(16);
ByteArrayOutputStream data = new ByteArrayOutputStream();
data.write(combineByte, combineByte.length - 16, 16);
iv.write(combineByte, 0, combineByte.length - 16);
ArrayList<byte[]> al = new ArrayList<byte[]>();
al.add(data.toByteArray());
al.add(iv.toByteArray());
return al;
}
public static void main(String[] args) throws Exception{
System.out.println(AESUtil.decrypt(AESUtil.encrypt("Hello", "AVH4TU8AC99dhL2l"), "AVH4TU8AC99dhL2l"));
}
}
Decryption (DataPower)
<xsl:variable name="algorithm">http://www.w3.org/2001/04/xmlenc#aes128-cbc</xsl:variable>
<xsl:variable name="decryptOut"><xsl:value-of select="dp:decrypt-data($algorithm,'name:danadp',$valueToDecrypt)"/></xsl:variable>
Decrypted Value: <xsl:copy-of select="$decryptOut"/>
But, I am not getting how to share the Key object to DataPower that we generated in Java code for encryption.
It will be great, if anyone can help me.
Create a shared secret key object with name 'danadp' and the key copied into a file and imported into the cert:/ folder in the DataPower domain where this code is running.
P.S Remember to prefix the key with "0x" if key is in hex.
I'm relatively new to developing something with encryption. Right now I'm trying to write a class which encrypts and decrypts Strings using BouncyCastle with AES-GCM. I read about the things you have to consider when implementing encryption. One of them was that you should always use a randomized IV.
The problem is, everytime I try to initialize my Cipher with an IV it won't decrypt my text properly.
It just throws the following exception:
javax.crypto.AEADBadTagException: mac check in GCM failed
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$AEADGenericBlockCipher.doFinal(Unknown Source)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at BouncyCastleEX.decrypt(BouncyCastleEX.java:78)
at BouncyCastleEX.main(BouncyCastleEX.java:43)
I'm using the following methods to encrypt and decrypt my data.
private static final String fallbackSalt = "ajefa6tc73t6raiw7tr63wi3r7citrawcirtcdg78o2vawri7t";
private static final int iterations = 2000;
private static final int keyLength = 256;
private static final SecureRandom random = new SecureRandom();
public byte[] encrypt(String plaintext, String passphrase, String salt)
throws Exception {
SecretKey key = generateKey(passphrase, salt);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key, generateIV(cipher),random);
return cipher.doFinal(plaintext.getBytes());
}
public String decrypt(byte[] encrypted, String passphrase, String salt)
throws Exception {
SecretKey key = generateKey(passphrase, salt);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
cipher.init(Cipher.DECRYPT_MODE, key, generateIV(cipher),random);
return new String(cipher.doFinal(encrypted));
}
private SecretKey generateKey(String passphrase, String salt)
throws Exception {
PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray(),
salt.getBytes(), iterations, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory
.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");
return keyFactory.generateSecret(keySpec);
}
private IvParameterSpec generateIV(Cipher cipher) throws Exception {
byte[] ivBytes = new byte[cipher.getBlockSize()];
random.nextBytes(ivBytes);
return new IvParameterSpec(ivBytes);
}
If I remove the "generateIV(cipher)" from my cipher.init(...) everything works flawlessly. But as far as I know it weakens the encryption tremendously.
Right know I'm unable to figure out whether this is a small mistake in the code or something else I know nothing about.
I really appreciate your help and thanks a lot!
You have to use the same IV for encryption and decryption. It doesn't have to be secret, but only unique for AES-GCM (it's technically a nonce). A common way is to prepend the IV to the ciphertext and remove it before decryption.
It's also common to use a message counter instead of randomly generating an IV. If the key is changed then you should reset the IV to an initial value and start counting again. At some number of messages, you need a new key, because the security guarantees of AES-GCM break down. That number is somewhere between 248 and 264 messages.
Here is the final version of my code which I wrote with the help of Artjom.
It seems to work great but if you find any mistakes or things that weaken the security, please let me know.
import java.security.SecureRandom;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Arrays;
import com.sun.org.apache.xml.internal.security.utils.Base64;
public class BouncyCastleEX {
private static final int iterations = 2000;
private static final int keyLength = 256;
private static final SecureRandom random = new SecureRandom();
private static BouncyCastleEX instance = null;
public String encryptString(String plaintext, String passphrase, String salt)
throws Exception {
return Base64.encode(encrypt(plaintext, passphrase, salt));
}
public String decryptString(String encrypted, String passphrase, String salt)
throws Exception {
return decrypt(Base64.decode(encrypted), passphrase, salt);
}
private BouncyCastleEX() {
Security.addProvider(new BouncyCastleProvider());
}
public static BouncyCastleEX getInstance() {
if (instance == null) {
instance = new BouncyCastleEX();
}
return instance;
}
private byte[] encrypt(String plaintext, String passphrase, String salt)
throws Exception {
SecretKey key = generateKey(passphrase, salt);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
byte[] ivBytes = generateIVBytes(cipher);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivBytes),
random);
return Arrays
.concatenate(ivBytes, cipher.doFinal(plaintext.getBytes()));
}
private String decrypt(byte[] encrypted, String passphrase, String salt)
throws Exception {
SecretKey key = generateKey(passphrase, salt);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
cipher.init(Cipher.DECRYPT_MODE, key,
new IvParameterSpec(Arrays.copyOfRange(encrypted, 0, 12)),
random);
return new String(cipher.doFinal(Arrays.copyOfRange(encrypted, 12,
encrypted.length)));
}
private SecretKey generateKey(String passphrase, String salt)
throws Exception {
PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray(),
salt.getBytes(), iterations, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory
.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");
return keyFactory.generateSecret(keySpec);
}
private byte[] generateIVBytes(Cipher cipher) throws Exception {
byte[] ivBytes = new byte[12];
random.nextBytes(ivBytes);
return ivBytes;
}
}