GPG: secret key & check passphrase - ruby

How to check that GPG secret key does not contain passphrase?
I wouldn't like import this key.

I found solution:
stdin, stdout, stderr = Open3.popen3("echo '\n\n\n\n\nsave' | gpg --command-fd 0 --homedir #{dir} --edit-key #{secret_key[:keyid]} passwd")
output = stderr.read
if output =~ /.../
...
end

Related

How to use Ruby to decrypt plaintext that was encrypted with the openssl command line utility using a password and no salt?

I have encrypted some plaintext using the openssl command line utility using a password:
echo -n "foo" | openssl enc -e -base64 -rc4 -nosalt -pass pass:secretkey
This returns:
ryW2
How can I decrypt this value using Ruby?
Finally with reference of Above in Ruby
Encode
def encrypt(str, key)
cipher = OpenSSL::Cipher.new('RC4')
cipher.key = OpenSSL::Digest.digest('md5', key)
cipher.decrypt
Base64.encode64(cipher.update(str) + cipher.final)
end
encrypt('This is Test', 'Test')
=> "JGmpM+ewGA79qaZH\n"
Decode
def decrypt(encrypted_string, key)
decipher = OpenSSL::Cipher.new('RC4')
decipher.key = OpenSSL::Digest.digest('md5', key)
decipher.decrypt
decipher.update(Base64.decode64(encrypted_string)) + decipher.final
end
decrypt("JGmpM+ewGA79qaZH\n", "Test")
=> "This is Test"
Here is the command you would need to enter to encrypt the string foo into some ciphertext:
echo -n "foo" | openssl enc -e -base64 -rc4 -nosalt -pass pass:secretkey -p
And this returns:
key=610A2EE688CDA9E724885E23CD2CFDEE
ryW2
ryW2 is your base64 encoded ciphertext.
We have added -p so we can see the key that is used for encryption.
-p is required because OpenSSL uses an internal function EVP_BytesToKey to convert the passphrase to a key and there is no equivalent Ruby method to do this. Accordingly, you'll have to use this key value directly in Ruby when encrypting or decrypting instead of the passphrase.
Decryption is just as straight-forward:
echo "ryW2" | openssl enc -d -base64 -rc4 -nosalt -pass pass:secretkey
This returns:
foo
Given all this, here is how you would encrypt this string using Ruby to get the same value:
require 'openssl'
require 'base64'
plaintext = 'foo'
cipher = OpenSSL::Cipher.new('rc4')
cipher.encrypt
# Use the key that was generated by EVP_BytesToKey
key = '610A2EE688CDA9E724885E23CD2CFDEE'
# Convert the key to a byte string
key_bytes = key.scan(/../).map { |x| x.hex.chr }.join
cipher.key = key_bytes
ciphertext = cipher.update(plaintext) + cipher.final
base64 = Base64.strict_encode64(ciphertext)
This returns the same value as OpenSSL on the command line:
ryW2
Decryption is fairly straightforward:
decipher = OpenSSL::Cipher.new('rc4')
decipher.decrypt
decipher.key = key_bytes
decrypted = decipher.update(ciphertext) + decipher.final
This returns the original plaintext:
foo
Ultimately, encryption and decryption is fairly straightforward as long as you know what EVP_BytesToKey will return for a given pass. You can read more about EVP_BytesToKey here:
How to extract the IV vector generated by encrypt method from encrypted_strings
How do I refactor OpenSSL pkcs5_keyivgen in ruby?
https://github.com/crypto-browserify/EVP_BytesToKey
https://www.openssl.org/docs/man1.0.2/man3/EVP_BytesToKey.html
I wasn't happy that there is no built-in way to do this in Ruby. My other answer to this question is a functional workaround but it requires some time-consuming steps that I'm not crazy about. So I wrote a gem called evp_bytes_to_key that'll do it for you!
First install the gem:
gem install evp_bytes_to_key
Then generate your ciphertext:
echo -n "foo" | openssl enc -e -base64 -rc4 -nosalt -pass pass:secretkey
This returns:
ryW2
Then decrypt it in Ruby:
require 'evp_bytes_to_key'
require 'openssl'
require 'base64'
decipher = OpenSSL::Cipher.new('rc4')
decipher.decrypt
decipher.key = EvpBytesToKey::Key.new('secretkey', nil, 128, 0).key
ciphertext = Base64.strict_decode64('ryW2')
plaintext = decipher.update(ciphertext) + decipher.final
This returns the original plaintext:
foo
More examples for using this gem can be found in the README.

