Java encrypt word by word - java

I'm trying to take in a long string and encrypt it using the following code:
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.*;
public class AESEncrypt {
/**
* Turns array of bytes into string
*
* #param buf
* Array of bytes to convert to hex string
* #return Generated hex string
*/
public static String asHex(byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
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;
}
public static void main(String[] args) throws Exception {
String message = "Test text!";
// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
System.out.println("Key: " + asHex(raw));
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal((args.length == 0 ? message : args[0]).getBytes());
System.out.println("encrypted string: " + asHex(encrypted));
}
}
However, I would like to encrypt word by word and print out the encrypted text as such:
Original string -> Test text!
Encrypted string -> 29f84h2f 23f9f92jf3
I couldn't find any examples online that could help me out. Is there anyway I can achieve this?

AES is a block cypher, and it uses 16 byte blocks. It does not work in words, but in fixed blocks. If you want to split up your text into varying size words then you will likely get closer to what you want by using either a stream cypher, such as RC4, or alternatively AES in CTR mode, which effectively turns AES into a stream cypher. Stream cyphers do not work in blocks, but in bytes. You can take 3 bytes off the stream for a 3 letter word, or nine bytes off the stream for a 9 letter word.
You will need to work out what to do with spaces, punctuation etc between words. You will also need to think about how often you re-key the cypher. Do you want to re-key for every individual word, or do you just re-key at the start of each string? As with any stream cypher, never ever use the same key twice.

Try as below example; Actually, It just need to used StringTokenizer. Firstly you have to token your target string. After that, encrypt the token string.
import java.util.StringTokenizer;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class AES {
public static String asHex(byte[] buf) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int)buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int)buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public static void main(String[] args) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
String target = "This is just an example";
StringTokenizer token = new StringTokenizer(target);
while(token.hasMoreTokens()) {
String temp = token.nextToken();
byte[] encrypted = cipher.doFinal((args.length == 0 ? temp : args[0]).getBytes());
System.out.println(asHex(encrypted) + " ");
}
}
}
Output :
d40186eab04d10e299801e7ad9046c06 6a71265c768a3b6e1f1a8f891d621c1d 735e3f54c8ad7242466e3517e8dd1659 5216643345db0f0c12f65c66c5363be3 b823355d5bb31bf092df98e18fa8001c

Related

Limit Size of Encrypted Data using 3DES

