Encrypt private key with passphrase using openssl ruby - ruby

In order to encrypt my private key and provide the passphrase. Currently, I am using bash cmd to get my encrypted key user_enc.pem
( openssl rsa -in user.pem -pubout && openssl rsa -in user.pem -aes256 ) > user_enc.pem
It would be helpful if someone can have a look and help me out to achieve this by using ruby probably with openssl.

OpenSSL::Cipher, which belong to Ruby standard library, provides symmetric algorithms for encryption and decryption. To use it just require OpenSSL in Ruby:
require 'openssl'
You can create a cipher instance specifying the Cipher algorithm. For example:
cipher = OpenSSL::Cipher::AES256.new(:CBC)
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv
data = "Confidential data"
encrypted = cipher.update(data) + cipher.final
For more details, check Ruby documentation: https://ruby-doc.org/stdlib-2.5.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html

Related

Ruby OpenSSL pkcs8 .key to .pem

We run this command via Gem.
openssl pkcs8 -inform DER -in file_init.key -passin pass:secret -out file_key.pem
All works fine on shell, we wanna convert this line to openssl with ruby, we tried:
key_file = OpenSSL::PKey::RSA.new File.read('file_init'), 'secret'
puts key_file.to_pem_pkcs8
But we get:
`initialize': Neither PUB key nor PRIV key: nested asn1 error (OpenSSL::PKey::RSAError)
Any advice?
Ruby's openssl implementation doesn't handle pkcs8 encrypted der key-file.
There is a workaround (converting the der to PEM by base64 encoding the der + adding correct headers) here:
Load PKCS#8 binary key into Ruby

ruby openssl smime encryption

I am looking for ruby code to mimic below openssl smime encryption. I looked up other related topics but mostly were around signing. In snippet below cert.pem is a PEM-encoded X.509 certificate containing the PEM public key. token.txt file is the file to be encrypted.
openssl smime -encrypt -out encrypted.p7m -in token.txt cert.pem
Figured it.
token_plain_text = File.read("/path/to/token.txt")
cert = X509::Certificate.new("/path/to/cert.pem")
encrypted_object = OpenSSL::PKCS7.encrypt([cert], token_plain_text)
Got the encrypted string by outputting above object to string.
encrypted_str = encrypted_object.to_str

Generate OpenSSL subject_name hash in ruby

I've investigated 30261296 however I'm still at a loss to find a way to generate the same results in Ruby with the openssl and/or digest gems. The OpenSSL output I'm trying to replicate in ruby is as follows:
$ openssl x509 -noout -subject_hash -in DigiCertSHA2SecureServerCA.pem
85cf5865
In reading many things, I believe this hash is generated from the Subject: portion of the certificate, which is like the distinguished name. In this certificates case something to the effect of:
$ openssl x509 -noout -subject -in DigiCertSHA2SecureServerCA.crt
subject=C = US, O = DigiCert Inc, CN = DigiCert SHA2 Secure Server CA
Attempting to SHA-1 encode that on the command line or in Ruby (which represents this as /C=US,/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA when using the openssl gem) has not yeilded the same has results displayed by OpenSSL.
I'm trying to do this more natively in Ruby to avoid shelling out to openssl if possible since openssl and digest come along with the ruby env. In the end I need this to generate the hash directory tree ... i.e. 85cf5865.0 (hash + '.0').
The CA I'm hasing is DigiCertSHA2SecureServerCA.crt - DER encoded. I converted DER to PEM because openssl command line uses that without the additional -inform der switch. It doesn't appear to matter to Ruby's openssl gem.
This turns out to be pretty straightforward, since Ruby’s OpenSSL bindings includes the OpenSSL::X509::Name#hash method, which is exactly what we want.
require 'openssl'
# Read the certificate.
cert = OpenSSL::X509::Certificate.new(File.binread("DigiCertSHA2SecureServerCA.crt"))
# Get the subject, which is an OpenSSL::X509::Name object.
name = cert.subject
# hash returns an integer, we want the hex string so call to_s(16).
puts name.hash.to_s(16) #=> 85cf5865
The integer will be positive since OpenSSL returns an unsigned int, so we can just use to_s(16) and don’t need to worry about converting negative values.

Ruby code for openssl to generate fingerprint

I need the ruby equivalent of the following:
openssl x509 -sha1 -fingerprint -noout -in cert.pem
The code that I wrote is:
data = File.read("cert.pem")
data["-----BEGIN CERTIFICATE-----\n"]=""
data["-----END CERTIFICATE-----\n"]=""
OpenSSL::Digest::SHA1.new(Base64.encode64(data))
This code does not generate the same fingerprint as the openssl cli command does.
Any idea what I may be doing wrong?
As gtrig mentions, the OpenSSL command line builds the fingerprint by hashing the DER encoding of the certificate, not the Base64 PEM representation. You can parse this using pure OpenSSL:
file_data = File.read("cert.pem")
cert = OpenSSL::X509::Certificate.new(file_data)
puts OpenSSL::Digest::SHA1.new(cert.to_der).to_s
Shameless plug: r509 can also do this like so:
cert = R509::Cert.load_from_file("cert.pem")
puts cert.fingerprint('sha1')
If you need it to be in colon separated form you can just take the hash and do something like "fingerprint".scan(/../).map{ |s| s.upcase }.join(":")
Try Base64.decode64.
OpenSSL::Digest::SHA1.new(Base64.decode64(data))
Certificates in PEM format are Base 64 encoded versions of a binary DER format, so they need to be DEcoded before the SHA1 hash is taken.
Alternatively, you could convert the PEM file to DER format with OpenSSL like this:
openssl x509 -in cert.pem -out cert.der -outform der
Then your Ruby code would look like this:
data2 = File.read("cert.der")
print OpenSSL::Digest::SHA1.new(data2)
Either way works.

OpenSSL not enough data

I've generated a self signed certificate using Adobe X, and exported a pfx file (for my private key) along with a .cer file (for the certificate).
I then try to collect the certificate, along with the key, but for some reason, OpenSSL is giving the error
OpenSSL::X509::CertificateError: not enough data
Here is my code
require 'openssl'
CERTFILE = "test.cer"
RSAKEYFILE = "test.pfx"
# Open certificate files
cert = OpenSSL::X509::Certificate.new(File.read CERTFILE)
key = OpenSSL::PKey::RSA.new(File.read RSAKEYFILE )
My certificate was generated using Adobe X reader, and is a self-signed certificate. It is working fine to sign pdf documents...
What might i do to make this work?
Apparently OpenSSL has some problems reading directly from .cer files, and for the key, we should use only the private_key, and the pfx has both the privatekey and the cert.
So, i installed openSsl locally, and first converted my .cer certificate to .pem with the following command :
C:\OpenSSL-Win32\bin>openssl x509 -inform der -in "c:\mydir\test.cer" -out "C:\mydir\certificate.pem"
and then extracted my privatekey from the pfx file (based on this site) :
C:\OpenSSL-Win32\bin>openssl pkcs12 -in "c:\mydir\test.pfx" -nocerts -out "c:\mydir\test_pk.pem"
just make sure you have your pfx pwd and select a passphrase when you extract the privatekey.
Here is the final code :
require 'openssl'
CERTFILE = "certificate.pem"
RSAKEYFILE = "test_pk.pem"
passphrase = "your chosen passphrase for the private key"
key4pem=File.read RSAKEYFILE
# Open certificate files
cert = OpenSSL::X509::Certificate.new(File.read CERTFILE)
key = OpenSSL::PKey::RSA.new key4pem, passphrase
And voilá :-), we have successfully mapped into memory both our certificate and privatekey, and can put it to uses like the answer here
While trying to create an OpenSSL::X509::Certificate object from '.cer', I found this error:
OpenSSL::X509::CertificateError (not enough data)
I checked that file was actually a DER-encoded certificate which is in binary format. In that case, we should read the file contents by File.binread.
To check if the file is PEM or DER encoded? We can use the following code:
require "open3"
require "openssl"
def pem_cert?(file)
details, status = Open3.capture2e("file", file)
return false unless status.success?
details.rpartition(":").last.strip == "PEM certificate"
end
contents = if pem_cert?(cer_file_path)
File.read(cer_file_path)
else
File.binread(cer_file_path)
end
OpenSSL::X509::Certificate.new(contents)
This is a pure ruby way, without any shell interaction.

Resources