AES/CBC/PKCS5Padding implementation in Ruby (for rails) - ruby

I need to decrypt text encrypted using AES/CBC/PKCS5Padding scheme. The encrypted text I got was generated using some Java software.
All values below are changed by me to something fictional.
What I get is a Key aHjgYFutF672eGIUGGVlgSETyM9VJj0K (256-bit = 32-chars * 8-bit)
and IV: rxYoks3c8hRRsL2P (16-bit)
and (I supposed) Base64 encoded encrypted result ETlAHS5ZcshKxQUaHVB8==
What I need is to decrypt in Ruby this ETlAHS5ZcshKxQUaHVB8== to get in the and a simple string, like 'blablablabla'
I tried to decrypt what I got using both Ruby and just common linux console openssl command.
NOTE: Key and IV below are not the ones used in real code:
# require 'openssl'
# require 'base64'
# decryption
aes = OpenSSL::Cipher::AES256.new(:CBC)
aes.decrypt
aes.padding = 1 # actually it's on by default
aes.key = "aHjgYFutF672eGIUGGVlgSETyM9VJj0K"
aes.iv="rxYoks3c8hRRsL2P"
aes.update(Base64::decode64("ETlAHS5ZcshKxQUaHVB8=="))+aes.final
=> OpenSSL::Cipher::CipherError: bad decrypt
Same as above but in console, key and iv converted to hex with:
$ echo -n $key256 | hexdump -e '16/1 "%02x"'
$ echo -n $iv | hexdump -e '16/1 "%02x"'
$ echo "ETlAHS5ZcshKxQUaHVB8==" | openssl enc -d -aes-256-cbc -a -K 61486a675946757446363732654749554747566c67534554794d39564a6a304b -iv 7278596f6b73336338685252734c3250
bad decrypt
140378046432928:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:539:
BTW. to get back original key and iv in the console you an use:
$ echo 61486a6... | xxd -r -p
#or , but then need to add \x before every character pair
$ eval `printf "\x61\x48......"
Please give me some clues as I hoped in the beginning that I will be able to use https://github.com/chicks/aes gem. The gem seems fine, it's just a nice wrapper for OpenSSL::Cipher::Cipher.
Is it possible that ruby/openssl use different PKCS, let's say PKCS#7, Java uses PKCS#5 and I need to preprocess my data ? Or there is a vesion mismatch between ruby/openssl and that Java's PKCS #7 and #5? #5 is meant for 8byte data blocks and #7 is for 16byte? Just a wild guess ...

The Ruby code in my first post is correct, the problem was this AES/CBC/PKCS5Padding used by Java part.
Java program should not use this scheme for AES-CBC-256. PKCS5 pads to a 64 bit (8 byte) block size, but AES-256-CBC uses 16 byte blocks. Therefore, PKCS7 must be used.

Related

encode / decode binary data in a qr-code using qrencode and zbarimg in bash

I have some binary data that I want to encode in a qr-code and then be able to decode, all of that in bash. After a search, it looks like I should use qrencode for encoding, and zbarimg for decoding. After a bit of troubleshooting, I still do not manage to decode what I had encoded
Any idea why? Currently the closest I am to a solution is:
$ dd if=/dev/urandom bs=10 count=1 status=none > data.bin
$ xxd data.bin
00000000: b255 f625 1cf7 a051 3d07 .U.%...Q=.
$ cat data.bin | qrencode -l H -8 -o data.png
$ zbarimg --raw --quiet data.png | xxd
00000000: c2b2 55c3 b625 1cc3 b7c2 a051 3d07 0a ..U..%.....Q=..
It looks like I am not very far, but something is still off.
Edit 1: a possible fix is to use base64 wrapping, as explained in the answer by #leagris .
Edit 2: using base64 encoding doubles the size of the message. The reason why I use binary in the first place is to be size-efficient so I would like to avoid that. De-accepting the answer by #leagris as I would like to have it 'full binary', sorry.
Edit 3: as of 2020-03-03 it looks like this is a well-known issue of zbarimg and that a pull request to fix this is on its way:
https://github.com/mchehab/zbar/pull/64
Edit 4: if you know of another command-line tool on linux that is able to decrypt qr-codes with binary content, please feel free to let me know.
My pull request has been applied. ZBar version 0.23.1 and newer will be able to decode binary QR codes:
zbarimg --raw --oneshot -Sbinary qr.png
zbarcam --raw --oneshot -Sbinary
QR codes have several encoding modes. The simplest, most commonly used and widely supported is the alphanumeric encoding which is suitable for simple text. The byte encoding allows storing arbitrary 8 bit data in the QR code. The ECI mode is like 8 bit mode but with additional metadata that tells the decoder which character set to use in order to decode the binary data back to text. Here's a list of known ECI values and the character encodings they represent. For example, when a decoder encounters an ECI 26 mode QR code it knows to decode the binary data as UTF-8.
The qrencode tool is doing its job correctly: it is creating a byte mode QR code with the data you gave it as its contents. The problem is most decoders were explicitly designed to handle textual data first and foremost. The retrieval of the raw binary data is a detail at best.
Current versions of the zbar library will treat byte mode QR codes as if they were unknown ECI mode QR codes. If a character set isn't specified, it will attempt to guess the encoding and convert the data to it. This will most likely mangle the binary data. As you noted, I brought this up in issue #55 and after some time managed to submit a pull request to improve this. Should it be merged, the library will have binary decoder option that will instruct decoders to return the raw binary data without converting it. Another source of data mangling is the tendency of the command line tools to append line feeds to the output. I submitted a pull request to allow users to prevent this and it has already been merged.
The zxing-cpp library will also try to guess the encoding of binary data in QR codes. The comments suggest that the QR code specification requires that decoders pick an encoding without specifying a default or allowing them to return the raw binary data. In order to make that possible, the binary data is copied to a byte array which can be accessed through the DecoderResult. When I have some free time, I intend to write zximg and zxcam tools with binary decoding support for this library.
It's always possible to encode binary data as base 64 and encode the result as an alphanumeric QR code. However, base 64 encoding will increase the size of the data and the alphanumeric mode doesn't allow use of the QR code's maximum capacity. In a comment, you mentioned what you intend to use binary QR codes for:
I want to have a package to effectively dump some gpg stuff in a format that makes recovery easy.
That is the exact use case I'm attempting to enable with my pull request: an easier-to-restore paperkey. 4096 bit RSA secret keys can be directly QR encoded in 8 bit mode but not in alphanumeric mode as base 64-encoded data.
See also: Storing binary data in QR codes
Look like zbarimg is only supporting printable characters and adding a newline
printf '%s' 'Hello World!' >data.bin
xxd data.bin
qrencode -l H -8 -o data.png -r data.bin
zbarimg --raw --quiet data.png | xxd
I think a better more portable option would be to base64 encode your binary data before qr encoding.
Like this:
dd if=/dev/urandom bs=10 count=1 status=none > data.bin
xxd data.bin
base64 <data.bin | qrencode -l H -8 -o data.png
zbarimg --raw --quiet data.png | base64 -d | xxd

How do I decrypt files that are encrypted using des command in Ruby?

I need to decrypt files that are encrypted with this command:
des -E -u -k "some key" file.in file.out.enc
The decryption code in Ruby:
def decrypt(key)
cipher = OpenSSL::Cipher.new(‘des’).decrypt
cipher.key = key
File.open(‘file.out’, ‘wb’) do |outf|
decrypted = cipher.update(File.read(‘file.in.enc’)) + cipher.final
outf.write(decrypted)
end
end
I’m getting wrong final block length error when I run the code above. I also tried decrypting using the openssl command line tool and got a bad magic number error. Any advice?
Try switching the mode, from CBC to ECB for instance with OpenSSL::Cipher.new('DES-ECB').
If you check which ciphers your Ruby installation supports by looking at OpenSSL::Cipher.ciphers, you'll find a list of available modes too.

Checking whether a certain root CA is trusted on the system

My objective is to test whether a certain root certification authority is trusted by the active user. I currently have a working solution that I managed to piece together using several other answers1,2, but it seems very convoluted to me, so I'm asking for alternative (or at least, simplified) suggestions from people who (unlike me) know what they're doing.
I am assuming that this will be executed by a non-privileged user (i.e. one that cannot install new packages), so I would like to use utilities that are likely bundled with most unix/linux distros (unlike e.g. certutil). For this reason, the current solution uses awk, grep and openssl, which seem quite universal.
Another thing I should note is that I'm not concerned with the possible security implications that might arise from testing certificates the way I do.
Here's my current code:
awk -v cmd='openssl x509 -noout -issuer' '/BEGIN/{close(cmd)};{print | cmd}'
< /etc/ssl/certs/ca-certificates.crt
| grep -F 'issuer=C = US, O = company, CN = localhost, OU = engineering'
It uses awk in conjunction with openssl to iterate over all existing certificates, outputting their Issuer, then piping it to grep to test whether the required line exists.
The output I'm getting in the case of a positive match is the string I'm looking for, even though all I need is a binary answer (true/false, yes/no, 1/0, ...).
Any suggestion on how to achieve my goal in a simpler and/or more universal fashion?
You can spare the call to awk by processing all the certificates using openssl alone. According to this answer on Server Fault the following will use an intermediate conversion to provide the same amount of information (i.e. the issuer for each certificate in the input file) which can be filtered for the data you're looking for:
openssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt \
| openssl pkcs7 -print_certs -noout \
| grep '^issuer=/C=US/O=company/CN=localhost/OU=engineering'
I find this an improvement because it doesn't use a bulky call to awk (which would also be another dependency), and the output of pkcs7 seems much more machine-readable than the whitespace-ridden original output from x509.
Note that you can use the return value of the above grep call to tell whether the given root CA is trusted:
openssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt \
| openssl pkcs7 -print_certs -noout \
| grep -q '^issuer=/C=US/O=company/CN=localhost/OU=engineering' && echo 'Certificate found!'

OpenSSL key length too short in Ruby, not in Bash

I originally experimented with a simple encryption script in Bash and it worked pretty much as expected. However, I'm now trying to do the same thing in Ruby and the Ruby version seems function a little differently.
Bash
Encrypt
echo 'hello' | openssl enc -aes-256-cbc -a
Password: mypass
Result: U2FsdGVkX19rERfOXiKs97FgwIkLy3+ttZzaHkEoQyE=
Decrypt
echo 'U2FsdGVkX19rERfOXiKs97FgwIkLy3+ttZzaHkEoQyE=' | openssl aes-256-cbc -d -a
Password: mypass
Result: hello
Ruby
require "openssl"
require 'base64'
cipher = OpenSSL::Cipher.new('AES-256-CBC').encrypt
cipher.key = 'mypass'
This is what I've attempted in Ruby so far but I receive a OpenSSL::Cipher::CipherError: key length too short error. I would like to mimic Bash as much as possible.
OpenSSL uses a (largely undocumented) password based key derivation function (PBKDF) called EVP_BytesToKey using an 8 byte salt and an iteration count of 1. A magic and salt of 8 bytes each are prefixed to the ciphertext (check the first bytes of the result to see the magic).
Obviously "mypass" cannot be a correct key for AES. AES keys are 16, 24 or 32 byte binary values for the 128, 192 and 256 key sizes. You can however specify a key directly using the -K switch on the command line to make the code compatible with the Ruby Cipher object. In that case you need to specify the key using binary (a file) or hexadecimals for the openssl command line and in Ruby. You would also need to specify an IV.
Alternatively you would have to find an EVP_BytesToKey implementation for Ruby, but note that this is an old OpenSSL specific function with a completely insecure iteration count.

openssl AES encryption adds a blocksize worth of bytes to output

I'm trying to encrypt with openssl on the console to match output generated by another implementation of AES. All details are known. I'm using AES in 128-bit CBC mode. Weirdly enough, irrespective of the file size, the output will be 16 bytes larger. I think openssl is appending some kind of padding.
In the direction to the other implementation it's not that much of a problem as I can drop the last 16 bytes, but the other way around is as I can't invent the bytes that openssl will probably check for validity.
How do I tell openssl not to do that?
Commandline:
openssl enc -aes-128-cbc -K <pre-shared key in hex> -in rawfile.bin -out encfile.enc -iv <pre-shared IV in hex>
openssl enc has a -nopad option. I've not used it, but it sounds relevant.
-nopad
disable standard block padding

Resources