Opening an RSA private key from Ruby - ruby

I think I know how to create custom encrypted RSA keys, but how can I read one encrypted like ssh-keygen does?
I know I can do this:
OpenSSL::PKey::RSA.new(File.read('private_key'))
But then OpenSSL asks me for the passphrase... How can I pass it to OpenSSL as a parameter?
And, how can I create one compatible to the ones generated by ssh-keygen?
I do something like this to create private encrypted keys:
pass = '123456'
key = OpenSSL::PKey::RSA.new(1024)
key = "0000000000000000#{key.to_der}"
c = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
c.encrypt
c.key = Digest::SHA1.hexdigest(pass).unpack('a2' * 32).map {|x| x.hex}.pack('c' * 32)
c.iv = iv
encrypted_key = c.update(key)
encrypted_key << c.final
Also, keys generated by OpenSSL::PKey::RSA.new(1024) (without encryption), don't work when I try password-less logins (i.e., I copy the public key to the server and use the private one to login).
Also, when I open an ssh-keygen file via OpenSSL and then check its contents, it appears to have additional characters at the beginning and end of the key. Is this normal?
I don't really understand some of this security stuff, but I'm trying to learn. What is it that I'm doing wrong?

According to the blog post here:
http://stuff-things.net/2008/02/05/encrypting-lots-of-sensitive-data-with-ruby-on-rails/
You can simply do:
OpenSSL::PKey::RSA.new(File.read('private_key'), 'passphrase')
Best of luck.

I've made some progress on this. If I use the Net::SSH library, I can do this:
Net::SSH::KeyFactory.load_private_key 'keyfile', 'passphrase'
By reading the source code I have yet to figure out what the library does to OpenSSL's PKey::RSA.new to accomplish this... And then I go and test again, and sure enough, OpenSSL can open the private key just fine without Net::SSH... I've made so much tests that somehow I didn't test this correctly before.
But I still have the issue of creating an SSH compatible key pair... and maybe I'll go test again and have the answer :P ... nah, I'm not that interested in that part

Related

How to store the random key and iv created when trying to encrypt in ruby?

I'm currently working on a project where I will be storing data of users in text files. I would like to encrypt these files using the OpenSSL library within Ruby. I am able to encrypt files using randomly created key and iv values. As the program won't be running on a server but locally I would like to export these values with a password so that a different method could decrypt the information.
require "openssl"
cipher = OpenSSL::Cipher::AES256.new(:CBC)
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv
encrypted = cipher.update("test.txt") + cipher.final
File.open("temp.txt", "w+") do |file|
file.write(encrypted)
end
If I don't use a random key it won't encrypt the information or returns errors.
'update': key not set (OpenSSL::Cipther::CipherError)
When I try to use the export commands (with random key) I also get an error saying that it isn't a method.
'export' for #<string:0x00000000065659b8> (NoMethodError)
It looks like you're not experienced with developing encryption schemes, so I'm going to suggest you not implement this yourself. The built-in OpenSSL library does not protect you from making security mistakes. As you've noticed, it provides no guidance on how to securely handle the key and iv (and doing it wrong would be insecure).
I recommend using the rbnacl gem, which has:
The RbNaCl::SimpleBox class provides a simple, easy-to-use cryptographic API where all of the hard decisions have been made for you in advance. If you're looking to encrypt something, and you don't know much about cryptography, this is probably what you want to be using.
Here's a full end-to-end example with outputting to & loading from disk:
require 'rbnacl'
generated_key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes)
box = RbNaCl::SimpleBox.from_secret_key(generated_key)
ciphertext = box.encrypt('plaintext')
# save both artifacts to disk
File.open('cipher.bin', 'w') { |f| f.write(ciphertext) }
File.open('key.bin', 'w') { |f| f.write(generated_key) }
# decrypt by reading artifacts from disk
# this could be done in separate program
ciphertext_from_file = File.read('cipher.bin', mode: 'rb') # r -> "read", b -> "binary"
key_from_file = File.read('key.bin', mode: 'rb')
regenerated_box = RbNaCl::SimpleBox.from_secret_key(key_from_file)
regenerated_box.decrypt(ciphertext_from_file)
# => 'plaintext'
# cleanup
File.delete('cipher.bin') if File.exist?('cipher.bin')
File.delete('key.bin') if File.exist?('key.bin')
Note that generated_key and ciphertext are both binary strings. If you don't want to save raw binary files, then convert them using something like Base64.encode64 or Digest.hexencode, converting them to printable characters. That way, they'll be printable, copy/paste-able, and you won't need to do a "read binary" with 'rb'.

