Decode data using openssl that has been encoded using java - java

I have to interface with a system written in java that encrypts data using the following java method:
public final void rsaEncrypt(String data, String filePath) {
try {
Base64.Encoder encoder = Base64.getEncoder();
PublicKey pubKey = readKeyFromFile("/" + Constants.PUBLIC_KEY_FILE_NAME, filePath);
Cipher cipher = Cipher.getInstance(Constants.RSA_INSTANCE);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherData = cipher.doFinal(data.getBytes("UTF-8"));
writeToFile(Constants.ENCRYPTED_STRING_FILE_NAME, filePath, encoder.encodeToString(cipherData));
} catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | NoSuchAlgorithmException
| IllegalBlockSizeException | UnsupportedEncodingException e) {
if (LOG.isErrorEnabled())
LOG.error("Error encrypting String.", e);
throw new EncryptionException("Error encrypting data", e);
}
}
My code is written in c++ using openssl:
std::string prv =
"-----BEGIN RSA PRIVATE KEY-----\n"
// cut key data
"-----END RSA PRIVATE KEY-----\n";
BIO *bio = BIO_new_mem_buf((void*)prv.c_str(), -1);
RSA* rsaPrivKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
if (!rsaPrivKey)
printf("ERROR: Could not load PRIVATE KEY! PEM_read_bio_RSAPrivateKey FAILED: %s\n", ERR_error_string(ERR_get_error(), NULL));
BIO_free(bio);
// where enc[] holds the Base64 decoded data, but len becomes -1 and no data is decoded.
int len = RSA_private_decrypt(64,(unsigned char *)&enc, (unsigned char *)&dec, private_key_, RSA_PKCS1_PADDING);
I can decode data I encrypt myself with the public key, but don't seem to match the java options for this.
Any suggestions?

These is no answer here as like my comment said, it turns out the Java code was incorrect in creating their keys, which were not matching the .pem files I was given.

Related

AES secret key lost on application start

