Blowfish encrypt in Java/Scala and decrypt in bash - java

I'm trying to build a tool to decrypt content in bash encrypted in a scala application:
But first, I've to succeed encoding the same message in both languages and make them equal:
Given the passphrase "0123456789abcdef"(hex: "30313233343536373839616263646566" and byte[]:[48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102])
scala> import javax.crypto.Cipher
scala> import javax.crypto.spec.SecretKeySpec
scala> val cipher = Cipher.getInstance("Blowfish")
scala> cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("0123456789abcdef".getBytes("utf-8"), "Blowfish"))
scala> javax.xml.bind.DatatypeConverter.printBase64Binary(cipher.doFinal("message".getBytes("utf-8")))
res7: String = K679Jz06jmc=
But I can't reproduce the same with openssl in bash.
$ echo "message" | openssl enc -a -e -blowfish -nosalt -nopad -k "0123456789abcdef"
LJ3iFJ2/mYk=
$ echo "message" | openssl enc -a -e -blowfish -nosalt -nopad -k "30313233343536373839616263646566"
JkkJgYv3fQg=
Any hint?
Thanks!

First of all, if you want to have the same ciphertexts you have to ensure that Scala and OpenSSL encryption use the same deterministic encryption algortithms with the same input parameters.
Since OpenSSL uses CBC as the default mode, we do the same in Scala.
import javax.crypto.Cipher
import javax.crypto.spec._
import javax.xml.bind.DatatypeConverter
val cipher = Cipher.getInstance("Blowfish/CBC/NoPadding")
val key = new SecretKeySpec(DatatypeConverter.parseHexBinary("0123456789ABCDEF0123456789ABCDEF"), "Blowfish")
val specIv = new IvParameterSpec(DatatypeConverter.parseHexBinary("0000000000000000"))
cipher.init(Cipher.ENCRYPT_MODE, key, specIv)
val enc = cipher.doFinal("messages".getBytes("UTF-8"))
println(DatatypeConverter.printBase64Binary(enc))
Note that without padding the input length must be a multiple of the Blowfish block length of 64 bits.
With OpenSSL you have to explicitely specify the same key and initialization vector (IV)
printf %s "messages" | openssl enc -e -blowfish -nopad -K "0123456789ABCDEF0123456789ABCDEF" -iv "0000000000000000" -base64
With the above example you will get JSj0k4FwtG8= as ciphertext in both cases.

It would be more accurate to tag OpenSSL, which runs the same from any shell or no shell at all.
OpenSSL enc by default does password-based encryption, with a key (plus IV) derivation similar to (but not exactly) PBKDF1. To encrypt with a key, not password, you need to use UPPERCASE -K with hex, and you need to specify at least part of -iv; on the other hand -nosalt is useless.
Also the echo command adds a newline to the data it echoes, and encryption of 'm e s s a g e NL' is entirely different from encryption of 'm e s s a g e'. Some OSes and shells, depending on whether the echo you are using is builtin or external, have ways to omit the newline, but they aren't all the same. OTOH all POSIX systems support printf, so:
$ printf %s message | openssl enc -blowfish -K 30313233343536373839616263646566 -iv 00 -a
K679Jz06jmc=
EDIT: Artjom is correct; Java/Scala (probably) defaults to ECB, which is a Bad Idea; you should specify /CBC or /CTR and padding. Unlike aventurin I see no reason you should force yourself into the straightjacket of /NoPadding; Java /PKCS5Padding is compatible with OpenSSL's default. For CBC or CTR you need to explicitly set the IV (with the third argument to Cipher.init) to get a deterministic result -- and you must either set the IV, or retrieve the default random one, to produce a decryptable result, which people usually want. OpenSSL enc -blowfish defaults to CBC, but it's clearer to explicitly state -bf-cbc or -bf-ctr. Or -bf-ecb if you really want ECB, which as above is Unrecommended.

Related

Decrypt file in java that was encrypted with openssl

