Sign JAR without JRE but with OpenSSL? - jar

Our system has Java and C parts. On the C side, we are signing certain data using command-line scripts calling OpenSSL commands. Now we want to sign some JARs too. We already have established PKI (what is important for this case - private keys are accessible) "on the C side" and we try to avoid duplicating/extending that to the Java side.
What would be an easy way to get the JAR signed for someone who does not want to have JRE (but has OpenSSL)? I.e. I want to create the correct MANIFEST.MF, KEY.SF and KEY.?SA for my JAR. Their format is not complicated and this seems to be doable with some scripting. Has anyone done this before?

Answering own question.
Format of MANIFEST.MF and KEY.SF is documented by Oracle. Surprisingly, exact content of the signature KEY.?SA (where "KEY" is the keystore alias of the signing key) is not detailed in the "Signature File" section.
This KEY.RSA (for RSA signatures) can be created by OpenSSL command-line tools in exactly the way jarsigner creates it. Example for RSA signature and SHA256 digest:
$ openssl smime -sign -noattr -in META-INF/TEST1.SF -outform der -out META-INF/TEST1.RSA -inkey privateKey.pem -signer cert.pem -md sha256
Similarly the signature can be produced with OpenSSL C API. Snap of C code (no error checking):
/* PKCS7_PARTIAL flag is needed to be able to change the digest from the default value */
PKCS7 *signed_data = PKCS7_sign(NULL, NULL, NULL, data,
PKCS7_NOATTR | PKCS7_DETACHED | PKCS7_PARTIAL
);
digest = EVP_get_digestbyname("sha256");
PKCS7_sign_add_signer(signed_data, signcert, pkey, digest, flags);
PKCS7_final(signed_data, NULL, 0);
Signature created in this way is identical to what jarsigner would have produced.

Related

Openssl 1.0.2p decryption failing?

I am using 1.0.2p to encrypt the file using the following command.
#openssl aes-128-cbc -e -k 'abcdefghijklmnop' -in my.txt -out myencrypt.txt
My decryption is based out of Crypto.Cipher python module.
Here is my code. However, I am unable to decrypt the text successfully.
I am unsure on what am I missing here?
from Crypto.Cipher import AES
def decrypt(ciphertext, key):
iv = ciphertext[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[AES.block_size:])
return plaintext.rstrip(b"\0")
def decrypt_file(file_name, key):
with open(file_name, 'rb') as encrypt_file:
ciphertext = encrypt_file.read()
dec = decrypt(ciphertext, key)
with open("plain.txt", "wb") as plain_file:
plain_file.write(dec)
if __name__ == "__main__":
decrypt_file('myencrypt.txt', 'abcdefghijklmnop')
Your issue could be that OpenSSL 1.0.2 still uses MD5 as its hashing algorithm instead of SHA. I am not super familiar with this Python library, but from the docs it looks like the default hashing algorithm is SHA1.
From the parameters section of new():
hashAlgo (hash object) – The hash function to use. This can be a module under Crypto.Hash or an existing hash object created from any of such modules. If not specified, Crypto.Hash.SHA1 is used.
So what I believe is happening is that your encryption line (using OpenSSL 1.0.2) uses MD5 when encrypting, but the Python library is defaulting to SHA1. So you would either need to update your OpenSSL version and/or specify the hashing algorithm in your Python code when calling new().
I believe OpenSSL was updated in 1.1.0 to use a SHA algorithm for hashing. I have actually ran into this problem when trying to decrypt an archive from an older server, using OpenSSL for both encryption and decryption but the versions won't match. If you have any issues decrypting an archive from an older version of OpenSSL, you may have to specify the older hashing algorithm with -md md5, like:
openssl enc -aes256 -d -in your/input/file.encrypt -out your/output/file -md md5

Openssl - AES-256-ECB decrypt with key, bad magic number