I am using AES encryption and have encrypted a message with a key generated from key generator. Encryption and Decryption is working as expected.
But when the application is restarted and I am trying to decrypt the same message again, it is giving error as the secret key is not stored anywhere which was used to encrypt the message and on restart of application we don't have that secret key anymore.
Cannot invoke javax.crypto.Cipher.getIV() because AES.encryptionCipher is null.
I don't want to store the secret key in database. Can I store the secretKey and write it in a file and place that file in src/main/resources folder.Will it be a good approach?
Is there any other place where I could store the secret key and at the time of decrypting the meesages I could load the key from that place and decrypt messages.
This is what I have done so far.
private static SecretKey key;
private static int KEY_SIZE = 128;
private static int T_LEN = 128;
private static Cipher encryptionCipher;
public static String encrypt(String message) {
byte[] messageInBytes = message.getBytes();
byte[] encryptedBytes = null;
try {
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(KEY_SIZE);
key = generator.generateKey();
encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
encryptedBytes = encryptionCipher.doFinal(messageInBytes);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
return encode(encryptedBytes);
}
public static String decrypt(String encryptedMessage) {
byte[] messageInBytes = decode(encryptedMessage);
byte[] decryptedBytes = null;
try {
Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(T_LEN, encryptionCipher.getIV());
decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
decryptedBytes = decryptionCipher.doFinal(messageInBytes);
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
return new String(decryptedBytes);
}
private static String encode(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
private static byte[] decode(String data) {
return Base64.getDecoder().decode(data);
}
I had similar situation, still have some open part. Suggested solution solve some, but still there are some open question, hopefully we can have common approach with expert inputs.
Keytool to generate Secret Key
keytool -genseckey -alias aeskey -keyalg AES -keysize 128 -keypass password -keystore project.keystore -storetype pkcs12
refer the Oracle documentation for details.
Java provide standard Java keystore(JKS), Secret key can be stored in jks file before starting application and this file can be read again on restart.
JKS also need password to access, in this approach, challenge is how to keep java keystore password securely
my suggestion is keystore password can be saved securely in new platform like kubernetes ( any other suggestion?)
Reference source code to retrieve Secret Key from Java Keystore:
KeyStore ks = KeyStore.getInstance("pkcs12");
String keyStorePassword = appSecurityConfig.getJksPass(); //get keystore password from external configuration, like kubernetes secret
//appSecurityConfig is configuration class
char [] password = keyStorePassword.toCharArray();
ks.load(new FileInputStream(appSecurityConfig.getKeystoreFile()), password); //keystoreFile -> JKS file name
SecretKey secretKey = (SecretKey) ks.getKey("kskey", password); //kskey -> is secreykey alias

Why do I get "BadPaddingException: Message is larger than modulus" when decrypting data stored as a bit array from text file?

I'm using the RSA algorithm to encrypt some numbers, then store those numbers in a bit array within a text file. The numbers are encrypted and added to the text file just fine, but when I try and decrypt them using the method below, I get "BadPaddingException: Message is larger than modulus" this error in my console. I'm pretty new to encryption and this is really throwing me off.
Cipher cipher;
KeyPair pair;
public byte[] encryptData(String data) throws BadPaddingException, IllegalBlockSizeException {
try{
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
pair = keyPairGen.generateKeyPair();
PublicKey publicKey = pair.getPublic();
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipher.update(data.getBytes());
cipher.doFinal();
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException ex){
ex.printStackTrace();
}
return cipher.doFinal();
}
writing data into a file
String nums = request.getParameter("numbers");
String password = (String) session.getAttribute("password");
String filename = (password +".txt");
File dir = new File("/volume");
dir.mkdir();
File myfile = new File(dir, filename);
System.out.println("filename: " + filename);
FileOutputStream output;
try {
if (myfile.isFile()){
output = new FileOutputStream(myfile, true);
}
else{
output = new FileOutputStream(myfile);
}
byte[] encryptednums = encryptData(nums);
output.write(encryptednums);
output.close();
} catch (BadPaddingException | IllegalBlockSizeException | IOException e) {
e.printStackTrace();
}
Reading the file
public byte[] bytesFileReader(File filename){
try {
return Files.readAllBytes(Paths.get(String.valueOf(filename)));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Decryption
HttpSession session = request.getSession();
KeyPair pair = (KeyPair) session.getAttribute("keypair");
Cipher cipher = (Cipher) session.getAttribute("cipher");
String password = (String) session.getAttribute("password");
String filename = (password +".txt");
File dir = new File("/volume");
dir.mkdir();
File myfile = new File(dir, filename);
byte[] encryptedBytes = bytesFileReader(myfile);
try {
cipher.init(Cipher.DECRYPT_MODE, pair.getPrivate());
byte[] decipheredText = cipher.doFinal(encryptedBytes);
System.out.println(new String(decipheredText, StandardCharsets.UTF_8));
} catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
}
Probably worth noting that this is all taking place across two java servlets.
There is one main issue and some minor issues in your code. Let's start will a minor one: when converting a string to a byte array and vice versa all conversions have to use a fixed encoding (see your encryptData method):
cipher.update(data.getBytes());
The main issue is the plaintext size limitation when doing RSA encryption. You can calculate the maximum plaintext size
with this formula
1) RSA keysize divided by 8 - e.g. 2048 / 8 = 256 bytes
2) PKCS1 padding takes 11 bytes
3) maximum of plaintext bytes: 256 - 11 = 245 bytes
In my example code I'm using a RSA keypair with size of (UNSECURE) 512 bits = 64 bytes, minus 11 bytes for padding there are 53 bytes that can get encrypted. The first round will run like expected, the next encryption with 54 bytes of data runs into the
IllegalBlockSizeException: Data must not be longer than 53 bytes
As your code has only the methods but does not show what data is written I can only assume that you try to decrypt all written data in one run - there is a high chance for failure.
The reason why my code runs into the error (and yours not) is the update- and double (!) final-call (beware I left out the catching that you maybe need):
change:
cipher.update(data.getBytes());
cipher.doFinal();
return cipher.doFinal();
to:
return cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
output:
BadPaddingException: Message is larger than modulus
encrypting 12345678901234567890123456789012345678901234567890123
encryptedData length: 64
decryptedData: 12345678901234567890123456789012345678901234567890123
encrypting 123456789012345678901234567890123456789012345678901234
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 53 bytes
Security warning: this code uses an UNSECURE 512 bit long RSA key (use minimum 2048 bit) and encrypts with RSA padding PKCS1 that is vulnerable - please use OEAP padding!.
code:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
public class Main {
static KeyPair keyPair;
public static void main(String[] args) throws NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException {
System.out.println("BadPaddingException: Message is larger than modulus");
// https://stackoverflow.com/questions/64722370/why-do-i-get-badpaddingexception-message-is-larger-than-modulus-when-decrypti
keyPair = generateKeyPair(512); // ### do not use RSA keys with 512 bits length, minimum 2048 bits
// 512 bit = 64 byte - 11 byte for padding = maximal 53 bytes data to encrypt
System.out.println("\nencrypting 53 chars");
String data53Chars = "12345678901234567890123456789012345678901234567890123";
//String data53Chars = "12345678901234567890";
System.out.println("encrypting " + data53Chars);
byte[] encryptedData = encryptData(data53Chars);
System.out.println("encryptedData length: " + encryptedData.length);
String decryptedData = decryptData(encryptedData);
System.out.println("decryptedData: " + decryptedData);
// now 54 bytes
System.out.println("\nencrypting 54 chars");
String data54Chars = "123456789012345678901234567890123456789012345678901234";
System.out.println("encrypting " + data54Chars);
encryptedData = encryptData(data54Chars);
System.out.println("encryptedData length: " + encryptedData.length);
decryptedData = decryptData(encryptedData);
System.out.println("decryptedData: " + decryptedData);
}
public static byte[] encryptData(String data) throws BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
PublicKey publicKey = keyPair.getPublic();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
}
public static String decryptData(byte[] encryptedBytes) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException {
byte[] decipheredText = new byte[0];
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
decipheredText = cipher.doFinal(encryptedBytes);
return new String(decipheredText, StandardCharsets.UTF_8);
}
static KeyPair generateKeyPair(int keyLength) throws NoSuchAlgorithmException {
final String KEY_ALGORITHM = "RSA";
KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keygen.initialize(keyLength, new SecureRandom());
KeyPair keyPair = keygen.generateKeyPair();
return keyPair;
}
}

