Ruby OpenSSL AES generate random key - ruby

I have an elementary problem that I can't seem to figure out. I'm trying to generate a random key in AES-256-CBC that can be used to encrypt/decrypt data.
Here is what i'm doing:
require 'openssl'
cipher = OpenSSL::Cipher::AES256.new(:CBC)
cipher.encrypt
puts cipher.random_key
>> "\xACOM:\xCF\xB3#o)<&y!\x16A\xA1\xB5m?\xF1 \xC9\x1F>\xDB[Uhz)\v0"
That gives me the string above, which looks nothing like keys i've used in the past. I am very new to encryption as you may be able to tell, but I'm trying to understand if I need to further prepare the string. I created a quick view in rails so I could go to /generate and it would render a simple html page with a random key. It wouldn't even render the page and was complaining about invalid uTF8. the only way I could get the page to display was to Base64 encode the key first.
I know i'm missing something stupid. Any ideas would be great.
EDIT:
This is what it looks like if I Base64encode. Should I be stripping the = signs off or something?
AES-128-CBC
Random Key: 0xdq+IZdmYHHbLC9Uv8jgQ==
Random IV: vp08d/nFGE3R8HsmOzYzOA==
AES-256-CBC
Random Key: BW0wY5fUkcwszV5GIczI+D45eFOz/Ehvw5XdZIavVOQ=
Random IV: D0pXdwQAqu+XSOv8E/dqBw==
Thanks for the help!

To answer your question (quoted from Wikipedia):
The '==' sequence indicates that the last group contained only one
byte, and '=' indicates that it contained two bytes.
In theory, the padding character is not needed for decoding, since the
number of missing bytes can be calculated from the number of Base64
digits. In some implementations, the padding character is mandatory,
while for others it is not used.
For Ruby and your use case the answer is: No problem to strip the =

Related

Export public key to base64 in Ruby

I have a public key in PEM format that was generated with:
ecdsa_public_key = OpenSSL::PKey::EC.new ecdsa_private_key
ecdsa_public_key.private_key = nil
ecdsa_public_key.to_pem
I have to read the PEM string and get a base64 url encoded string.
How can I do that in Ruby?
ecdsa_public_key = OpenSSL::PKey.read pem_string
ecdsa_public_key.to_base64 # pseudo code...
BTW I have to do this for the WebPush protocol, which states:
you must add your VAPID public key to the Crypto-Key header as a base64 url encoded string with p256ecdsa= prepended to it.
The PEM string actually is base 64 encoded (at least partially), but I don’t think it’s what you want here, it includes other details and I think you want the “raw” public key data.
Here’s one way you can get your key into the format I think you want. It’s a bit long winded but I don’t think Ruby’s OpenSSL bindings provide a more direct method (you’ll need to require "base64" first):
# Assuming the key is in ecdsa_public_key
Base64.urlsafe_encode64(ecdsa_public_key.public_key.to_bn.to_s(2), padding: false)
This calls public_key to get the underlying OpenSSL::PKey::EC::Point, then converts that to an OpenSSL::BN in the correct format, and converts that to a binary string. Finally this string is base64 encoded.
Sorry that this is a bit late.
I'm not terribly familiar with Ruby, so I can't offer code examples for what to do, but I can try to describe the process of VAPID. (Also, my apologies if I go into needless detail, since I figure that others might stumble across this.)
In short, VAPID is a Javascript Web Token (JWT). You create a ECDSA key pair using your favorite ECDSA generation method specifically for VAPID.
e.g.
openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem
openssl ec -in vapid_private.pem -pubout -out vapid_public.pem
A "PEM" is a formatted file that includes a standard header line, a footer line, and a set of long strings of crap. (Not sure if that's the technical term for them, but yeah, I'm going to go with that.) Those long strings of crap are Base64 representations of the key data saved in specific formats.
Honestly, I'd STRONGLY encourage using a library where and when possible. jwt.io has a number of Ruby libraries you could use, as well as libraries for other languages. As for the "Crypto-Key:" header, there's a bit of good news/other news.
You could just take the Long Strings of Crap from your vapid-public.pem file, append them together and specify them as the 'p256ecdsa=' key.
The VAPID protocol is changing soon https://datatracker.ietf.org/doc/html/draft-ietf-webpush-vapid-02
The change effectively gets rid of the Crypto-Key p256ecdsa component. Instead, the Authorization key becomes:
Authorization: vapid t=JWT containing VAPID info,k=VAPID Public key
e.g.
vapid t=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtYWlsdG86d2VicHVzaF9vcHNAY2F0ZmFjdHMuZXhhbXBsZS5jb20iLCJleHAiOjE0ODc0NDM3MTR9.mlLOWYMt-6aM3NB6b6_Msf8LqRKCuHd1Vfdp_fuJ3eqsQoID8lit305hIfNubTbvfACucuCygF3qB4scDbuHvg,k=EJwJZq_GN8jJbo1GGpyU70hmP2hbWAUpQFKDByKB81yldJ9GTklBM5xqEwuPM7VuQcyiLDhvovthPIXx-gsQRQ
I'm of mixed opinion about this. It does reduce the need for a separate header, but also shortens the amount of info you can shove into your claims before you run out of header room.
You could try
require 'base64'
Base64.encode64(ecdsa_public_key)
to convert to base64