I am trying to encrypt and decrypt a string of data using 3DES and it is working fine. However I want that the size of the data after encryption to be limited to length of 16 bits.
This is the code I am referring from https://gist.github.com/riversun/6e15306cd6e3b1b37687a0e5cec1cef1 :
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class DesedeCrypter {
private static final String CRYPT_ALGORITHM = "DESede";
private static final String PADDING = "DESede/CBC/NoPadding";
private static final String CHAR_ENCODING = "UTF-8";
private static final byte[] MY_KEY = "5oquil2oo2vb63e8ionujny6".getBytes();//24-byte
private static final byte[] MY_IV = "3oco1v52".getBytes();//8-byte
public static void main(String[] args) {
String srcText = "M3A1B2C3D4HHG393";
final DesedeCrypter crypter = new DesedeCrypter();
String encryptedText = crypter.encrypt(srcText);
System.out.println("sourceText=" + srcText + " -> encryptedText=" + encryptedText + "\n");
System.out.println("encrypted-text=" + encryptedText + " -> decrypted-text(source text)="
+ crypter.decrypt(encryptedText));
}
public String encrypt(String text) {
String retVal = null;
try {
final SecretKeySpec secretKeySpec = new SecretKeySpec(MY_KEY, CRYPT_ALGORITHM);
final IvParameterSpec iv = new IvParameterSpec(MY_IV);
final Cipher cipher = Cipher.getInstance(PADDING);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
final byte[] encrypted = cipher.doFinal(text.getBytes(CHAR_ENCODING));
retVal = new String(encodeHex(encrypted));
} catch (Exception e) {
e.printStackTrace();
}
return retVal;
}
public String decrypt(String text) {
String retVal = null;
try {
final SecretKeySpec secretKeySpec = new SecretKeySpec(MY_KEY, CRYPT_ALGORITHM);
final IvParameterSpec iv = new IvParameterSpec(MY_IV);
final Cipher cipher = Cipher.getInstance(PADDING);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
final byte[] decrypted = cipher.doFinal(decodeHex(text.toCharArray()));
retVal = new String(decrypted, CHAR_ENCODING);
} catch (Exception e) {
e.printStackTrace();
}
return retVal;
}
private byte[] decodeHex(char[] data) throws Exception {
int len = data.length;
if ((len & 0x01) != 0) {
throw new Exception("Odd number of characters.");
}
byte[] out = new byte[len >> 1];
// two characters form the hex value.
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(data[j], j) << 4;
j++;
f = f | toDigit(data[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
private int toDigit(char ch, int index) throws Exception {
int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new Exception("Illegal hexadecimal character " + ch + " at index " + index);
}
return digit;
}
private char[] encodeHex(byte[] data) {
final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
int l = data.length;
char[] out = new char[l << 1];
// two characters form the hex value.
for (int i = 0, j = 0; i < l; i++) {
out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
out[j++] = DIGITS[0x0F & data[i]];
}
return out;
}
}
Currently this is the output I am getting:
sourceText=M3A1B2C3D4HHG393 -> encryptedText=afc1d48ea5cc703253cbc1a88a198103
encrypted-text=afc1d48ea5cc703253cbc1a88a198103 -> decrypted-text(source text)=M3A1B2C3D4HHG393
Is there any way that the size of the encryptedText be limited to 16 as I want to add the encrypted text back into a message which has 16 digits space for encrypted text.
Please suggest some way or any other change that is required to achieve this. Thanks !
For one, I highly recommend not supporting (3)DES anymore, as it's officially unsecure in favour of AES/ChaCha, which I must say before answering this question.
3DES has a block size of 64 bits (or 8 bytes). With that also comes that encryption ≠ compression. So if you want to encrypt 16 bytes, provide 16 bytes of input, unless:
You apply a padding scheme (which it appears you're not doing)(taken from the DESede/CBC/NoPadding
You apply a (random) initialisation vector (which it appears you're doing)
The latter one should, but I'm not to sure of Android's implementation, create a 64 bits (8 byte) iv, as the iv should be as big as the block size of the cipher (again, 64 bits for 3DES).
So if you want 16 bytes of output, you can, and should, only provide 8 bytes to encrypt. Now, if 8 bytes is not enough, you might choose to drop the iv from being in the ciphertext, which you could do if you use a fixed iv (such as an all zero iv) with random keys, as reusing the same key with iv is not secure.
Now if you do take security in consideration, keep in mind that AES has a block size of 16 bytes. AES and ChaCha come with the same constraints regarding the iv.
Then you might also might want to consider changing the message (protocol) instead, so it can take more than 16 bytes of data or use those 16 bytes in such a way that it indicates that there is more data to handle, as like an attachment to an e-mail.

Reading a PKCS#1 or SPKI public key in Java without libraries

I need to use a public key to verify some data in Java, but I can't seem to format the key in such a way that Java can use without third-party plugins.
I'm generating the key with Node.js's crypto library, which gives me the option of PKCS#1 or SPKI, and either .pem or .der file format.
I've heard that Java doesn't support PKCS#1 out-of-the box, and pretty much every other answer on StackOverflow recommends using BouncyCastle or similar, but in my case, I am writing an SDK, and simply cannot afford to use a library just to read this public key.
So I'm currently reading the key in .der format as it saves having to strip the PEM headers and decode the key from base-64. When I run this, I get the error:
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0000be:ASN.1 encoding routines:OPENSSL_internal:WRONG_TAG
Here's what I have (sorry, it's in Kotlin, not Java like the title suggests)
// Here's a key for convenience
val key = Base64.getDecoder().decode("MFUCTgF/uLsPBS13Gy7C3dPpiDF6SYCLUyyl6CFqPtZT1h5bwKR9EDFLQjG/kMiwkRMcmEeaLKe5qdj9W/FfFitwRAm/8F53pQw2UETKQI2b2wIDAQAB");
val keySpec = X509EncodedKeySpec(key)
val keyFactory = KeyFactory.getInstance("RSA")
val publicKey = keyFactory.generatePublic(keySpec) // error thrown here
val cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, publicKey)
My best idea at the minute is to install a library on the Node.js side, which is less problematic, to support exporting the key as PKCS#8, but I thought I'd check first to see if I'm missing anything.
The following code turns a PKCS#1 encoded public key into a SubjectPublicKeyInfo encoded public key, which is the public key encoding accepted by the RSA KeyFactory using X509EncodedKeySpec - as SubjectPublicKeyInfo is defined in the X.509 specifications.
Basically it is a low level DER encoding scheme which
wraps the PKCS#1 encoded key into a bit string (tag 0x03, and a encoding for the number of unused bits, a byte valued 0x00);
adds the RSA algorithm identifier sequence (the RSA OID + a null parameter) in front - pre-encoded as byte array constant;
and finally puts both of those into a sequence (tag 0x30).
No libraries are used. Actually, for createSubjectPublicKeyInfoEncoding, no import statements are even required.
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class PKCS1ToSubjectPublicKeyInfo {
private static final int SEQUENCE_TAG = 0x30;
private static final int BIT_STRING_TAG = 0x03;
private static final byte[] NO_UNUSED_BITS = new byte[] { 0x00 };
private static final byte[] RSA_ALGORITHM_IDENTIFIER_SEQUENCE =
{(byte) 0x30, (byte) 0x0d,
(byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01,
(byte) 0x05, (byte) 0x00};
public static RSAPublicKey decodePKCS1PublicKey(byte[] pkcs1PublicKeyEncoding)
throws NoSuchAlgorithmException, InvalidKeySpecException
{
byte[] subjectPublicKeyInfo2 = createSubjectPublicKeyInfoEncoding(pkcs1PublicKeyEncoding);
KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey generatePublic = (RSAPublicKey) rsaKeyFactory.generatePublic(new X509EncodedKeySpec(subjectPublicKeyInfo2));
return generatePublic;
}
public static byte[] createSubjectPublicKeyInfoEncoding(byte[] pkcs1PublicKeyEncoding)
{
byte[] subjectPublicKeyBitString = createDEREncoding(BIT_STRING_TAG, concat(NO_UNUSED_BITS, pkcs1PublicKeyEncoding));
byte[] subjectPublicKeyInfoValue = concat(RSA_ALGORITHM_IDENTIFIER_SEQUENCE, subjectPublicKeyBitString);
byte[] subjectPublicKeyInfoSequence = createDEREncoding(SEQUENCE_TAG, subjectPublicKeyInfoValue);
return subjectPublicKeyInfoSequence;
}
private static byte[] concat(byte[] ... bas)
{
int len = 0;
for (int i = 0; i < bas.length; i++)
{
len += bas[i].length;
}
byte[] buf = new byte[len];
int off = 0;
for (int i = 0; i < bas.length; i++)
{
System.arraycopy(bas[i], 0, buf, off, bas[i].length);
off += bas[i].length;
}
return buf;
}
private static byte[] createDEREncoding(int tag, byte[] value)
{
if (tag < 0 || tag >= 0xFF)
{
throw new IllegalArgumentException("Currently only single byte tags supported");
}
byte[] lengthEncoding = createDERLengthEncoding(value.length);
int size = 1 + lengthEncoding.length + value.length;
byte[] derEncodingBuf = new byte[size];
int off = 0;
derEncodingBuf[off++] = (byte) tag;
System.arraycopy(lengthEncoding, 0, derEncodingBuf, off, lengthEncoding.length);
off += lengthEncoding.length;
System.arraycopy(value, 0, derEncodingBuf, off, value.length);
return derEncodingBuf;
}
private static byte[] createDERLengthEncoding(int size)
{
if (size <= 0x7F)
{
// single byte length encoding
return new byte[] { (byte) size };
}
else if (size <= 0xFF)
{
// double byte length encoding
return new byte[] { (byte) 0x81, (byte) size };
}
else if (size <= 0xFFFF)
{
// triple byte length encoding
return new byte[] { (byte) 0x82, (byte) (size >> Byte.SIZE), (byte) size };
}
throw new IllegalArgumentException("size too large, only up to 64KiB length encoding supported: " + size);
}
public static void main(String[] args) throws Exception
{
// some weird 617 bit key, which is way too small and not a multiple of 8
byte[] pkcs1PublicKeyEncoding = Base64.getDecoder().decode("MFUCTgF/uLsPBS13Gy7C3dPpiDF6SYCLUyyl6CFqPtZT1h5bwKR9EDFLQjG/kMiwkRMcmEeaLKe5qdj9W/FfFitwRAm/8F53pQw2UETKQI2b2wIDAQAB");
RSAPublicKey generatePublic = decodePKCS1PublicKey(pkcs1PublicKeyEncoding);
System.out.println(generatePublic);
}
}
Notes:
NoSuchAlgorithmException should probably be caught and put into a RuntimeException;
the private method createDERLengthEncoding should probably not accept negative sizes.
Larger keys have not been tested, please validate createDERLengthEncoding for those - I presume it works, but better be safe than sorry.

RC4 Encryption/Decryption with C# and Java

I even use the AES algorithm to encrypt and decrypt files, but according to my research, the performance of this algorithm is slower than the RC4 algorithm in Java.
I'm use this code for encrypt files in C#
public static class RC4
{
public static byte[] Encrypt(byte[] key, byte[] data)
{
return EncryptOutput(key, data).ToArray();
}
private static byte[] EncryptInitalize(byte[] key)
{
byte[] s = Enumerable.Range(0, 256)
.Select(i => (byte)i)
.ToArray();
for (int i = 0, j = 0; i < 256; i++)
{
j = (j + key[i % key.Length] + s[i]) & 255;
Swap(s, i, j);
}
return s;
}
private static IEnumerable<byte> EncryptOutput(byte[] key, IEnumerable<byte> data)
{
byte[] s = EncryptInitalize(key);
int i = 0;
int j = 0;
return data.Select((b) =>
{
i = (i + 1) & 255;
j = (j + s[i]) & 255;
Swap(s, i, j);
return (byte)(b ^ s[(s[i] + s[j]) & 255]);
});
}
private static void Swap(byte[] s, int i, int j)
{
byte c = s[i];
s[i] = s[j];
s[j] = c;
}
}
I need to encrypt a file in C # and decrypt this file with java, but found no implementation for both languages.
This solution implemented by Michael Remijan showed better performance to decrypt files using AES. Encrypt and Decrypt files for I implemented just a string conversion to byte array.
Java Code
package org.ferris.aes.crypto;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.spec.KeySpec;
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 org.apache.commons.codec.binary.Base64;
/**
*
* #author Michael Remijan mjremijan#yahoo.com #mjremijan
*/
public class AesBase64Wrapper {
private static String IV = "IV_VALUE_16_BYTE";
private static String PASSWORD = "PASSWORD_VALUE";
private static String SALT = "SALT_VALUE";
public String encryptAndEncode(String raw) {
try {
Cipher c = getCipher(Cipher.ENCRYPT_MODE);
byte[] encryptedVal = c.doFinal(getBytes(raw));
String s = getString(Base64.encodeBase64(encryptedVal));
return s;
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
public String decodeAndDecrypt(String encrypted) throws Exception {
byte[] decodedValue = Base64.decodeBase64(getBytes(encrypted));
Cipher c = getCipher(Cipher.DECRYPT_MODE);
byte[] decValue = c.doFinal(decodedValue);
return new String(decValue);
}
private String getString(byte[] bytes) throws UnsupportedEncodingException {
return new String(bytes, "UTF-8");
}
private byte[] getBytes(String str) throws UnsupportedEncodingException {
return str.getBytes("UTF-8");
}
private Cipher getCipher(int mode) throws Exception {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = getBytes(IV);
c.init(mode, generateKey(), new IvParameterSpec(iv));
return c;
}
private Key generateKey() throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
char[] password = PASSWORD.toCharArray();
byte[] salt = getBytes(SALT);
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
byte[] encoded = tmp.getEncoded();
return new SecretKeySpec(encoded, "AES");
}
}
C# Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace EncryptDecryptTest
{
class Program
{
class AesBase64Wrapper
{
private static string IV = "IV_VALUE_16_BYTE";
private static string PASSWORD = "PASSWORD_VALUE";
private static string SALT = "SALT_VALUE";
public static string EncryptAndEncode(string raw)
{
using (var csp = new AesCryptoServiceProvider())
{
ICryptoTransform e = GetCryptoTransform(csp, true);
byte[] inputBuffer = Encoding.UTF8.GetBytes(raw);
byte[] output = e.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
string encrypted = Convert.ToBase64String(output);
return encrypted;
}
}
public static string DecodeAndDecrypt(string encrypted)
{
using (var csp = new AesCryptoServiceProvider())
{
var d = GetCryptoTransform(csp, false);
byte[] output = Convert.FromBase64String(encrypted);
byte[] decryptedOutput = d.TransformFinalBlock(output, 0, output.Length);
string decypted = Encoding.UTF8.GetString(decryptedOutput);
return decypted;
}
}
private static ICryptoTransform GetCryptoTransform(AesCryptoServiceProvider csp, bool encrypting)
{
csp.Mode = CipherMode.CBC;
csp.Padding = PaddingMode.PKCS7;
var spec = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(PASSWORD), Encoding.UTF8.GetBytes(SALT), 65536);
byte[] key = spec.GetBytes(16);
csp.IV = Encoding.UTF8.GetBytes(IV);
csp.Key = key;
if (encrypting)
{
return csp.CreateEncryptor();
}
return csp.CreateDecryptor();
}
}
static void Main(string[] args)
{
string encryptMe;
string encrypted;
string decrypted;
encryptMe = "please encrypt me";
Console.WriteLine("encryptMe = " + encryptMe);
encrypted = AesBase64Wrapper.EncryptAndEncode(encryptMe);
Console.WriteLine("encypted: " + encrypted);
decrypted = AesBase64Wrapper.DecodeAndDecrypt(encrypted);
Console.WriteLine("decrypted: " + decrypted);
Console.WriteLine("press any key to exit....");
Console.ReadKey();
}
}
}
Based on your comments, I am assuming you want to know how to speed up your encryption / decryption process, and changing the main algorithm is not mandatory.
You could look at different modes for AES. For example, AES in counter (CTR) mode is significantly faster than cipher block chaining (CBC) which is often used.
Try creating your cipher like
Cipher myCipher = Cipher.getInstance("AES/CTR/NoPadding");
and you should see a performance increase. Additionally, using NoPadding will keep the size the same as the plaintext.
(Yes, I know that CTR mode turn AES into a stream cipher, never mind my comment)
UPDATE
I have used this in the past along these lines:
Key key = new SecretKeySpec(yourKeyValue, "AES");
Cipher enc = Cipher.getInstance("AES/CTR/NoPadding");
enc.init(Cipher.ENCRYPT_MODE, key);
// Get the IV that was generated
byte[] iv = enc.getIV();
// Encrypt your data
...
Cipher dec = Cipher.getInstance("AES/CTR/NoPadding");
dec.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
// Decrypt your data
...

Java CBC decrypting works, but CTR is failing

I setup a program for a class I am taking on crypto. I will follow this with my code and another section for my variable differences. My goal is to decrypt the text for our homework. I do not want someone to decrypt this for me, but would like some help as to what is causing this within my code. When I decrypt CBC I get the correct output with no problem, though it does have some extra chars in it (this may be an issue with padding? I am not sure)
Then when I use the CTR with the correct changes it returns a bunch of garbage. Any help would be greatly appreciated.
Thank you,
CBC:
CBC key: 140b41b22a29beb4061bda66b6747e14
CBC Ciphertext 1:
4ca00ff4c898d61e1edbf1800618fb2828a226d160dad07883d04e008a7897ee2e4b7465d5290d0c0e6c6822236e1daafb94ffe0c5da05d9476be028ad7c1d81
CTR:
CTR key: 36f18357be4dbd77f050515c73fcf9f2
CTR Ciphertext 1:
69dda8455c7dd4254bf353b773304eec0ec7702330098ce7f7520d1cbbb20fc388d1b0adb5054dbd7370849dbf0b88d393f252e764f1f5f7ad97ef79d59ce29f5f51eeca32eabedd9afa9329
CBC Variables
String algorithm = "AES";
String mode = "CBC";
String padding = "PKCS5Padding";
byte[] ciphertextBytes = StringToByte("4ca00ff4c898d61e1edbf1800618fb2828a226d160dad07883d04e008a7897ee2e4b7465d5290d0c0e6c6822236e1daafb94ffe0c5da05d9476be028ad7c1d81");
byte[] keyBytes = StringToByte("140b41b22a29beb4061bda66b6747e14");
CTR Variables
String algorithm = "AES";
String mode = "CTR";
String padding = "NoPadding";
byte[] ciphertextBytes = StringToByte("770b80259ec33beb2561358a9f2dc617e46218c0a53cbeca695ae45faa8952aa0e311bde9d4e01726d3184c34451");
byte[] keyBytes = StringToByte("36f18357be4dbd77f050515c73fcf9f2");
Decrypt Main
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import static java.lang.Character.digit;
public class CryptoClass {
public static void main(String[] args) throws Exception {
byte[] decryptByte = Decrypt();
String hexString = ByteToHex(decryptByte);
StringBuilder decryptedString = HexToString(hexString);
System.out.println(decryptedString);
}
public static byte[] Decrypt() throws Exception {
//
String algorithm = "AES";
String mode = "CTR";
String padding = "NoPadding";
byte[] ciphertextBytes = StringToByte("770b80259ec33beb2561358a9f2dc617e46218c0a53cbeca695ae45faa8952aa0e311bde9d4e01726d3184c34451");
byte[] keyBytes = StringToByte("36f18357be4dbd77f050515c73fcf9f2");
IvParameterSpec ivParamSpec = null;
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.nextBytes(iv);
ivParamSpec = new IvParameterSpec(iv);
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "JsafeJCE");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParamSpec);
byte[] result = cipher.doFinal(ciphertextBytes);
return result;
}
//convert ByteArray to Hex String
public static String ByteToHex(byte[] byteArray) {
StringBuilder sb = new StringBuilder();
for (byte b : byteArray)
{
sb.append(String.format("%02X", b));
}
return sb.toString();
}
//convert String to ByteArray
private static byte[] StringToByte(String input) {
int length = input.length();
byte[] output = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
output[i / 2] = (byte) ((digit(input.charAt(i), 16) << 4) | digit(input.charAt(i+1), 16));
}
return output;
}
//changes a hex string into plain text
public static StringBuilder HexToString(String hex) throws Exception {
StringBuilder output = new StringBuilder();
for (int i = 0; i < hex.length(); i+=2) {
String str = hex.substring(i, i+2);
output.append((char)Integer.parseInt(str, 16));
}
return output;
}
}
*Edit method for solution - instead of a random IV I pulled the IV from the first 16 bits of the ciphertext. In the assignment it stated that this was the case, for some reason I glossed over it when I looked through it the first time.
public static byte[] Decrypt() throws Exception {
String algorithm = "AES";
String mode = "CTR";
String padding = "NoPadding";
byte[] ciphertextBytes = StringToByte("0ec7702330098ce7f7520d1cbbb20fc388d1b0adb5054dbd7370849dbf0b88d393f252e764f1f5f7ad97ef79d59ce29f5f51eeca32eabedd9afa9329");
byte[] keyBytes = StringToByte("36f18357be4dbd77f050515c73fcf9f2");
//int ivSize = 16;
//byte[] iv = new byte[ivSize];
//SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
//secureRandom.nextBytes(iv);
byte[] ivParamSpecTMP = StringToByte("69dda8455c7dd4254bf353b773304eec");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivParamSpecTMP);
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "JsafeJCE");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
byte[] result = cipher.doFinal(ciphertextBytes);
return result;
The trick is that you must send the IV (in plain text) to the receiver. If you randomly generate the IV before decryption you will get garbage by definition. Random IV's should only be generated before encryption.
Standard practice is for the sender to prefix the IV to the ciphertext. The receiver uses the first 16 bytes as an IV and the rest as the actual ciphertext.