AES-Encryption result of C# code is not same as of Java AES-Encryption

I've following aes encryption code in Java which I want to write it in C#, but it is not giving same output.
Java Code
public String doEncryptString(String salt, String password,String token) throws CryptoException {
try {
Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec secretKeySpec = generateKeySpec(salt,password);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] inputBytes = token.getBytes();
byte[] outputBytes = cipher.doFinal(inputBytes);
return Base64Utils.encodeToString(outputBytes);
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException
| IllegalBlockSizeException ex) {
throw new CryptoException("Error encrypting password", ex);
}
}
private SecretKeySpec generateKeySpec(String salt,String password) throws CryptoException{
try {
String generatedkey=salt+password;
byte[] key = generatedkey.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
return secretKeySpec;
} catch (NoSuchAlgorithmException | IOException ex) {
throw new CryptoException("Error encrypting password", ex);
}
}
This is what I've tried in C#
public static string DoEncrypt(string salt, string password, string token)
{
var tdes = new AesManaged();
tdes.Key = GenerateKey(salt, password);
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
ICryptoTransform crypt = tdes.CreateEncryptor();
byte[] plain = Encoding.UTF8.GetBytes(token);
byte[] cipher = crypt.TransformFinalBlock(plain, 0, plain.Length);
return Convert.ToBase64String(cipher);
}
private static byte[] GenerateKey(string salt, string password)
{
string generatedkey = $"{salt}{password}";
var key = Encoding.UTF8.GetBytes(generatedkey);
var sha1 = SHA1Managed.Create();
key = sha1.ComputeHash(key);
return key.Take(16).ToArray(); // use only first 128 bit
}
string/token to encrypt : ZHKRIWB310XVVWG315PI7UZZWU1V0YYL5WE9JL
Java output: eUjNH8kcgWtlEmuCFHMPwnCFWjy5Pye/gF+itrPs1g8AjtAEZQqlzW/v7kEt2haG
My C# code output: O8sKdJWH+XCOIbexZPEwN5NxWqpWRHC5b3ZsihT8cfBqpI1eVr3PEr9Eq39a5pMn
I don't know what I am doing wrong here. Any help would be appreciated. Thanks
Update
My apologies everyone. The code translated in C# in working fine. By mistake, I was passing different salt value. Thanks everyone.
What's in TRANSFORMATION from the Java code?
You need also to use the same mode and padding to get the same results, meaning ECB and PKCS7 in your case.
Java seems to offer only PKCS5 padding? But it seems to be compatible with PKCS7? I'm not a Java dev and can't provide details, but there is a discussion here: https://crypto.stackexchange.com/questions/9043/what-is-the-difference-between-pkcs5-padding-and-pkcs7-padding where they say:
Some cryptographic libraries such as the SUN provider in Java indicate
PKCS#5 where PKCS#7 should be used - "PKCS5Padding" should have been
"PKCS7Padding". This is - with high probability - a legacy from the
time that only 8 byte block ciphers such as (triple) DES symmetric
cipher were available.
And by the way: for production never use ECB mode as it's unsafe.