How to use openssl to output encrypted/decrypted message to stdout

I am just trying to encrypt/decrypt a password with the shell (non-interactively - it's for an automated script). I am following this example:
https://superuser.com/a/20552/362669
I tried converting it to this so that it doesn't use a file output.bin to store the encrypted text:
#!/usr/bin/env bash
cd `dirname "$BASH_SOURCE"`
# generate a 2048-bit RSA key and store it in key.txt
openssl genrsa -out key.txt 2048
# encrypt "hello world" using the RSA key in key.txt
encrypted="$(echo "hello world" | openssl rsautl -inkey key.txt -encrypt)"
echo "encrypted: $encrypted"
# decrypt the message and output to stdout
decrypted="$(echo "$encrypted" | openssl rsautl -inkey key.txt -decrypt)"
echo "decrypted: $decrypted";
but all I get is this garbully-guk:
Generating RSA private key, 2048 bit long modulus
........................................................................................................................+++
............................+++
e is 65537 (0x10001)
��◆J��┌ܥײ��R▒��%⎽F�� 1l�}�%��?�0���+��%���C�8|_/!�A"Ꜵ:�������.��W2Pras��1���� ��(�a
��]�[�남␍◆�=┬─�з≤�ɦ�;�└�1MFP��^␋�#D� �T_⎺F�Eπ�2��U2Ÿ┌π��N│�� ⎽��_\2�� 8V��%��(�^���␍4�#�π���*^D ���/�└�
RSA ⎺⎻␊⎼▒├␋⎺┼ ␊⎼⎼⎺⎼
4662363756:␊⎼⎼⎺⎼:04FFF06B:⎼⎽▒ ⎼⎺┤├␋┼␊⎽:CRYPTO_␋┼├␊⎼┼▒┌:␉┌⎺␌┐ ├≤⎻␊ ␋⎽ ┼⎺├ 02:/B┤␋┌␍R⎺⎺├/L␋␉⎼▒⎼≤/C▒␌␤␊⎽/␌⎺└.▒⎻⎻┌␊.│␉⎽/S⎺┤⎼␌␊⎽/┌␋␉⎼␊⎽⎽┌/┌␋␉⎼␊⎽⎽┌-22.260.1/┌␋␉⎼␊⎽⎽┌-2.6/␌⎼≤⎻├⎺/⎼⎽▒/⎼⎽▒_⎻┐1.␌:185:
4662363756:␊⎼⎼⎺⎼:04FFF072:⎼⎽▒ ⎼⎺┤├␋┼␊⎽:CRYPTO_␋┼├␊⎼┼▒┌:⎻▒␍␍␋┼± ␌␤␊␌┐ °▒␋┌␊␍:/B┤␋┌␍R⎺⎺├/L␋␉⎼▒⎼≤/C▒␌␤␊⎽/␌⎺└.▒⎻⎻┌␊.│␉⎽/S⎺┤⎼␌␊⎽/┌␋␉⎼␊⎽⎽┌/┌␋␉⎼␊⎽⎽┌-22.260.1/┌␋␉⎼␊⎽⎽┌-2.6/␌⎼≤⎻├⎺/⎼⎽▒/⎼⎽▒_␊▒≤.␌:580:
␍␊␌⎼≤⎻├␊␍:
▒┌␊│⎽-└▒␌:␋┼├␊⎼⎺⎽ ▒┌␊│$
and my shell session is basically messed up.
Anyone know what that is? Maybe it's outputting characters that the shell can't handle?
Update: if I don't log the encrypted value, then I get this:
Generating RSA private key, 2048 bit long modulus
........................+++
..........+++
e is 65537 (0x10001)
RSA operation error
4558829164:error:04FFF06B:rsa routines:CRYPTO_internal:block type is not 02:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.260.1/libressl-2.6/crypto/rsa/rsa_pk1.c:185:
4558829164:error:04FFF072:rsa routines:CRYPTO_internal:padding check failed:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.260.1/libressl-2.6/crypto/rsa/rsa_eay.c:580:
decrypted:
I think the best idea would be to convert the binary to/from base64.
Just pipe output through a "openssl base64" to command to enable and "openssl base64 -d" command to decode.
so:
encrypted="$(echo "hello world" | openssl rsautl -inkey key.txt
-encrypt | openssl base64)"
and
decrypted="$(echo "$encrypted" | openssl base64 -d | openssl rsautl
-inkey key.txt -decrypt)"