The file is encrypted using the following command:
openssl enc -aes-256-cbc -in file.txt -out file_enc.txt -k 1234567812345678
The file is decrypted using the following command:
openssl enc -d -aes-256-cbc -in file_enc.txt -out file.txt -k 1234567812345678
After printing the salt and key in java I get:
Key=b796fbb416732ce13d39dbb60c0fb234a8f6d70e49df1c7e62e55e81d33a6bff774254ac99268856bf3afe0b95defdad
and in cmd I get :
salt=2D7C7E1C84BD6693
key=B796FBB416732CE13D39DBB60C0FB234A8F6D70E49DF1C7E62E55E81D33A6BFF
iv =774254AC99268856BF3AFE0B95DEFDAD
after running :
openssl enc -aes-256-cbc -in file.txt -out file_enc.txt -pbkdf2 -k 1234567812345678 -p
I am using the following code but the encrypted file is printing :
public static void main(String args[]) throws InvalidKeySpecException,
NoSuchAlgorithmException,
IllegalBlockSizeException,
InvalidKeyException,
BadPaddingException,
InvalidAlgorithmParameterException,
NoSuchPaddingException,
IOException {
String password = "1234567812345678";
String algorithm = "AES/CBC/PKCS5Padding";
IvParameterSpec ivParameterSpec = AESUtil.generateIv();
Resource resource = new ClassPathResource("file_enc.txt");
File inputFile = resource.getFile();
byte[] salt = new byte[8], data = new byte[1024], tmp;
int keylen = 32, ivlen = 16, cnt;
try( InputStream is = new FileInputStream(inputFile) ){
if( is.read(salt) != 8 || !Arrays.equals(salt, "Salted__".getBytes() )
|| is.read(salt) != 8 ) throw new Exception("salt fail");
byte[] keyandIV = SecretKeyFactory.getInstance("PBKDF2withHmacSHA256")
.generateSecret( new PBEKeySpec(password.toCharArray(), salt, 10000, (keylen+ivlen)*8)
).getEncoded();
System.out.println("Key "+ byteArrayToHex(keyandIV));
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyandIV,0,keylen,"AES"),
new IvParameterSpec(keyandIV,keylen,ivlen));
while( (cnt = is.read(data)) > 0 ){
if( (tmp = ciph.update(data, 0, cnt)) != null ) System.out.write(tmp);
}
tmp = ciph.doFinal(); System.out.write(tmp);
}
}
Your getKeyFromPassword creates a SecretkeyFactory for PBKDF2 (with HmacSHA256) but doesn't use it; instead you use the password as the key, which is wrong -- unless you actually wanted to do openssl enc with a key (-K uppercase and hex which should be 64 hexits/32 bytes for AES-256) and not a password (-k lowercase and any characters or any length). (Below IIRC 18, String.getBytes() gives you a JVM-dependent encoding which for an arbitrary string like a real password can vary on different systems or environments, which will cause cryptography using it to fail, but for the example string you show all realistic encodings give the same result.)
But even if you did use this factory it wouldn't be correct because openssl enc by default does not use PBKDF2, it uses a function called EVP_BytesToKey which is based on but different from PBKDF1. In OpenSSL 1.1.1 and 3.0 only, you can optionally specify -pbkdf2 (and/or -iter N) in enc, and if you don't it gives a warning message about 'deprecated key derivation' which you should have noticed, so I assume you're either using obsolete OpenSSL or trying to prevent us accurately understanding your situation and thus making it unlikely to get a useful answer.
Also, with either kind of key derivation (old EVP_BytesToKey or optional new PBKDF2) openssl enc by default uses salt, which is randomly generated and written in the file when encrypting; when decrypting you must read the salt from the file and use it. Specifically, skip or discard the first 8 bytes (which are the fixed characters Salted__) and take the next 8 bytes as salt, then the remainder of the file as ciphertext
Depending on what you want (or maybe your users/customers/etc want) to do there are three possibilities:
encrypt with openssl enc -aes-256-cbc -k ... with the default derivation (as now) and code the Java to read the salt from the file as above, implement EVP_BytesToKey using the password and that salt, and use its output for both the key and IV in the Java Cipher (for aes-256-cbc generate 48 bytes and use the first 32 bytes as key and the last 16 bytes as IV). EVP_BytesToKey uses a hash which defaults to SHA256 for OpenSSL 1.1.0 up and MD5 for lower versions, so you need to know which version did the encryption for this to work, or else you can specify the hash on the enc command with -md $hash. There have been hundreds of Qs about this going back over a decade; search for EVP_BytesToKey to find some of them.
encrypt with openssl enc -aes-256-cbc -pbkdf2 -k ... and code the Java to read the salt from the file as above and use the keyfactory you created to generate 48 bytes of 'key' material, which you must actually split into key and IV as above in the Java Cipher.
encrypt with openssl enc -aes-256-cbc -K 64hexits -iv 32hexits and code the Java to use the corresponding binary key and IV values.
In command i didn't specify neither random IV nor PKCS5Padding
When you use either old or new key derivation in openssl enc it derives the IV rather than specifying it separately; only if you use explicit key (-K uppercase) do you also specify -iv. openssl enc always defaults to the padding variously called pkcs5, pkcs7, or pkcs5/7, except when no padding is needed (stream ciphers like RC4 or ChaCha or stream modes like CTR, OFB, CFB).
Okay, you seem to be reading only about half of what I said. Most fundamentally, you still have openssl enc without -pbkdf2 but are trying to decrypt in Java with PBKDF2, which is flat wrong. In addition you are reading the salt but then converting it to hex, which is wrong, the salt from the file is the correct salt, and you are generating a completely bogus random IV, not deriving it as I said.
To be concrete, if you (or I) encrypt a file with -pbkdf2 like
openssl enc -aes-cbc-256 -pbkdf2 -k 1234567812345678
which will only work on OpenSSL 1.1.1 or 3.0 (i.e. since 2018), the following (minimalistic) Java code correctly decrypts it:
static void SO73456313OpensslEnc2_Java (String[] args) throws Exception {
// file pw: decrypt openssl(1.1.1+) enc -aes-256-cbc -pbkdf2 -k $pw
byte[] salt = new byte[8], data = new byte[1024], tmp;
int keylen = 32, ivlen = 16, cnt;
try( InputStream is = new FileInputStream(args[0]) ){
if( is.read(salt) != 8 || !Arrays.equals(salt, "Salted__".getBytes() )
|| is.read(salt) != 8 ) throw new Exception("salt fail");
byte[] keyandIV = SecretKeyFactory.getInstance("PBKDF2withHmacSHA256")
.generateSecret( new PBEKeySpec(args[1].toCharArray(), salt, 10000, (keylen+ivlen)*8)
).getEncoded();
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyandIV,0,keylen,"AES"),
new IvParameterSpec(keyandIV,keylen,ivlen));
while( (cnt = is.read(data)) > 0 ){
if( (tmp = ciph.update(data, 0, cnt)) != null ) System.out.write(tmp);
}
tmp = ciph.doFinal(); System.out.write(tmp);
}
}
Note in PBEKeySpec I used itercount=10000 which is the enc default. You can use a higher number like 65536, which may be desirable for security (but that part is offtopic here), if you specify it when encrypting like:
openssl enc -aes-256-cbc -pbkdf2 -iter 65536 -k ...
OTOH if you use the command you posted, which you must on OpenSSL 1.1.0 or lower, then you cannot decrypt using PBKDF2 at all.
For that case instead see
How to decrypt file in Java encrypted with openssl command using AES?
How to decode a string encoded with openssl aes-128-cbc using java?
Java equivalent of an OpenSSL AES CBC encryption
Java AES Decryption with keyFile using BouncyCastle SSL
and CryptoJS AES encryption and Java AES decryption (cryptojs is sometimes compatible with OpenSSL, including the case in that Q).
And remember, as noted in at least some of those earlier Qs, the command you posted uses EVP_BytesToKey with SHA256 in 1.1.0 up but MD5 in 1.0.2 and lower, so you need to know which OpenSSL was or will be used.

