Decrypt PKCS7 in Ruby on Rails - ruby

I try to decrypt PKCS7 data, but always receive error OpenSSL::PKCS7::PKCS7Error - wrong content type on method "pkcs7.decrypt(key, cer)".
example:
cer = OpenSSL::X509::Certificate.new(File.read("path/cert.cer"))
key = OpenSSL::PKey::RSA.new(File.read("path/private.key"), "111111")
crypted_data = File.read('path/pkcs7') # contains xml-file and signature "-----BEGIN PKCS7----- ... -----END PKCS7-----"
pkcs7 = OpenSSL::PKCS7.new(crypted_data)
decrypted_data = pkcs7.decrypt(key, cer) # error is here
need help! how to get data from the encrypted string?
updated:
on bash I can solve this task by following code:
openssl smime -verify -noverify -inform PEM -nointern -certfile "path/cert.cer" -CAfile "path/cert.cer" < path/pkcs7
returns xml file.

Solution founded!!!
cert_store = OpenSSL::X509::Store.new
my_cert = OpenSSL::X509::Certificate.new(File.read("remote.cer"))
signature = OpenSSL::PKCS7.new(File.read('pkcs7-resp.file'))
signature.verify([my_cert], cert_store, nil, OpenSSL::PKCS7::NOVERIFY)
signature.data

Related

openssl req utf8 strings

as others i'm struggling with utf8 in requests. sure i went through all preciding questions and answers. well here is my workout.
configuration:
[ req ]
default_bits = 2048
default_md = sha256
string_mask = utf8only
utf8 = yes
distinguished_name = DistName
attributes = Attr
[ DistName ]
commonName = Name (your official name)
commonName_default = hans nägli dödüé
stateOrProvinceName = State or Province Name (full name)
countryName = Country Code (2 letter code)
countryName_default = CH
[ Attr ]
subjectAltName = Name (international / latin)
subjectAltName_default = hans nägeli dädü
postalCode = zip code
localityName = Locality Name (eg, city)
emailAddress = Email Address
i verified this config file is utf8 and started thereafter using default values:
openssl req -verbose -config CsrPerson.cfg -new -key HansMuster-ecpem.key -passin pass:gugus -out HansMuster-pem.csr
i then had a look into the request using
openssl req -utf8 -noout -text -in HansMuster-pem.csr
showing
Certificate Request:
Data:
Version: 1 (0x0)
Subject: CN = hans n\C3\A4gli d\C3\B6d\C3\BC\C3\A9, ST = z\C3\BCrich, C = CH
Subject Public Key Info:
...snip...
NIST CURVE: P-384
Attributes:
postalCode :2222
localityName :Dörrhausen
X509v3 Subject Alternative Name:hans nägeli dädü
emailAddress :hans.naegeli#gmx.net
...snip...
using same source, same configuration, same procedure: subject/distinguished name has not been treated as utf8 but worked out well with attributes even with locality name which was entred by keyboard.
ASN1 definition of subject show a possible content as utf8 string. openssl version OpenSSL 1.1.0g 2 Nov 2017.
where have i been mistaken?
have fun
You can try the following:
openssl req -nameopt utf8 -noout -text -in HansMuster-pem.csr
This should only fix the display problem, you have to check if your other requirements are still met.
You can find more details about the "-nameopt" options here in the "NAME OPTIONS" section: https://www.openssl.org/docs/man1.0.2/man1/x509.html
Other related links:
How to create CSR with utf8 subject in openssl?

Verify signed data vault offline