AES/EAX Crypto with JCE - Mac check in EAX failed

I am trying to perform encryption/decryption using AES/EAX/NoPadding. Since EAX doesn't appear to be available without BouncyCastle, BC has been added as a provider.
When I try to encrypt "Hello World!", it appears to have encrypted successfully.
#NotNull
#Override
public byte[] encrypt(#NotNull Key key, #NotNull byte[] plain, #Nullable byte[] authentication) throws CryptoException {
try {
final AesEaxKey aesEaxKey = (AesEaxKey) key;
final Cipher cipher = Cipher.getInstance(getCipherAlgorithm(), BouncyCastleProvider.PROVIDER_NAME);
final byte[] cipherText = new byte[getIvSize(aesEaxKey) + plain.length + getTagSize()];
final byte[] iv = randomIv(aesEaxKey);
System.arraycopy(iv, 0, cipherText, 0, getIvSize(aesEaxKey));
cipher.init(Cipher.ENCRYPT_MODE, aesEaxKey, getParameterSpec(iv));
if (authentication != null && authentication.length != 0) {
cipher.updateAAD(authentication);
}
cipher.doFinal(plain, 0, plain.length, cipherText, getIvSize(aesEaxKey));
return cipherText;
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException |
InvalidKeyException | BadPaddingException | IllegalBlockSizeException | ShortBufferException e) {
throw new CryptoException(e.getMessage(), e);
}
}
When I try to decrypt the ciphertext, it throws "Mac check in EAX failed".
#NotNull
#Override
public byte[] decrypt(#NotNull Key key, #NotNull byte[] cipherText, #Nullable byte[] authentication) throws CryptoException {
try {
final AesEaxKey aesEaxKey = (AesEaxKey) key;
final Cipher cipher = Cipher.getInstance(getCipherAlgorithm(), BouncyCastleProvider.PROVIDER_NAME);
cipher.init(Cipher.DECRYPT_MODE, aesEaxKey, getParameterSpec(cipherText, 0, getIvSize(aesEaxKey)));
if (authentication != null && authentication.length != 0) {
cipher.updateAAD(authentication);
}
return cipher.doFinal(cipherText, getIvSize(aesEaxKey), cipherText.length - getIvSize(aesEaxKey));
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException |
InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new CryptoException(e.getMessage(), e);
}
}
Details:
getParameterSpec() returns an instance of IvParameterSpec with the IV.
During encryption, the IV bytes are inserted into the beginning of the ciphertext byte[] and retrieved from the ciphertext during decryption.
The tag size being used is 16 bytes.
AesEaxKey is simply a wrapper class that implements SecretKey and delegates all of its methods.
I have an implementation of AES/GCM/NoPadding that uses this same exact code which works perfectly.
What am I doing wrong?
AEAD modes such as EAX require a more complicated AlgorithmParameterSpec because both the nonce (aka IV) and the tag length in bits must be specified. Java since 1.7 has provided a GCMParameterSpec for GCM ciphers. The same data is needed for EAX mode, and apparently the Bouncycastle provider will use a GCMParameterSpec for EAX mode as well.
Note that, for the GCMParameterSpec, the tag length is specified in bits, while for the purposes of sizing arrays the tag length needs to be specified in bytes.