Decoding - from Java to Powershell

I have inherited a self-made certification solution based on Java and Microsoft SQL server. We are moving forward to a Venafi solution. The old certificate has to be moved from the old solution to a Venafi pki solution. The designer of the old solution is not here anymore, but I have the decryption part in Java, including the decryption key. I have absolutely no experience in Java, and very limited cryptographic experience in Powershell.
The Java code to decrypt is this:
SecretKeySpec key = new SecretKeySpec(Base64.decode(encryptionkey.getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] original = cipher.doFinal(encrypted);
The password is something like: gTsLrRTFR5Q0cvQZFRuZPw== (Not the actual password) and the certificates in encrypted format is basic hex data like 0x5F4E23E1 ... all in 1024 sizes.
How would I decrypt these certificates in powershell?
I'm not the specialist for powershell so I can just give you some hints to run the decryption part, for tasks like "get all files in folder with ending *.enc"
I'm leaving it to you to find a solution.
Assuming you are having a key in the format
MTIzNDU2Nzg5MDEyMzQ1Ng==
then it is a Base64-encode key. If all of your 30.000 certificates were encrypted with the same key then simply get the hex encoded value of the key
using an online service like https://base64.guru/converter/decode/hex.
Just enter the string above and press convert Base64 to Hex and you receive the key as follows:
31323334353637383930313233343536
Now count the chars - here we have 32 chars that mean it is a 16 byte (128 bit) long key used for AES cryptography. It's not a joke to count them because
the length of the key is important for the decryption task.
Having a certificate-file that was encrypted with a 32-char long hex-key ("cert32.enc") you use openssl (can be used in powershell as well as in many
other scripting languages) with this command (it is important to use the -K-option with a capital K and the hexstring enclosed with quotation marks):
openssl enc -aes-128-ecb -d -in cert32.enc -out cert.pem -K "31323334353637383930313233343536"
This will decode you the original certificate to the file "cert.pem" as follows (it is a sample certificate !):
-----BEGIN CERTIFICATE-----
MIIEETCCAvmgAwIBAgIUP2GufsPxg8R2n6L161b6wauxnGYwDQYJKoZIhvcNAQEL
BQAwgZcxCzAJBgNVBAYTAkRFMRIwEAYDVQQIDAlzb21lU3RhdGUxDTALBgNVBAcM
BGNpdHkxEDAOBgNVBAoMB2NvbXBhbnkxEDAOBgNVBAsMB3NlY3Rpb24xHzAdBgNV
BAMMFmphdmFjcnlwdG9AYnBsYWNlZC5uZXQxIDAeBgkqhkiG9w0BCQEWEWphdmFj
cnlwdG9AZ214LmRlMB4XDTIwMDcxMTA4MDMxNloXDTIxMDcxMTA4MDMxNlowgZcx
CzAJBgNVBAYTAkRFMRIwEAYDVQQIDAlzb21lU3RhdGUxDTALBgNVBAcMBGNpdHkx
EDAOBgNVBAoMB2NvbXBhbnkxEDAOBgNVBAsMB3NlY3Rpb24xHzAdBgNVBAMMFmph
dmFjcnlwdG9AYnBsYWNlZC5uZXQxIDAeBgkqhkiG9w0BCQEWEWphdmFjcnlwdG9A
Z214LmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv1fayJg6TO7D
NASipOooJu9aYxLfw5/jokbaggOkL7wPQ2+eWk3tby72jMFniWySm6YIkiTbewYo
8WKl94zWTT8xx5pg/eOh28BTQLsi0/s9RF37z+eJA1TjA6TuBesNevm3V310H93Y
BLTA2Mjp/99W/smBhefaeYEkLh6TrZAAi2JtUHrs0FwNREoXrRIfq9monpUpY7lr
YRSy70nEaEyctw6khxeTRVRR97ZdqogLl2oEur9k5NKD8XHJ6A7MYz+asLMYNcnA
0jV02wR+b6etEr1tAtnswdxQ5T6tLrAnoen5v/fXSDnz93L7oRmTPsQJhK55TrrM
G+RWvV8aoQIDAQABo1MwUTAdBgNVHQ4EFgQULdU7CowdOtePac360y4n9aEbP2ow
HwYDVR0jBBgwFoAULdU7CowdOtePac360y4n9aEbP2owDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAQEAcP6EBsscMFAg0mMTLnd7+7VJzLuodPBAxiMS
zCYtUNS0KPBBR6OrGbHTbbvYd8/VGORpaWORfm0MDLP2kIxLKCYn4l7Wwoou7Idc
+Z+mohQKPwjtHnMZX6HyiCpmDF+qNR7dpOKpIsMahm9zVD8rfySFzr5oDSa7zFSr
MyJKmnz5I+gkUjJKvjYpKPjv7yuENhCbj4roNYK7ztN/vU6yJnFmzOaomP4MxhlE
GDoucjmy+qdGWF/i3Kh8n7zXBxRoBJZSkGqPE2N0PLIJlAFxb9c2QjPu5rmtZiDO
HiFj2Xk7jayMNw3JNVcayHjAcdHkp/u/BgTeqJWZRC+GsMz7ag==
-----END CERTIFICATE-----
Now we are working with the other key length (64 chars long). Having a base64 that looks like
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
you will get the hex-string with 64 characters:
3132333435363738393031323334353637383930313233343536373839303132
Now we are running the openssl-commandline with a similar algorithm (and another filename/key):
openssl enc -aes-256-ecb -d -in cert64.enc -out cert.pem -K "3132333435363738393031323334353637383930313233343536373839303132"
Voila, it's decoding to the same cert.pem as above.
All files are available via my GitHub repo for easy testing:
https://github.com/java-crypto/Stackoverflow/tree/master/Decoding_from_Java_to_Powershell

Unable to decrypt aes-256-gcm encrypted data in java

I have encrypted a file by using OpenSSL aes-256-gcm. As aes-256-gcm not directed supported by command line I have installed LibreSSL and I am able to use the below command to encrypt the data of a file.
openssl enc -aes-256-gcm -K 61616161616161616161616161616161 -iv 768A5C31A97D5FE9 -e -in file.in -out file.out
I need to decrypt the data of file.out in Java and I am unable to do that.
Sample code :
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
String key = "61616161616161616161616161616161";
byte[] IV = "768A5C31A97D5FE9".getBytes();
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText); // for the data by reading file.out
However, I am getting an exception saying javax.crypto.AEADBadTagException: Tag mismatch!
That should't work. Commandline openssl enc doesn't support AEAD ciphers/modes, although early versions of 1.0.1 (below patch h, in 2012-2014) failed to catch if you incorrectly specified such a cipher and silently produced wrong output. If you are actually using LibreSSL and not OpenSSL, it appears to have inherited this problem and not fixed it, even though the whole point of the LibreSSL project was that they were going to fix all the bugs caused by the incompetent OpenSSL people.
If this were a cipher that worked correctly in OpenSSL (and also Java), like aes-256-ctr, then your only problem would be that openssl enc -K -iv take their arguments in hex (suitable for a shell context), whereas Java crypto is called from code that can handle binary data and expects its arguments in that form. As a result the values you provide to OpenSSL are actually 16 bytes (128 bits) and 8 bytes (64 bits), not 256 bits and 128 bits as they should be (for CTR; for GCM an IV of 96 bits would be correct, but as noted GCM won't work here). openssl enc automatically pads -K -iv with (binary) zeros, but Java doesn't. Thus you would need something more like
byte[] key = Arrays.copyOf( javax.xml.bind.DatatypeConverter.parseHexBinary("61616161616161616161616161616161"), 32);
// Arrays.copyOf zero-pads when expanding an array
// then use SecretKeySpec (key, "AES")
// and IVParameterSpec (iv) instead of GCMParameterSpec
// but after Java8 most of javax.xml is removed, so unless you
// are using a library that contains this (e.g. Apache)
// or have already written your own, you need something like
byte[] fromHex(String h){
byte[] v = new byte[h.length()/2];
for( int i = 0; i < h.length(); i += 2 ) v[i] = Integer.parseInt(h.substring(i,i+2),16);
return v;
}
Compare AES encrypt with openssl command line tool, and decrypt in Java and Blowfish encrypt in Java/Scala and decrypt in bash (the latter is the reverse direction, but the need to match is the same)

Java hash function in bash

I'm trying to make a bash script which generates the same hash code like this java hash function:
import java.security.Security;
import org.apache.commons.codec.binary.Hex;
import java.io.UnsupportedEncodingException;
public final static String hash(final String _password)
throws Exception
{
String _salt="0c321e8669fce545";
Security.addProvider(new BouncyCastleProvider());
final String algorithm = "SHA-256";
final String encoding = "UTF-8";
MessageDigest md = MessageDigest.getInstance(algorithm);
md.reset();
md.update(_salt);
byte[] digest = md.digest(_password.getBytes(encoding));
char[] hashChars = Hex.encodeHex(digest);
return salt + new String(hashChars);
}
E.g. I tried it with a static salt and the ssl method to create the password hash:
#!/bin/bash
salt='0c321e8669fce545'
passwordHash=$(echo -n 'hello' | openssl sha256)
finalPassword=$(echo $salt$passwordHash)
But I never get the same result.
Is there any way to realize this?
Edit: My goal is to create a password which is accepted by a platform using this java function for it's password creation.
You need to use -salt and -pass parm. On CentOS, I do something like this in bash:
############################################
# To be used with the -pass parm of openssl
# It's the salt for creating hash
############################################
export enc_salt="0c321e8669fce545"
encyption_file="/tmp/encrypted_pwd.enc"
echo -n 'hello' | openssl aes-256-cbc -md sha256 -a -salt -pass env:enc_salt -out $encyption_file
# Decrypt
openssl aes-256-cbc -md sha256 -d -a -salt -pass env:enc_salt -in $encyption_file
Since each time you run this, even with the same salt, it will give you a different hash. I doubt it will ever match what you are seeing in your Java program. What's important is when you de-crypt the encryption in bash or Java, it should give you what you are looking for.
The reason for using an environment variable for the salt is so anyone lurking using ps command monitoring what is executing will not see the salt passed to openssl
On a 64 bit OS, substitute -base64 for -a parm when using openssl

OpenSSL Hmac and BouncyCastle Hmac Differ

I am trying to figure out why an hmac in openssl is not giving me the same result as an hmac in java.
in open ssl
echo -n "Hello" | openssl dgst -sha256 -hmac 04d6b077d60e323711b37813b3a68a71
Output:
cc598d8840fe409d5fcc1c1c856f9e8c311d1c458850615555857b023f1cd94c
In java
String key = "04d6b077d60e323711b37813b3a68a71"
SecretKeySpec key2 = new SecretKeySpec(Hex.decode(key), "RAW");
String data = "Hello";
Mac hmac = Mac.getInstance("Hmac-SHA256", BouncyCastleProvider.PROVIDER_NAME);
hmac.init(key2)
byte[] bytes = hmac.doFinal(data.getBytes());
System.out.println(Hex.toHexString(bytes));
Output:
877f9c8eb44c20987e3978928fbfcea0f1cf99c88f9db904596921b7ecf0613b
I am at a loss why these are different.
OpenSSL treats -hmac key option as if the key is just an array of bytes represented as corresponding ASCII characters. The key is thus limited to contain only printable characters.
You can get the same results in Java as in OpenSSL by using
SecretKeySpec key2 = new SecretKeySpec( key.getBytes("ASCII"), "RAW" );
Alternatively you can use openssl dgst -sha256 -mac HMAC -macopt hexkey:string where string will be treated as a HEX encoded key.

Categories

Resources