I'm sending to a client a public_key and a payload plus a signature.
I'm trying to verify the signature on the client using the public_key and payload but I can't find a way to get the data verified. Every time I receive false as a response regardless of what I'm doing.
I'm using vault to sign the payload
def sign(box_identifier, data) do
Vaultex.Client.write("transit/sign/#{box_identifier}",
%{
input: data,
hash_algorithm: "sha2-256"
},
#authentication_strategy,
{#token})
end
Vault.Transit.sign("coucou", Base.encode64("test"))
_____________________________________________________________
digest = OpenSSL::Digest::SHA256.new
key = OpenSSL::PKey::RSA.new File.read 'key.pem'
key.public_key.verify digest, signature, "test"
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzj0qLxEc0Qu9g9nxdMRe
jBaUD0+GuQITiAPEDOrjScJTznJrR9hXqO14BqepuEmcz4irv4hEkBEBfqZ1XnP9
2fc9zG4A20lepqDRwPhkEdI4D71KRPSxv/a+O2HrAhTYH17NbsYDtpkFCdepC6FC
01aso679d3kAZiZ+GD2OLDWifreBVPE2aXacJYXZZ4kTkchsevY3PnAcOG4LmM6b
kUoF1qfP6tJ/VItJXyqSC2PI9Io28zFhwOf6cPLEQCBCTNCNwHunqHW8olcE/Xfn
b2toym0/UvW3kH/P4h+TE1ZoCV5FWwcx9hcAy1TC0zm4D5Xwt0/4Pgj1GiXFGOY4
WKGyuDK8gs7QsSDqu+B5p0xiDg7226bpplVxR/P87CohYTtYZj/lO03G2ZEj3pCr
/juxThdzTgO5xUcPV5GFbPjlK1TIIb5XVZZzW6+sviB6cYZ5T/Xp9dtbR8G9Zt9n
gLlaKU7U/DDQxRv2uiCZy5U/DUpfxY56r4Y73Ir2YgmZY1PKLC2a5/w3wScVZILN
fnxnVYOzSPPaKxAJWbZsZjxXJS0veE8RgGFHgWfe8+qPCEnx81Jf2NzupQHO1KIk
UnlYGcPESk/90psDNsmISSdtF2D6j4k28k0ncViTu2eMKBX81W8TgTeHtQa3zR0S
upN2o25b3Wi2oQU14kTdOCcCAwEAAQ==
-----END PUBLIC KEY-----
Am I even capable of validating the signature offline when its signed by vault ?
Yes, it's possible to verify the signature offline. My answer is going to mention some Ruby specifics, since I didn't notice this was Elixir until after I started researching a bit more.
By default, when signing with a RSA key, Vault uses the PSS algorithm for the signature. The verify method on OpenSSL::PKey::PKey is expecting PKCS #1 v1.5. Here's some useful info about the pros and cons of the two.
In addition to this algorithm difference, you need to strip off the vault:v1: from the returned signature from Vault.
Here's some sample Ruby code to sign and then verify the signature:
transit_key = "test_key"
message = "test"
# Returns something like:
# vault:v1:B3reNpf8e/WyAYzBzyWz3oSUM...
signature = Vault.logical.write(
"transit/sign/#{transit_key}/sha2-256",
input: Base64.encode64(message),
signature_algorithm: "pkcs1v15"
).data[:signature]
signature = signature.split(":")[2]
# Gives us the PEM encoded public key
# -----BEGIN PUBLIC KEY-----
# ...
public_key = Vault.logical.read("transit/keys/#{transit_key}").data[:keys][:"1"][:public_key]
public_key = OpenSSL::PKey::RSA.new(public_key)
digest = OpenSSL::Digest::SHA256.new
puts public_key.verify(digest, Base64.decode64(signature), message) # returns true
puts public_key.verify(digest, Base64.decode64(signature), message + "modified") # returns false
Looks like as of Ruby 2.5, there's a new verify_pss method on the OpenSSL::PKey::RSA class.
signature = Vault.logical.write(
"transit/sign/#{transit_key}/sha2-256",
input: Base64.encode64(message),
signature_algorithm: "pss"
).data[:signature].split(":")[2]
puts public_key.verify_pss(digest, Base64.decode64(signature), message, salt_length: :auto, mgf1_hash: "SHA256") # returns true
puts public_key.verify_pss(digest, Base64.decode64(signature), message + "modified", salt_length: :auto, mgf1_hash: "SHA256") # returns false

How to generate csr and crt files using openssl in ruby

So i'm using AWS Iot and have a server running ruby on rails in the backend. i need to generate some certs for the client and the example on the AWS website only provides a way to do it using the openssl command line. If possible i would like to use the open ssl library in ruby to do this to avoid making ruby run commands in the terminal which may cause issues.
These are the commands i want to replicate using ruby
openssl genrsa -out deviceCert.key 2048
openssl req -new -key deviceCert.key -out deviceCert.csr
openssl x509 -req -in deviceCert.csr -CA sampleCACertificate.pem -CAkey sampleCACertificate.key -CAcreateserial -out deviceCert.crt -days 99999 -sha256
The first line i found and think i can do
require 'openssl'
rsa_key = OpenSSL::PKey::RSA.new(2048)
but i'm stuck on the last 2 lines. Any ideas?
Here is an example to generate a self signed certificate.
require 'rubygems'
require 'openssl'
key = OpenSSL::PKey::RSA.new(1024)
public_key = key.public_key
subject = "/C=BE/O=Test/OU=Test/CN=Test"
cert = OpenSSL::X509::Certificate.new
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
cert.not_before = Time.now
cert.not_after = Time.now + 365 * 24 * 60 * 60
cert.public_key = public_key
cert.serial = 0x0
cert.version = 2
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = cert
cert.extensions = [
ef.create_extension("basicConstraints","CA:TRUE", true),
ef.create_extension("subjectKeyIdentifier", "hash"),
# ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
]
cert.add_extension ef.create_extension("authorityKeyIdentifier",
"keyid:always,issuer:always")
cert.sign key, OpenSSL::Digest::SHA1.new
puts cert.to_pem
Source for Example
was able to eventually figure it out using this rdoc example
http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/X509/Certificate.html#method-c-new

Decrypting salted AES file generated on command line with Ruby

I would like to decrypt a text file within a ruby 2.1 script which was previously encrypted using OpenSSL's commandline tools:
openssl enc -aes-256-cbc -a -salt -in my_file
As seen in the command, the file is AES-256-CBC encrypted, salted and base64 encoded.
The password is known, but not the IV nor the key, which are required to follow this code snippet, taken from the ruby documentation:
decipher = OpenSSL::Cipher::AES.new 256, :CBC
decipher.decrypt
decipher.key = key
decipher.iv = iv
plain = decipher.update(encrypted_text) + decipher.final
While trying to find an answer, I found the gem AESCrypt gem which supposedly simplifies en- and decrypting, yet the currently released version is not compatible with ruby 2.1.
Looking at it's source code, I found that the key was retrieved by digesting the password, and the IV is just left as nil.
So I tried to get the following running:
encoded_and_encrypted_text = File.read my_file_path
encrypted_text = Base64.decode64 encoded_and_encrypted_text.to_s.strip
decipher = OpenSSL::Cipher::AES.new 256, :CBC
decipher.decrypt
decipher.key = OpenSSL::Digest::SHA256.new(my_password).digest
plain_text = decipher.update(encrypted_text) + decipher.final
But this results in OpenSSL::Cipher::CipherError: bad decrypt.
Do I need to somehow specifically handle that the file is salted? I have read in the OpenSSL documentation for the enc function that the IV, if not specified while encrypting the file, is generated from the password. Do I need to manually reconstruct the IV somehow?
Any advice would be highly appreciated :)
OpenSSL uses a custom header and key derivation routine. Security.SE has a good description of the header and the docs for EVP_BytesToKey describe the key derivation.
We can modify your code to use this weird and somewhat broken key derivation as follows:
encoded_and_encrypted_text = File.read my_file_path
encrypted_text = Base64.decode64 encoded_and_encrypted_text.to_s.strip
header = encrypted_text[0,8]
salt = encrypted_text[8,8]
payload = encrypted_text[16..-1]
decipher = OpenSSL::Cipher::AES.new 256, :CBC
decipher.decrypt
D_1 = OpenSSL::Digest::MD5.new(my_password + salt).digest
D_2 = OpenSSL::Digest::MD5.new(D_1 + my_password + salt).digest
D_3 = OpenSSL::Digest::MD5.new(D_2 + my_password + salt).digest
decipher.key = (D_1 + D_2)
decipher.iv = D_3
plain_text = decipher.update(payload) + decipher.final

Which privatekey format is this?

I've a private key made with OpenSSL and encrypted with RSA like this:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQILJ8rNTk54VICAggA
MBQGCCqGSIb3DQMHBAiXuThuf12hMwSCBMhrm8H2j7yzLBOIvSWbLoyUmIn7e9zA
6PLDzj1dos+h+wAkPN0IgJlkdoBBXlnF7tzGdoctOYOKiu/p+HLzMnvHaDfNSFwz
5zx4yyZsKtf90gT6IVuJy7TMiLK3gk4gdZJc820OeRr1FjdRV3iVpTXbVfpwG/B3
IbV0PX3ZaHeR1m8vWI0/XQB7ZL7lLT+3MO66X/PAMTPA9joFMxuPFasIb2uXSqe/
CJNOyY4iW89uYHFiOe53e4ggUdT/rupoR4OSNKKEH5f1y+FIuH1xNvlmi14hJn+k
KOOi89vVIcvh4abCM+jhYwpPOGB8Sz2bAXsYocgZPvKtKakHlis7RcSsd5Usx9Nt
tgQ=
-----END ENCRYPTED PRIVATE KEY-----
And I've stored it into a user.key file.
Actually I'm implementing a CMS encrypt and decrypt project in objective-c. Now I'm trying to decrypt my encrypted container file using certificates, private key, and so on...
I'm a little bit confused which method from OpenSSL I should choose to load the aforementioned key into an EVP_PKEY object.
It suggests me the following formats:
FORMAT_ASN1
FORMAT_PEM
FORMAT_PKCS12
FORMAT_MSBLOB
FORMAT_PVK
I know that it's not PEM and PKCS12. I would guess ASN1 or PVK but I really don't know and I haven't found any further information on this in the OpenSSL docu.
Can anyone tell me please which format the aforementioned key has?
EDIT:
To specify my question a bit more: I have the aforementioned key loaded into a BIO. I can read this BIO with BIO_read(keybio, ...) successfully and it contains the key (in ascii). Now I want to get an EVP_PKEY from this BIO.
I've tried...
EVP_PKEY *key = PEM_read_bio_PrivateKey(keybio, NULL, 0, NULL);
... but this doesn't work. When I call this it asks me to enter the passphrase in the console and when I do this, the key is NULL. :-/
The key is a PKCS#8 EncryptedPrivateKeyInfo wrapped in a PEM-container.
You can do something like this to get it into an EVP_PKEY-object:
// This must be called once:
OpenSSL_add_all_algorithms();
BIO* bio_err = BIO_new_fp (stderr, BIO_NOCLOSE);
X509_SIG* p8 = PEM_read_bio_PKCS8(in, NULL, NULL, NULL);
if(p8==NULL){
BIO_printf(bio_err, "Error decoding key\n");
ERR_print_errors(bio_err);
return;
}
PKCS8_PRIV_KEY_INFO* p8inf = PKCS8_decrypt(p8, password, strlen(password));
if(p8inf==NULL){
BIO_printf(bio_err, "Error decrypting key\n");
ERR_print_errors(bio_err);
return;
}
EVP_PKEY* pkey = EVP_PKCS82PKEY(p8inf);
if(pkey==NULL){
BIO_printf(bio_err, "Error converting key\n");
ERR_print_errors(bio_err);
return;
}
That (extended) PEM format is used by OpenSSL for PKCS#8 encoded private keys.

Resources