Ruby RSA public key encryption to Golang

I'm currently working on a project where I have to "convert" some code from Ruby(version 1.9.3p194) to Golang(version 1.7). There is this part where Ruby uses RSA public key encryption and I always get a consistent result every time it gets executed. This is the function used:
Edit: I overlooked that after the public key encryption, there is a base 64 encoding as well
public_key = OpenSSL::PKey::RSA.new(public_encryption_key)
public_encrypted_text = public_key.public_encrypt(text, OpenSSL::PKey::RSA::NO_PADDING)
base64_encrypted_text = Base64.encode64(public_encrypted_text).gsub("\n", "")
escaped_encrypted_text = URI.escape(encrypted_key, "/+=")
However in Golang, due to the rsa library I can't get a consistent result since the function to encrypt takes a random parameter to generate different result each time. I understand why it needs to be different every time, but i can't get anything remotely similar to what ruby generates. These are the functions used in Golang:
//keyBytes is the public key as []byte
block, _ := pem.Decode(keyBytes)
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
pubKey, ok := key.(*rsa.PublicKey)
if !ok {
return nil, errors.New("Cannot convert to rsa.PublicKey")
}
result, err := rsa.EncryptPKCS1v15(cryptorand.Reader, pubKey, text)
encryptedText := base64.URLEncoding.EncodeToString(result)
encryptedText = strings.TrimRight(encryptedText, "=")
One of the problems is that ruby can encrypt the text with no problem, and in golang I'm getting an error that the key is too short to encrypt everything.
If I encrypt something else, like "Hello". When decrypting I get from ruby the error "padding check failed". The decryption is being handle like follows:
private_key.private_decrypt(Base64.decode64(text))
EDIT: Thanks to the answer of gusto2 I now know better what is going on since I didn't have much understanding of RSA.
Now in Golang I was able to encrypt the text using PKCS1 v1.5, and just to be sure I tried to decrypt that as well, also in Golang, with no problem.
However in Ruby I still wasn't able to decrypt using the private key. So I figured that the base64 encoding used in Golang was the issue. So I changed that part to this:
encryptedText := base64.StdEncoding.EncodeToString(result)
And also I removed the last line were the equal sign was being trimmed.
With that done it worked like a charm.
I am no knowledgeable about golang, however I may know something about RSA.
The difference seems to be in the padding.
For ruby - no padding is used
For golang - PKCS1v15 padding is used
In the rubyexample you use OpenSSL::PKey::RSA::NO_PADDING is used which is VERY VERY unsafe. It is called textbook RSA and is not inteded in real-life use as it has many weaknesses and dangerous traps. So the ruby example is very dangerously unsafe because of using the textbook RSA. As well it is limited to encrypting small messages (much smaller than the keyspace).
There are two padding types used with RSA:
PKCS1 v1 (commonly referred as PKCS1) - this is a deterministic padding (the output is always the same), many cryptographers consider this option obsolete as some weaknesses has been found when not used properly, but it is still in use and not considered broken.
PKCS1 v2 (commonly refered as OAEP or PSS) which is stochastic (randomized) padding. You can distinguish the last two as the output of OAEP is always different.
One of the problems is that ruby can encrypt the text with no problem, and in golang I'm getting an error that the key is too short to encrypt everything
You've provided only a small part of the golang example, so here I may only assume many things.
As you claim the golang example outputs randomized output and according to the parameters PKCS1 v1.5 is used, I'd assume the implementation is doing hybrid encryption which is good and much safer way to encrypt data with RSA (using symmetric encryption with random key and wrap/encrypt the key with RSA).

