openssl equivalent command in ruby - ruby

I have to convert certificate file(pem format) into pfx using private key. The command perfectly works in linux is
openssl pkcs12 -export -out certificate1.pfx -inkey myPrivateKey.key -in myCert.pem
Can anyone help me to write equivalent code in ruby using ruby-openssl.

Should be easy:
#!/usr/bin/env ruby
# export-der.rb
require 'openssl'
def export_der(pass, key, cert, out)
key = OpenSSL::PKey.read File.read(key)
cert = OpenSSL::X509::Certificate.new File.read(cert)
name = nil # not sure whether this is allowed
pkcs12 = OpenSSL::PKCS12.create(pass, name, key, cert)
File.open(out, 'w'){|f| f << pkcs12.to_der }
end
puts 'Password:'
export_der($stdin.read, *ARGV)
And call it this way (untested ;-)):
$ ruby export-der.rb myPrivateKey.key myCert.pem certificate1.pfx

Related

openSSL encrypt with CLI, decrypt with ruby not working

I'm using the openSSL CLI tool to encrypt data, and I want to decrypt that data on another machine in my ruby application. I'm unable to decrypt the data and I'm not sure what isn't lining up between the two. I'd really appreciate any help, below is what I'm running:
CLI Tool
$ export PASS=EncryptDecryptSuperSecretKeyNoOne123456789123456789
$ echo 'someTextIWantToEncrypt' | openssl enc -aes-256-ecb -base64 -e -k $PASS
:> U2FsdGVkX18Gboawkim6n6Ps/yssGaOZkdb1e6I4VyOZDUcqEh2uYdT8jxUydplX
Ruby Application
require 'openssl'
require 'base64'
module Aes
KEY = "EncryptDecryptSuperSecretKeyNoOne123456789123456789"
ALGORITHM = 'AES-256-ECB'
def self.decrypt(msg)
begin
cipher = OpenSSL::Cipher.new(ALGORITHM)
cipher.decrypt
cipher.key = KEY
tempkey = Base64.decode64(msg)
crypt = cipher.update(tempkey)
crypt << cipher.final
return crypt
rescue Exception => exc
puts ("Message for the decryption log file for message #{msg} = #{exc.message}")
end
end
end
When I call Aes.decrypt("U2FsdGVkX18Gboawkim6n6Ps/yssGaOZkdb1e6I4VyOZDUcqEh2uYdT8jxUydplX") it raises an error bad decrypt
What am I missing or not including? ECB does not require an IV. Is there something else?

Encrypt file with Ruby OpenSSL and decrypt with command line