How do I get a UTF-8 string out of an MD5 digest?

I am trying to use an API that requires an MD5 hash to be sent in UTF-8 format.
Problem is, I can't find any way to actually make that happen.
require 'digest/md5'
api_sig = Digest::MD5.digest "api_key=blahblahblah"
puts api_sig
>> Decode error: not UTF-8
So I try force_encoding(Encoding::UTF_8). Same error. inspect, to_s, nothing gives me what I want.
How can I get a UTF-8 string representing an MD5 digest of another string?
Call Digest::MD5.hexdigest "api_key=blahblahblah"
The documentation of this is very poor, but you can find a lackluster explanation here: http://www.ruby-doc.org/stdlib-2.0/libdoc/digest/rdoc/Digest/Class.html#method-c-hexdigest

CI encrypt and decrypt

I get that the encryption class produces different output each time the same word/string is encrypted, for example, $this->encrypt->encode("word") ran five times would produce five different encrypted strings.
How can I reference the encrypted string in a DB query if each time I call $this->encrypt->encode("word") gives me something different?
Asked in a different way, is there something I can encrypt with that doesn't have a random value so that each time I encrypt I get the same output for the same input?
Base64 encoding is not encryption (referring to your own answer). I have not used codeigniter, but I notice on its doc pages that it allows:
$this->encrypt->set_mode();
You could encrypt with ECB mode (MCRYPT_MODE_ECB) for deterministic encryption where the same data always encrypts to the same ciphertext. This way, you encrypt your search string, and it will match the encrypted data in the database.
This is considered a weakness of ECB mode, but in this case, the deterministic behavior may be what you want.
I think base64_encode($str) is what I'm looking for.
this code works only on php 5.5 or higher
echo password_hash(variable, PASSWORD_DEFAULT);
The first parameter is the password string that needs to be hashed and the second parameter specifies the algorithm that should be used for generating the hash.
The default algorithm is currently bcrypt, but a stronger algorithm may be added as the default later at some point in the future and may generate a larger string. If you are using PASSWORD_DEFAULT in your projects, be sure to store the hash in a column that’s capacity is beyond 60 characters. Setting the column size to 255 might be a good choice. You could also use PASSWORD_BCRYPT as the second parameter. In this case the result will always be 60 characters long.
and to check the password hash here is the syntax
<?php
if (password_verify($oldpassword, $hash)) {
// Success!
// the first parameter is your password that's not yet encrypted, the secode is your password encrypted
}
else {
// Invalid password
}

Password Hashes Characters in Applications

