Java hash function in bash - java

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

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

Java - how to unlock a passphrase-protected PEM private key

I have got a passphrase protected PEM key file generated by OpenSSL
openssl genrsa -aes128 -passout stdin -out testfile.pem
I have also generated a public key file using OpenSSL
openssl rsa -in testfile.pem -out testfile_pub.pub ( propts for password)
I would like to be able to use this private key to sign my claims etc. and then send requests. What I am struggling to understand (or more like confirming my understanding about) are the following:
1) My private key is password protected, does it mean no one can actually generate the public key without unlocking it first? i.e. that's where the protection is?
2) If I was to read this encrypted private key PEM file in Java, I would have to do something similar to:
\\ 1. Read file as string
\\ 2. Replace all boring bits e.g. begin/end/rsa/private/public/key/enc/--
\\ 3. decode using Base64
\\ 4. read as PKCS8 keyspec and generate PrivateKey object
but doesn't this mean that no one is actually stopping me from reading the keyspecs ? I guess what I am trying to compare with is how we generate JKS keys with optional keypass/storepass. But may be I am not supposed to compare this.
Could anyone help me understand?
Thanks,
openssl rsa -in testfile.pem -out testfile_pub.pub does not export the public key, it actually exports the private key out in clear text (if you provided the correct password). To export the public key use the -pubout option.
Yes you will need the password to export the public key.
To import the private key in Java you will need to convert it to PKCS8 first:
openssl pkcs8 -topk8 -in testfile.pem -inform pem -out testfile_pkcs8.pem -outform pem
Then you can import it in Java like:
String encrypted = new String(Files.readAllBytes(Paths.get("testfile_pkcs8.pem")));
encrypted = encrypted.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
encrypted = encrypted.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo(Base64.decode(encrypted));
PBEKeySpec keySpec = new PBEKeySpec("mypassword".toCharArray()); // password
SecretKeyFactory pbeKeyFactory = SecretKeyFactory.getInstance(pkInfo.getAlgName());
PKCS8EncodedKeySpec encodedKeySpec = pkInfo.getKeySpec(pbeKeyFactory.generateSecret(keySpec));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey encryptedPrivateKey = keyFactory.generatePrivate(encodedKeySpec);
No, it doesn't mean that anyone can read the key, because you still have to provide the password.

Openssl rsault verification Java implementation

For example I have generated signature:
$ openssl rsautl -sign -inkey private_key.pem -keyform PEM -in data > signature
Then if I want to verify it, I just do:
$ openssl rsautl -verify -inkey public_key.pem -in signature -pubin
And the output will be my data encoded in first step.
So the question is, how to implement this verification with Java?
Can I use Signature class somehow or any other way?
P.S. 1 more question: As I know, public key must not be used to decrypt rsa signature, but anyway, in my example it is used for that. So anyone who has public key, can decrypt my message?
Thanks
Apparently the rsaautl uses equivalent of NONEwithRSA signature scheme, where the input data are takend as they are (assumed to be hashed or short ( length << N))
Signature verification:
Signature signature = Signature.getInstance("NONEwithRSA");
signature.initVerify(pubKey);
signature.update(data);
boolean verified = signature.verify(signatureBytes);
If data are longer (say longer than a common key / hash length) I suggest to use hashed signature, for example:
openssl dgst -sha256 -sign private.pem data.txt | base64
And verify by Java
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(pubKey);
signature.update(data);
boolean verified = signature.verify(signatureBytes);

Blowfish encrypt in Java/Scala and decrypt in bash

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.

Categories

Resources