Ruby error "bignum too big to convert into long"

I'm trying to generate an RSA keypair in ruby with:
OpenSSL::PKey::RSA.generate(aReallyLongBignum, 65537)
but I am getting the following error:
bignum too big to convert into long
However it works in python using RSA.construct. Is there any way for this to work in ruby? I've looked everywhere. Really lost here. I am not trying to only take a section of this number at a time, I need to be able to pass the whole number to RSA.generate
I was able to solve this using OpenSSL::BN and setting it after creating an instance of OpenSSL::Pkey::RSA
key = OpenSSL::PKey::RSA.new
key.e = OpenSSL::BN.new(65537)
key.n = OpenSSL::BN.new(aReallyLongBignum)

How to enumerate through multiple certificates in a bundle?

I can create OpenSSL::X509::Certificate objects from files containing certificates:
blob = IO.binread path
cert = OpenSSL::X509::Certificate.new blob
However, this creates the certificate-object from the first certificate found in the file. When dealing with certificate-bundles the subsequent certificates in the same file are quietly ignored.
I suppose, I can split the blob on "\n-----END CERTIFICATE-----\n" and feed each part to OpenSSL::X509::Certificate.new, but I'm not sure, that will work with all file-formats, that my script might encounter and be expected to operate on.
Is there a better way?
Did some digging, it seems that the Ruby openssl bindings doesn't support this. It also seems like this is not something that is common and/or easy to do with the raw C bindings. All the code I could find online shows doing stuff with a single cert, or using a X509 store to verify other certs. I've not seen any examples of having a single cert "object" refer to multiple certs, and/or iterating the certs in a store.
So I think the short answer is: no. At least nothing provided by the Ruby bindings can help you do this.
If your use case is to verify a certificate against a bundle of certificates, there's always OpenSSL::X509::Store, which has a method store.add_file that adds all the certs in that file to the store, and has methods to verify whether or not a cert is valid based on the bundle contents.
Ok, so with the assumption that only PEM-encoded certificates can be packed into a bundle, I wrote my code as below. If the assumption is invalid -- or if you can offer a better way, please let me know!
DELIMITER = "\n-----END CERTIFICATE-----\n"
blob = IO.binread path
blobs = blob.split(DELIMITER)
blobs.each do |blob|
blob += DELIMITER # Does not break DER
cert = OpenSSL::X509::Certificate.new blob
.... process the cert ....
end
Instead of splitting and then re-adding the delimiter, one could use scan, but that requires regular-expression use, which is probably more expensive, than what I'm doing...
This code works with DER-encoded certificates too -- but only one per file, because there is no obvious way to split them.
I do not know about ruby, but with C you can load a CRT (and CRL) bundle like
X509_STORE *store = X509_STORE_new();
X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
X509_LOOKUP_load_file(lookup, argv[i], X509_LU_X509);
and iterate over its elements with
for (int i = 0; i < sk_X509_OBJECT_num(store->objs); ++i) {
X509_OBJECT *o = sk_X509_OBJECT_value(store->objs, i);
switch (o->type) {
case X509_LU_X509:
handle_crt(o->data.x509);

How to decrypt a string with known key and a IV in java?

I have a string that is encrypted in c#.net which I have to decrypt in java using a key and a IV provided by the client. Used algorithm is AES.
I have tried few things. The key looks something like
key = "QWEEqweASDreefERTfdf45fefdWERfsdf34fedfdwn5=" //length 44 bytes
iv = "nkfghER24dfdfdf56YUIgH==" // lenght=24 bytes
When I use this with Cipher class with algorith AES/CBC/PKCS5Padding
passing the above key to Secretkeyspec class it says invalid key lenth 44 bytes
I am not able makeout whats wrong with the key. Tried all suggested solutions for few days nothing works. Can some one help please? Thank you.
Use java native for C# code.
First write C# code for decrypt the key.
and calling the code in java using native.
for reference
http://www.codeproject.com/Articles/378826/How-to-wrap-a-Csharp-library-for-use-in-Java

Resources