AES 256 CBC encrypt with Ruby with OpenSSL not working

I'm trying to encrypt data with OpenSSL library in Ruby using passphrase.
Ruby code looks like this:
require('openssl')
require('base64')
cipher = OpenSSL::Cipher.new ('AES-256-CBC')
cipher.encrypt
cipher.iv = iv = cipher.random_iv
pwd = 'topsecret'
salt = OpenSSL::Random.random_bytes 8
iter = 10000
key_len = cipher.key_len
digest = OpenSSL::Digest::SHA256.new
key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
cipher.key = key
puts "salt=#{salt.unpack('H*')[0]}"
puts "key=#{key.unpack('H*')[0]}"
puts "iv=#{iv.unpack('H*')[0]}"
encrypted = cipher.update 'my data to encrypt'
encrypted << cipher.final
puts "encrypted=#{Base64.strict_encode64(encrypted)}"
# it returns:
# salt=1332e5603cbc018a
# key=11a168cf01556a5ee3e22e049f0e65d3adcd75f39e32c7d19aec32a0ccb40d93
# iv=35a08f2d3e719abbee78a0f4fe47c938
# encrypted=E3Ag6cRL2R+xytgw01i6tKSFpV7s7bKoiiWvPA1FYxM=
Unfortunately, when I try to decrypt this, I get error bad magic number:
$ echo "E3Ag6cRL2R+xytgw01i6tKSFpV7s7bKoiiWvPA1FYxM=" | openssl enc -aes-256-cbc -base64 -d -p -pass pass:topsecret
bad magic number
However, when I try this in terminal by running openssl enc command, it works:
$ echo 'my data' | openssl enc -aes-256-cbc -base64 -p -pass pass:topsecret
salt=8135837A305553F2
key=8B4373ABD786BAC107F4112640E95E920C77C017FCEC18E1BD919CED42F0298E
iv =910637CE50FADF27D944B7A8DD239E6D
U2FsdGVkX1+BNYN6MFVT8oWa5P/oxZFwzMk1DRCSSGg=
$ echo "U2FsdGVkX1+BNYN6MFVT8oWa5P/oxZFwzMk1DRCSSGg=" | openssl enc - aes-256-cbc -d -p -base64 -pass pass:topsecret
salt=8135837A305553F2
key=8B4373ABD786BAC107F4112640E95E920C77C017FCEC18E1BD919CED42F0298E
iv =910637CE50FADF27D944B7A8DD239E6D
my data
I think I tried every possible combination of generating key/IV from passphrase but I get error every time. Is anyone able to spot where is the problem with this way? I've spent entire day on this.

Can't reply to email via mutt with GnuPG - asks for "keyID"