I appreciate your time.
I'm trying to understand something. Why is it that hashes that I generate manually only seem to include alphanumeric characters 0-9, a-f, but all of the hashes used by our favorite applications seem to contain all of the letters [and capitalized ones at that]?
Example:
Manual hash using sha256:
# sha256sum <<< asdf
d1bc8d3ba4afc7e109612cb73acbdddac052c93025aa1f82942edabb7deb82a1 -
You never see any letters above f. And nothing is capitalized.
But if I create a SHA hash using htpasswd, it's got all the alphanumerics:
# htpasswd -snb test asdf
test:{SHA}PaVBVZkYqAjCQCu6UBL2xgsnZhw=
Same thing happens if you look at a password hash in a website CMS database for example. There must be some extra step I'm missing or the end format is different than the actual hash format. I thought it might be base64 encoded or something, but it did not seem to decode.
Can someone please explain what's happening behind the scenes here? My friend explained that piping "asdf" to sha256sum is showing the checksum, which is not the actual hash itself. Is that correct? If so, how can I see the actual hash?
Thank you so much in advanced!
There's two things going on here.
First, your manual hash is using a different algorithm than htpasswd. The -s flag causes htpasswd to use SHA1, not SHA256. Use sha1sum instead of sha256sum.
Second, the encoding of the hashes are different. Your manual hash is Hex encoded, the htpasswd hash is Base64 encoded. The htpasswd hash will decode, it just decodes to binary. If you try to print this binary it will look like =¥AU™¨Â#+ºPöÆ'f (depending on what character encoding you're using), and that may be why you believe it's not decoding.
If you convert the Base64 directly to Hex (you can use an online tool like this one), you'll find that sha1sum will generate the same hash.
My friend explained that piping "asdf" to sha256sum is showing the checksum, which is not the actual hash itself.
Your friend is incorrect. You're seeing the Hex encoding of the hash. But the piping does affect the hash that's generated, it adds a newline character, so what you're actually hashing is asdf\n. Use this command instead:
echo -n "asdf" | sha1sum
It is base64 encoded.
Base64 encoding ends an an equal sign. So that is the first indicator. Although the htpasswd man page doesn't mention it, other Apache docs about "the password encryption formats generated and understood by Apache" does say that the SHA format understood by Apache is base64 encoded.

Create a SHA1 hash in Ruby via .net technique

OK hopefully the title didn't scare you away. I'm creating a sha1 hash using Ruby but it has to follow a formula that our other system uses to create the hash.
How can I do the following via Ruby? I'm creating hashes fine - but the format stuff is confusing me - curious if there's something equiv in the Ruby standard library.
System Security Cryptography (MSDN)
Here's the C# code that I'm trying to convert to Ruby. I'm making my hash fine, but not sure about the 'String.Format("{0,2:X2}' part.
//create our SHA1 provider
SHA1 sha = new SHA1CryptoServiceProvider();
String str = "Some value to hash";
String hashedValue; -- hashed value of the string
//hash the data -- use ascii encoding
byte[] hashedDataBytes = sha.ComputeHash(Encoding.ASCII.GetBytes(str));
//loop through each byte in the byte array
foreach (byte b in hashedDataBytes)
{
//convert each byte -- append to result
hashedValue += String.Format("{0,2:X2}", b);
}
A SHA1 hash of a specific piece of data is always the same hash (effectively just a large number), the only variation should be how you need to format it, to e.g. send to the other system. Although particularly obscure systems might post-process the data, truncate it or only take alternate bytes etc.
At a very rough guess from reading the C# code, this ends up a standard looking 40 character hex string. So my initial thought in Ruby is:
require 'digest/sha1'
Digest::SHA1.hexdigest("Some value to hash").upcase
. . . I don't know however what the force to ascii format in C# would do when it starts with e.g. a Latin-1 or UTF-8 string. They would be useful example inputs, if only to see C# throw an exception - you know then whether you need to worry about character encoding for your Ruby version. The data input to SHA1 is bytes, not characters, so which encoding to use and how to convert are parts of your problem.
My current understanding is that Encoding.ASCII.GetBytes will force anything over character number 127 to a '?', so you may need to emulate that in Ruby using a .gsub or similar, especially if you are actually expecting that to come in from the data source.

Resources