Im trying to decrypt a text, which was encrypted with AES-256-ECB with the given key. To decrypt, Im using the same version of the openssl which was used for encryption (OpenSSL 1.1.1d 10 Sep 2019).
String to decrypt: VAWawVAWawxiyH20dI+t5NPAY9w== (inside file.txt)
Key: 461a966faef244e4808d6b2b8e928d01 (inside key.txt)
I tried those commands:
cat file.txt | base64 -d > file2.txt
openssl enc -AES-256-ECB -d -in file2.txt -out answer.txt --kfile key.txt
And im getting: bad magic number. Whats the problem?
openssl enc will normally use a password to derive a key. So it is the derived key that is used to decrypt the file. The derivation process requires a "salt", and openssl enc during encryption stores that salt at the beginning of the file along with a "magic number" to identify it. If the magic number is missing (usually because the file wasn't encrypted by openssl enc or because the password based key derivation derivation method wasn't used) then you get this error.
The -kfile option tells OpenSSL to read the password from a file and then derive the key from it. Probably want you intended was to not use password derivation at all, but to use the explicit key. In which case you need to use the -K option and supply the key on the command line using hex.

Generating secret tenant for Salesforce Shield encryption module using Openssl, BYOK

Our customer needs us to implement the Shield encryption module for Salesforce, with the Bring Your Own key feature.
Our customer also provided an example of secret tenant to be integrated in Shield.
This tenant is a 32 or 64-character (letters and numbers only) chain.
Salesforce provides a script to generate our own files if we don't want to use the native ones. Here is the script :
#!/bin/sh
PLAINTEXT_SECRET_HASH_B64="plaintext_secret_hash.b64"
ENCRYPTED_SECRET_B64="encrypted_secret.b64"
PLAINTEXT_SECRET="plaintext_secret.bin"
PUBLIC_KEY_PEM="public_key.pem"
usage ()
{
echo 'Generates a random 256-bit value, hashes it, and encrypts it using a given certificate.'
echo ''
echo 'Usage : secretgen <downloaded.crt>'
exit
}
if [ -z "$1" ]
then
usage
fi
# generate a random value to use as the secret.
head -c 32 /dev/urandom | tr '\n' = > $PLAINTEXT_SECRET
# hash the plaintext secret and encode it with base64. Salesforce will use this for integrity checking.
openssl dgst -sha256 -binary $PLAINTEXT_SECRET | openssl base64 -out $PLAINTEXT_SECRET_HASH_B64
# extract the public key from the downloaded .crt file
openssl x509 -pubkey -noout -in $1 > $PUBLIC_KEY_PEM
# encrypt the secret using the public key and encode it with base64.
openssl rsautl -oaep -encrypt -pubin -inkey $PUBLIC_KEY_PEM -in $PLAINTEXT_SECRET | openssl base64 -out $ENCRYPTED_SECRET_B64
echo 'Generated files: '$ENCRYPTED_SECRET_B64' and '$PLAINTEXT_SECRET_HASH_B64'.'
echo 'Both of these should be uploaded to Salesforce.'
The thing is that I would like to directly use the 32-character chain provided by our customer instead of the one generated during this step :
# generate a random value to use as the secret.
head -c 32 /dev/urandom | tr '\n' = > $PLAINTEXT_SECRET
So I tried to directly replace the generated chain with the one provided, but it simply doen't work and gives me an error messsage when all the files are uploaded in Salesforce.
I don't understand why the 32-characters chain provided by the customer won't work while a randomly generated 32-characters chain works.
Is the randomly generated chain not so random ? (it looks like a mix of letters, numbers and special characters, while the customer chain only contains numbers and letters).
Any Ideas ?
Thanks a lot
Just assumptions, depending on your OS and/or openssl tool you are using, a few things may happen:
your new file (plaintext_secret.bin) is not actually 32 bytes
there may be hidden space chars (line the '\n', tabs..), and your version of openssl is not handling that properly.
This is why there are actually 2 versions of the script, Unix and OSX
all is good, but you may have mixed up the certificates (BYOK needs a certificate to wrap your content)
the certificate needs to be a PE certificate - check it on the BYOK page.
from M2Crypto import RSA, X509
x509 = X509.load_cert("your_cert.pem")
rsa = x509.get_pubkey().get_rsa()
# building a secret as a 32bytes bytearray
my_secret = "This is my secret, and nobody knows it"
my_secret_bin = bytearray(my_secret)
# this should be exactly 32
len(my_secret_bin)
import base64
import hashlib
my_hash = base64.b64encode((hashlib.sha256(my_secret_bin[0:32])).digest())              
my_encrypted_secret = base64.b64encode(rsa.public_encrypt(my_secret_bin[0:32], RSA.pkcs1_oaep_padding))    
# This can be used to call it via API
# note that the 'your_cert' certificate name should match
upload_params = { 'SecretValue':my_encrypted_secret, 'SecretValueHash':my_hash, 'SecretValueCertificate':'your_cert'}
# write to files
with open('encrypted_secret.bb64', 'wb') as f:
f.write(my_encrypted_secret)
with open('secret_hash.b64', 'wb') as f:
f.write(my_hash)