CryptoJS AES encryption and JAVA AES decryption value mismatch

I am encrypting a text using CryptoJS AES algorithm on the client side and I am decrypting It on Server side in java, I am getting the exception.
JS code :
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
console.info("encrypted " + encrypted);
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
var plainText = decrypted.toString(CryptoJS.enc.Utf8)
console.info("decrypted " + plainText);
js output :
encrypted U2FsdGVkX1/uYgVsNZmpbgKQJ8KD+8R8yyYn5+irhoI=
decrypted Message
Java Code :
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.regex.Pattern;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class AESJavaScript {
private SecretKeySpec key;
private Cipher cipher;
private int size = 128;
private static final Charset CHARSET = Charset.forName("UTF-8");
public AESJavaScript() throws NoSuchAlgorithmException,
NoSuchPaddingException, NoSuchProviderException {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(size); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
key = new SecretKeySpec(raw, "AES/CTR/NoPadding");
cipher = Cipher.getInstance("AES/CTR/NoPadding");
}
public void setKey(String keyText) {
byte[] bText = new byte[size];
bText = keyText.getBytes(CHARSET);
key = new SecretKeySpec(bText, "AES/CTR/NoPadding");
}
public String encrypt(String message) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(message.getBytes());
return byteArrayToHexString(encrypted);
}
public String decrypt(String hexCiphertext)
throws IllegalBlockSizeException, BadPaddingException,
InvalidKeyException {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(hexStringToByteArray(hexCiphertext));
return byteArrayToHexString(decrypted);
}
private static String byteArrayToHexString(byte[] raw) {
String hex = "0x";
String s = new String(raw);
for (int x = 0; x < s.length(); x++) {
char[] t = s.substring(x, x + 1).toCharArray();
hex += Integer.toHexString((int) t[0]).toUpperCase();
}
return hex;
}
private static byte[] hexStringToByteArray(String hex) {
Pattern replace = Pattern.compile("^0x");
String s = replace.matcher(hex).replaceAll("");
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
public static void main(String[] args) {
try {
AESJavaScript ajs = new AESJavaScript();
ajs.setKey("Secret Passphrase");
String hexCiphertext = "U2FsdGVkX1/uYgVsNZmpbgKQJ8KD+8R8yyYn5+irhoI=";
String decrypted = ajs.decrypt(hexCiphertext);
System.out.println("decrypted > " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Exception is :
java.security.InvalidKeyException: Invalid AES key length: 17 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:372)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1052)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1010)
at javax.crypto.Cipher.implInit(Cipher.java:786)
at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
at javax.crypto.Cipher.init(Cipher.java:1213)
at javax.crypto.Cipher.init(Cipher.java:1153)
at com.test.jenkins.jenkinsRestart.AESJavaScript.decrypt(AESJavaScript.java:49)
at com.test.jenkins.jenkinsRestart.AESJavaScript.main(AESJavaScript.java:82)
Is there anything that I am doing wrong here or Is there anyother simple way to do these kind of encryption and decryption ?
Your Java code is riddled with errors. Here are a few of them:
Your initial exception is caused because your "Secret Passphrase" string contains 17 bytes. You need to truncate this to 16 bytes (or pad it to match 24 or 32 bytes). You need to match the behaviour of the CryptoJS library.
You are trying to hex-decode data that appears to be base64 encoded. Try using DatatypeConverter.parseBase64Binary(hex);.
You are creating a secret key with the algorithm "AES/CTR/NoPadding". This in invalid, just use "AES".
You must pass an IV/nonce value into your decryption:
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(...));
The value you use will depend upon what CryptoJS is doing. Perhaps it uses all zeroes? Perhaps it generates a random one and you need to store it with the ciphertext?
This should be enough to get you started.

Categories

Resources