I'm using mutt with GnuPG on Ubuntu. I have these general settings for GnuPG:
set pgp_decode_command = "gpg %?p?--passphrase-fd 0? --no-verbose --batch --output - %f"
set pgp_verify_command = "gpg --no-verbose --batch --output - --verify %s %f"
set pgp_decrypt_command = "gpg --passphrase-fd 0 --no-verbose --batch --output - %f"
set pgp_sign_command = "gpg --no-verbose --batch --output - --passphrase-fd 0 --armor --detach-sign --textmode %?a?-u %a? %f"
set pgp_clearsign_command = "gpg --no-verbose --batch --output - --passphrase-fd 0 --armor --textmode --clearsign %?a?-u %a? %f"
set pgp_import_command = "gpg --no-verbose --import -v %f"
set pgp_export_command = "gpg --no-verbose --export --armor %r"
set pgp_verify_key_command = "gpg --no-verbose --batch --fingerprint --check-sigs %r"
set pgp_list_pubring_command = "gpg --no-verbose --batch --with-colons --list-keys %r"
set pgp_list_secring_command = "gpg --no-verbose --batch --with-colons --list-secret-keys %r"
unset pgp_retainable_sigs
# set pgp_ignore_subkeys
# set pgp_verify_sig=yes
# set pgp_create_traditional = no
# set pgp_autosign = no
# set pgp_autoencrypt = no
# set pgp_replysignencrypted
# set pgp_replyencrypt = yes
# set pgp_replysign = yes
set crypt_autosign # automatically sign all outgoing messages
# set crypt_replysign # sign only replies to signed messages
# set crypt_autoencrypt=yes # automatically encrypt outgoing msgs
# set crypt_replyencrypt=yes # encryp only replies to signed messages
# set crypt_replysignencrypted=yes # encrypt & sign replies to encrypted msgs
set crypt_verify_sig=yes # auto verify msg signature when opened
set pgp_create_traditional = yes # http://www.rdrop.com/docs/mutt/manual236.html#pgp_create_traditional
set pgp_timeout = 3600
set pgp_good_sign = "^gpg: Good signature from"
And I have these settings for the specific account in question:
send-hook mark.nichols#gmial.com 'set pgp_autosign'
pgp-hook mark.nichols#gmail.com 53445200
set pgp_encrypt_only_command="/usr/lb/mutt/pgpewrap gpg --batch --quiet --no-verbose --output - --encrypt --textmode --armor --always-trust --encrypt-to 53445200 -- -r %r -- %f"
set pgp_encrypt_sign_command="/usr/lib/mutt/pgpewrap gpg --passphrase-fd 0 --batch --quiet --no-verbose --textmode --output - --encrypt --sign %?a?-u %a? --armor --always-trust --encrypt-to 53445200 -- -r %r -- %f"
set pgp_sign_as=53445200
If I send an email to someone who does not have a GPG key, the outgoing email is digitally signed using my key and is sent. When I get a reply to that email, and reply in turn, however, I cannot it as I asked:
Enter keyID for <recipient email>
I can get out of that dialog with Ctrl-G, but I cannot get past it. At first I thought it was one of these two setting:
# set crypt_replyencrypt=yes # encryp only replies to signed messages
# set crypt_replysignencrypted=yes # encrypt & sign replies to encrypted msgs
But even with them commented out I am asked for a key that doesn't exist. Where is the setting I am missing that is requiring I specify a key for the receiver on a reply?
Thanks
The answer is use GPGME. The GPGME library encapsulates most, if not all, commonly used GnuPG functions. With just a few configuration lines everything GnuPG related "just works." This posting is a good guide: https://sanctum.geek.nz/arabesque/gnu-linux-crypto-email/
In the end my setup now looks like:
# Use GPGME
set crypt_use_gpgme = yes
# Sign replies to signed emails
set crypt_replysign = yes
# Encrypt replies to encrypted emails
set crypt_replyencrypt = yes
# Encrypt and sign replies to encrypted and signed email
set crypt_replysignencrypted = yes
# Attempt to verify signatures automatically
set crypt_verify_sig = yes
# Use my key for signing and encrypting
set pgp_sign_as = 0x53445200
# Automatically sign all out-going email
set crypt_autosign = yes
Far simpler, and it works.

How to generate RSA1 signature from file with a passphrase

I need to generate a RSA1 signature for a string using file with a passphrase
ex:
require "openssl"
pri = OpenSSL::PKey::RSA.new( File.read("cert.prv" ) )
string = 'Some string'
sign = pri.sign( "sha1", string.force_encoding("utf-8") )
puts sign
cert.prv file has a passphrase 123456 if I run this script from console I input this passphrase by hand...
But if this is run from a script how can I pass passphrase to OpenSSL ?
Pass the passphrase while opening the file.
pri = OpenSSL::PKey::RSA.new(File.read("cert.prv"), '123456')

Resources