Decrypt OpenSSL Bruteforce

I forgot the password to my bitcoin backup and would like to brute-force decrypt it.
I have been given this information;
"The manual backup files are encrypted using your chosen backup password. You can use OpenSSL to decrypt:
openssl enc -d -aes-256-cbc -a -in <filename>"
Unfortunately, I don't have much experience doing this and all I can do it get it in terminal asking for the password. Can anybody give me instructions on how to brute-force decrypt a file using OpenSSL?
n.b. The password was quite simple, using only letters and perhaps one number.
... all I can do it get it in terminal asking for the password.
You need to add -passin pass:XXX options, where XXX is the password you want to try.
There's more options for -passin, see PASS PHRASE ARGUMENTS for openssl(1) command.
You will also need to understand the -k and -K options to openssl enc.
Can anybody give me instructions on how to brute-force decrypt a file using OpenSSL?
Run something like this in a loop:
# Build your list of candidates
PASSWORDS=...
for PASSWORD in $PASSWORDS; do
openssl enc -d -aes-256-cbc -a -in <filename> -passin pass:$PASSWORD
RET=$?
if [ $RET -eq 0 ]; then
echo "Candidate password: $PASSWORD"
fi
done
openssl enc returns 0 on success, non-zero otherwise. Note: you will get false positives because AES/CBC can only determine if "decryption works" based on getting the padding right. If the file decrypts but is not recovered, then remove that candidate password.
Authenticated encryption would fix the ambiguity, but you would have needed to encrypt with AES/CCM, AES/GCM, AES/EAX etc.
If you want to try on your own there is now a bruteforcing tool that will do it very efficiently, called bruteforce-salted-openssl. Note that the digest used back in the days by openssl was md5 (rather than sha256 today), so you would have to specify the digest (-m md5)

OpenSSL Authenticated Encryption

I'm trying to use OpenSSL for authenticated encryption. Specifically, I'm trying to use AES-256-GCM (or CCM).
However, when I run openssl list-cipher-commands, I don't see it. The only AES ciphers shown are these:
aes-128-cbc
aes-128-ecb
aes-192-cbc
aes-192-ecb
aes-256-cbc
aes-256-ecb
I'm on openssl 1.0.1e, so it should be supported.
OpenSSL supports aes-256-gcm as an algorithm, but it does not support aes-256-gcm as a command tool. The difference is that you can enter openssl aes-256-cbc in the command line to encrypt something. On the other hand, there are no such openssl aes-256-gcm command line tool.
You can use the EVP interface to call aes-256-gcm algorithm, as this answer shows.
By the way, you may try to use openssl enc aes-256-gcm in the command line. That does not work either, because no additional authenticated data will be handled by the enc command. See more information here.

Resources