RSA/ECB/NoPadding decryption returning null characters

I am encrypting a string using RSA algorithm and encryption and decryption logic is
public class RsaEncrypt {
private static final String ALGORITHM = "RSA";
public static void main(String[] args) {
String filePath = "/home/Desktop/abc.jks";
char[] password = "changeit".toCharArray();
String alias = "123";
KeyStore ks = null;
try {
//loading the keystore
ks = KeyStore.getInstance("JKS");
InputStream readStream = new FileInputStream(filePath);
ks.load(readStream, password);
Certificate cert = ks.getCertificate(alias);
PublicKey publicKey = cert.getPublicKey();
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password);
RsaEncrypt e = new RsaEncrypt();
String result = e.encrypt("abvhdh", publicKey);
String decryptResult = e.decrypt(result.getBytes(), privateKey);
} catch (Exception e) {
e.printStackTrace();
}
}
//Encryption of a string
public String encrypt(String text,PublicKey publicKey) {
String retVal = null;
byte[] cipherText = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
cipherText = Base64.getEncoder().encode(cipherText);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return new String(cipherText) ;
}
// Decryption of a string
private String decrypt(byte[] text, PrivateKey privatekey) {
byte[] dectyptedText = null;
try {
final Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privatekey);
dectyptedText = cipher.doFinal(Base64.getDecoder().decode(text));
} catch (Exception ex) {
ex.printStackTrace();
} catch (Throwable e) {
throw new RuntimeException(e);
}
return new String(dectyptedText);
}
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value.
But when i decrypt,The reuslt string contains some null characters
Example
input : abcd output : abcd \u0000 \u0000 \u0000 \u0000....
How can i resolve this problem and what is the best way to get same encryption value if we encrypt multiple times?????
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
That is correct and it is even required property of the RSA encryption. By default PKCS1.5 padding is used (RSA/ECB/PKCS1Padding) which contains some random bytes. Safer option is RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING mode which is even more random.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value. But when i decrypt,The reuslt string contains some null characters
Using RSA without padding (NoPadding) is very unsafe (it is called textbook RSA).
Padding extends the original encrypted value to full space length (e.g. 2048 bits) and then the RSA magic (exponentiation) will be executed. Using the NoPadding parameter you are telling the crypto library that you will do the padding yourself. In that case you are expected to remove the padding after decryption (in your case zero padding)
I hope you are doing that for learning / academic purposes, not some real security project. You may have a look at my blog about encryption to get some examples.
btw: you should not use RSA to encrypt the plaintext itself. Rather use symmetric encryption to encrypt the plaintext and then RSA to encrypt the symmetric encryption key.

Categories

Resources