I am automating an OpenSSL file encryption process using Ruby. The files encrypted this way need to be able to be decrypted using OpenSSL on linux command line.
I can encrypt the file using the following Ruby method:
def encrypt_file
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.encrypt
cipher.key = "somelongkeystring"
buf = ""
File.open("file.enc", "wb") do |outf|
File.open("file.zip", "rb") do |inf|
while inf.read(4096, buf)
outf << cipher.update(buf)
end
outf << cipher.final
end
end
end
I need to be able to decrypt the file (file.enc) using the following command:
$ openssl aes-256-cbc -d -in file.enc -out file.zip
However, when I run this I get an error for bad magic number after I type the key from above.
Since I cannot change the decrypt approach (meaning it uses only a password and is entered on linux command line) how can I change my Ruby method to encrypt the file so it can be decrypted this way?
With the help of a similar question in stackoverflow, Im able to achieve it in ruby.
First u've to add an iv thing into ur cipher object, like this
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.encrypt
cipher.iv = "0"*16
cipher.key = "somelongkeystring"
While decryption u've to pass -K and -iv values
openssl aes-256-cbc -d -in file.enc -out file.zip -K <key_hex_code> -iv <iv_hex_code>
U can generate hex codes in ruby like this "string".unpack('H*'). There must be some way to generate hex codes in cli as well.
A solution based on Sumit's answer:
require 'openssl'
class Crypto
def initialize(key, iv, data, cipher='aes-256-cbc')
#key = key
#iv = iv
#cipher = cipher
#data = data
end
def encrypt
c = OpenSSL::Cipher.new(#cipher).encrypt
c.iv = #iv
c.key = #key
c.update(#data) + c.final
end
def decrypt
c = OpenSSL::Cipher.new(#cipher).decrypt
c.iv = #iv
c.key = #key
c.update(#data) + c.final
end
end
iv = '0'*16
key = '1'*32
message = 'My Message'
encrypted = Crypto.new(key,iv,message).encrypt
puts Crypto.new(key,iv,encrypted).decrypt
puts `echo -n '#{encrypted}' | openssl aes-256-cbc -d -K #{key.unpack('H*').first} -iv #{iv.unpack('H*').first}`
# My Message
# My Message
This solutions works for strings, can easily be adapted to files.
I got this to work though not using the Ruby OpenSSL implementation (which I would prefer). If you use the -k flag you can specify a password as the next argument then you don't need to pass anything and can use system to call it.
system("openssl aes-256-cbc -k '#{key}' -in file.zip -out file.enc")
If anyone has an approach using the Ruby OpenSSL implementation I would appreciate it.

How to encrypt files with Ruby?

I need to write a simple tool which encrypts/decrypts files.
I guess the best way is to use OpenSSL:
Generate a key:
openssl rand -base64 2048 > secret_key
Encrypt a file:
openssl aes-256-cbc -a -e -in file -out file.enc -k secret_key
Decrypt a file:
openssl aes-256-cbc -d -in file.enc -out file -k secret_key
Is there an easy way to implement this in Ruby? Is there a better way to do that? Using PGP maybe?
Ruby's OpenSSL is a thin wrapper around OpenSSL itself and provides almost all the functionality that OpenSSL itself does, so yes, there's a one-to-one mapping for all your examples:
openssl rand -base64 2048 > secret_key
That's actually exaggerated, you are using AES-256, so you only need a 256 bit key, you are not using RSA here. Ruby OpenSSL takes this decision off your shoulders, it will automatically determine the correct key size given the algorithm you want to use.
You are also making the mistake of using a deterministic IV during your encryption. Why? Because you don't specify an IV at all, OpenSSL itself will default to an IV of all zero bytes. That is not a good thing, so I'll show you the correct way to do it, for more information have a look at the Cipher documentation.
require 'openssl'
# encryption
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv
buf = ""
File.open("file.enc", "wb") do |outf|
File.open("file", "rb") do |inf|
while inf.read(4096, buf)
outf << cipher.update(buf)
end
outf << cipher.final
end
end
# decryption
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.decrypt
cipher.key = key
cipher.iv = iv # key and iv are the ones from above
buf = ""
File.open("file.dec", "wb") do |outf|
File.open("file.enc", "rb") do |inf|
while inf.read(4096, buf)
outf << cipher.update(buf)
end
outf << cipher.final
end
end
As you can see, encryption and decryption are fairly similar, so you can probably combine the streaming reading/writing into one shared method and just pass it a properly configured Cipher plus the corresponding file names, I just stated them explicitly for the sake of clarity.
If you'd like to Base64-encode the key (and probably the IV, too), you can use the Base64 module:
base64_key = Base64.encode64(key)
Ruby has an OpenSSL library that should take care of the heavy lifting.

Difference in blowfish encryption between perl and ruby

Why is there a difference in blowfish encryption between Crypt::CBC (perl) and OpenSSL (ruby)?
Perl
use Crypt::CBC;
my $cipher = Crypt::CBC->new( -key => 'length32length32length32length32', -cipher => 'Blowfish' );
my $ciphertext = $cipher->encrypt_hex('test');
# ciphertext is 53616c7465645f5f409c8b8eb353823c06d9b50537c92e19
Ruby
require "rubygems"
require "openssl"
cipher = OpenSSL::Cipher::Cipher.new("bf-cbc")
cipher.encrypt
cipher.key = "length32length32length32length32"
result = cipher.update("test") << cipher.final
ciphertext = result.unpack("H*").first
# ciphertext is 16f99115a09e0464
Crypt::CBC seems to be prepending Salted__ to the output by default. Can you explain what is going on that is so different between these? Is there a way to make OpenSSL behave in a similar way to Crypt::CBC?
Crypt::CBC (perl) uses its own method to randomize the salt and initialization vector. Plus in the case of Blowfish it uses a key length of 56 as mentioned above.
Using the perl code from your example:
Perl
use Crypt::CBC;
my $cipher = Crypt::CBC->new( -key => 'length32length32length32length32', -cipher => 'Blowfish' );
my $ciphertext = $cipher->encrypt_hex('test');
# 53616c7465645f5f409c8b8eb353823c06d9b50537c92e19
To decode this using ruby (OpenSSL) requires a little tweaking to extract the key and initialization vector:
Ruby
require 'openssl'
# Hex string to decode(from above)
string = '53616c7465645f5f409c8b8eb353823c06d9b50537c92e19'
# Pack Hex
string = [string].pack('H*')
# Some Config
pass = 'length32length32length32length32'
key_len = 56;
iv_len = 8;
desired_len = key_len + iv_len;
salt_re = /^Salted__(.{8})/
#Extract salt
salt = salt_re.match(string)
salt = salt.captures[0]
data = '';
d = '';
while (data.length < desired_len)
d = Digest::MD5::digest("#{d}#{pass}#{salt}");
data << d;
end
#Now you have extracted your key and initialization vector
key = data.slice(0..key_len-1)
iv = data.slice(key_len .. -1)
# Trim string of salt
string = string[16..-1]
cipher = OpenSSL::Cipher::Cipher.new "bf-cbc"
cipher.decrypt
cipher.key_len = key_len
cipher.key = key
cipher.iv = iv
puts cipher.update(string) << cipher.final
# test
Turns out the blowfish key size defaults are different between these two. OpenSSL defaults to 16, Crypt::Blowfish defaults to 56.
You can override key size in Crypt::CBC by specifying -keysize => n, but unfortunately there does not appear to be a way to override the key size in OpenSSL.
The Openssl library defaults to 16 byte Blowfish key sizes, so for
compatibility with Openssl you may wish to set -keysize=>16
http://metacpan.org/pod/Crypt::CBC
Perl (specify keysize)
my $cipher = Crypt::CBC->new(
-key => 'length32length32length32length32',
-keysize => 16,
-cipher => 'Blowfish'
);

Ruby Generate Self-Signed Certificate

I'm trying to generate a self-signed certificate in ruby, but am running into trouble. This is what I currently have right now:
require 'openssl'
if ARGV.length != 3 then
puts "USAGE: #{__FILE__} <type[der|pem]> <private-out> <public-out>"
exit
end
type = ARGV[0].downcase
privateKeyFile = ARGV[1]
publicKeyFile = ARGV[2]
values = [{ 'C' => 'US'},
{'ST' => 'SomeState'},
{ 'L' => 'SomeCity'},
{ 'O' => 'Organization'},
{'OU' => 'Organizational Unit'},
{'CN' => "somesite.com"}]
name = values.collect{ |l| l.collect { |k, v| "/#{k}=#{v}" }.join }.join
key = OpenSSL::PKey::RSA.generate(1024)
pub = key.public_key
ca = OpenSSL::X509::Name.parse(name)
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 1
cert.subject = ca
cert.issuer = ca
cert.public_key = pub
cert.not_before = Time.now
cert.not_before = Time.now + (360 * 24 * 3600)
File.open(privateKeyFile + "." + type, "w") {|f| f.write key.send("to_#{type}") }
File.open(publicKeyFile + "." + type, "w") {|f| f.write cert.send("to_#{type}") }
When I try to use the generated private key and certificate in apache, I get this error:
[Thu Mar 04 10:58:44 2010] [error] Init: Unable to read server certificate from file /etc/ssl/certs/gnarly.pem
[Thu Mar 04 10:58:44 2010] [error] SSL Library Error: 218529960 error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
[Thu Mar 04 10:58:44 2010] [error] SSL Library Error: 218595386 error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error
This is what my certificate says:
-----BEGIN CERTIFICATE-----
<lots of stuff>
-----END CERTIFICATE-----
It calls itself a certificate instead of a CSR, which is what most of the things I've found online say about that apache2 error (that I might have gotten the CSR and CERT mixed up). My guess is that I'm not generating the right type of certificate. Maybe I have to change the serial or version attributes. Also, I'm not doing any self-signing anywhere, not that I know of anyways. I know you can do something like this though:
require "openssl"
key = OpenSSL::PKey::RSA.generate(1024)
signature = key.sign(OpenSSL::Digest::SHA1.new, "data to sign")
Reminder: My goal is to generate a self-signed certificate, in case my long-ish question lost focus on the way.
EDIT: I guess the real question is how to sign a certificate with a key
I created a helper class for this from code I lifted directly from nickyp's gist that I found on a Google search. The only dependency you need is the openssl gem (gem install openssl)
require 'openssl'
class SelfSignedCertificate
def initialize
#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
end
def self_signed_pem
#cert.to_pem
end
def private_key
#key
end
end
Usage:
my_cert = SelfSignedCertificate.new
puts "Private Key:\n#{my_cert.private_key}"
puts "Self-signed PEM:\n#{my_cert.self_signed_pem}"
Output:
Private Key:
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDTtjPd3X9KX9BZpXKS82tM74Bs/hXsSLgnkitrc+oR4oF5PVko
NZL3j51gkX3jJRSG9tNPQC5NVR+5h7tXPxU5TAQZl6MUiV+YWuRng98GeCjP3ePp
meSStsKEMUiZI8YLVWrdbIjS+Q+lZnYMffeEOAoMSaei9hR4rOX0i+9hdwIDAQAB
AoGBALdAc/6sFd0zuC2Qhu7p4kvS11AAUsuWWkhuPkUhLU9TxwxBbOXgEZlVZzzK
UrQFSZJVHazweeOYNgCqmx82zE+cB4YzRLqkCPUD9t1bZcgk31tV39MSrC9CDKCB
inUTMKflPbHL0B+Lq24S8KfuW9bOPofhspjlV7cZCX5adFiBAkEA7KOMkiQMyq9X
ZVeRzJU0LmVdjrb7UBD5NebV+KaN8O7q+W4FG0nihcNj7xt2fZnvKM4FMfRwDP3G
HRUfR0alQQJBAOUIjKXYyoUsk+tLASoYLX+uPocjd7YSB9UPK2lFxqHOzekAlynF
u1JWEDPOjZNtNHmsQKOp5AWTUnm33JxfQLcCQByY5zQCB0m3RuiIXKZMobG5rkTA
+D4EzxkkfFdASYcEWIEsOpHBrA5ePoV23Crxn2VfAGG5GJF5WafKFa2XbAECQFL/
5Ch+BfZ5DynnxoZAuMxakuJaYhmjMx9tHehKlw8waMKVqjJDK/1MnxaHNhtFKg0l
9U7aVH4Iw4zEqrgodMUCQQCWZEUepSGoRVs1YDtag4FKSTMGXcnI/jllJmxHQhf4
uiy/8Hb+FW49w3KO1zeq7WdXw7W7Q1uO94npYX5p535d
-----END RSA PRIVATE KEY-----
Signed PEM:
-----BEGIN CERTIFICATE-----
MIICgjCCAeugAwIBAgIBADANBgkqhkiG9w0BAQUFADA6MQswCQYDVQQGEwJCRTEN
MAsGA1UECgwEVGVzdDENMAsGA1UECwwEVGVzdDENMAsGA1UEAwwEVGVzdDAeFw0x
NDA4MjEwMTI1MTZaFw0xNTA4MjEwMTI1MTZaMDoxCzAJBgNVBAYTAkJFMQ0wCwYD
VQQKDARUZXN0MQ0wCwYDVQQLDARUZXN0MQ0wCwYDVQQDDARUZXN0MIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQDTtjPd3X9KX9BZpXKS82tM74Bs/hXsSLgnkitr
c+oR4oF5PVkoNZL3j51gkX3jJRSG9tNPQC5NVR+5h7tXPxU5TAQZl6MUiV+YWuRn
g98GeCjP3ePpmeSStsKEMUiZI8YLVWrdbIjS+Q+lZnYMffeEOAoMSaei9hR4rOX0
i+9hdwIDAQABo4GXMIGUMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNNbdqck
QT/B5hdQqimtW1Wnf+fmMGIGA1UdIwRbMFmAFNNbdqckQT/B5hdQqimtW1Wnf+fm
oT6kPDA6MQswCQYDVQQGEwJCRTENMAsGA1UECgwEVGVzdDENMAsGA1UECwwEVGVz
dDENMAsGA1UEAwwEVGVzdIIBADANBgkqhkiG9w0BAQUFAAOBgQB80KzzhkXCgJ0s
1zXJXuocNDU5v0Z42ditNX9jS8pXuhHwcQbx7PVfOieO3GHC5YzzgMHGR3i2U2CQ
rz9hP937ERxCfqpxhfMAD3Q+3rHsdGdNIauzzFb6XoXsM7koRnM27I6qvO3bamcz
AVGH5eLic9IjZTQbZizFzNoR+H2N/g==
-----END CERTIFICATE-----
There is a create_self_signed_cert method in webrick/ssl, which is easy to understand and useful.
I've since found several very good sources for examples using OpenSSL:
http://snippets.dzone.com/posts/show/6309
http://projects.reductivelabs.com/projects/puppet/repository/revisions/master/entry/lib/puppet/sslcertificates.rb
http://projects.reductivelabs.com/projects/puppet/repository/revisions/master/entry/lib/puppet/sslcertificates/ca.rb
http://projects.reductivelabs.com/projects/puppet/repository/revisions/master/entry/lib/puppet/sslcertificates/certificate.rb
I still haven't found any good documentation for this yet, although I don't think it would take too long to write down what's in the examples.
I've also figured out how to do what I wanted from the puppet source code. Hope this helps someone else who's frustrated at the lack of documentation of OpenSSL in ruby.

Resources