Verifying signature of non-hashed data with Ruby OpenSSL - ruby

I have an RSA public key, some data and a signature of that data. I need to verify the signature. However, the signature is not of a digest of the data, but of the entire data. (The data itself is only 16 bytes, so the signer doesn't bother to hash the data before signing it.) I can verify the signature in C by specifying a NULL engine when initializing the context:
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(verify_key, NULL);
However, I have been unable to find an equivalent in Ruby's OpenSSL::PKey::PKey verify method. That method requires a Digest object, and there is no Digest that I can find that doesn't actually hash but just returns the data as-is. I tried creating my own Digest subclass, but I don't believe that can work, since the underlying OpenSSL library won't know about the existence of a custom digest type.
Am I stuck, or is there a way to solve this problem given that I cannot modify the code run by the signer?

Summarizing the answer from the comments in order to remove this question from the "Unanswered" filter...
owlstead:
Have you tried to find a function like public_decrypt? It may work, as normally you should not encryption with a private key and decrypt with a public key. With a bit of luck it will accept the signature version of PKCS#1 padding (note that the padding used for encryption and signing is different in PKCS#1).
Wammer:
Of course - decrypting the signature with the public key and verifying that it matches the data works fine. So far this is working fine with the standard PKCS#1 padding, but I'll do some more research to see if the differing encryption and signing paddings are a problem in practice. Thanks.
owlstead:
After a decrypt and validation of the padding, all that is left is a (if possible, secure) compare. So that would replace the verification function pretty well. Most of the security is in the modular arithmetic and padding.

Related

How to use S3 SSE C (Server Side Encryption with Client Provided Keys) with Ruby

I'm trying to upload a file to S3 and have it encrypted using the SSE-C encryption options. I can upload without the SSE-C options, but when I supply the sse_customer_key options I'm getting the following error:
ArgumentError: header x-amz-server-side-encryption-customer-key has field value "QkExM0JGRTNDMUUyRDRCQzA5NjAwNEQ2MjRBNkExMDYwQzBGQjcxODJDMjM0\nnMUE2MTNENDRCOTcxRjA2Qzk1Mg=", this cannot include CR/LF
I'm not sure if the problem is with the key I'm generating or with the encoding. I've played around with different options here, but the AWS documentation is not very clear. In the general SSE-C documentation it says you need to supply a x-amz-server-side​-encryption​-customer-key header, which is described as this:
Use this header to provide the 256-bit, base64-encoded encryption key
for Amazon S3 to use to encrypt or decrypt your data.
However, if I look at the Ruby SDK documentation for uploading a file the 3 options have a slightly different description
:sse_customer_algorithm (String) — Specifies the algorithm to use to when encrypting the object (e.g.,
:sse_customer_key (String) — Specifies the customer-provided encryption key for Amazon S3 to use in
:sse_customer_key_md5 (String) — Specifies the 128-bit MD5 digest of the encryption key according to RFC
(I didn't copy that wrong, the AWS documentation is literally half-written like that)
So the SDK documentation makes it seem like you supply the raw sse_customer_key and that it would base64-encode it on your behalf (which makes sense to me).
So right now I'm building the options like this:
sse_customer_algorithm: :AES256,
sse_customer_key: sse_customer_key,
sse_customer_key_md5: Digest::MD5.hexdigest(sse_customer_key)
I previously tried doing Base64.encode64(sse_customer_key) but that gave me a different error:
Aws::S3::Errors::InvalidArgument: The secret key was invalid for the
specified algorithm
I'm not sure if I'm generating the key incorrectly or if I'm supplying the key incorrectly (or if it's a different problem altogether).
This is how I'm generating the key:
require "openssl"
OpenSSL::Cipher.new("AES-256-CBC").random_key
Oh, did you notice that your key contains '\n'? That's most probably why you get the CR/LF error:
QkExM0JGRTNDMUUyRDRCQzA5NjAwNEQ2MjRBNkExMDYwQzBGQjcxODJDMjM0(\n)nMUE2MTNENDRCOTcxRjA2Qzk1Mg=
As mentioned by the colleague in the comments, strict_encode64 is an option, as it complies to RFC 2045.
By the way, I got this insight from here: https://bugs.ruby-lang.org/issues/14664
Hope it helps! :)
First of all, please make sure that you are using the latest version of the SDK (2.2.2.2) from here
So, As I understand while we generate the presigned URL, we have to specify the SSECustomerMethod and when consuming the URL, the "x-amz-server-side-encryption-customer-key" header is set with the customer key, you also need to set the "x-amz-server-side-encryption-customer-algorithm" header.
var getPresignedUrlRequest = new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = "EncryptedObject",
SSECustomerMethod= SSECustomerMethod.AES256,
Expires = DateTime.Now.AddMinutes(5)
};
var url = AWSClients.S3.GetPreSignedURL(getPresignedUrlRequest);
var webRequest = HttpWebRequest.Create(url);
webRequest.Headers.Add("x-amz-server-side-encryption-customer-algorithm", "AES256");
webRequest.Headers.Add("x-amz-server-side-encryption-customer-key", base64Key);
using (var response = webRequest.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream()))
{
var contents = reader.ReadToEnd();
}

How to encrypt and decrypt data with Openssl RSA public private key pair in Golang?

I am trying to write a program which encrypts data using a RSA public key and and decrypts data using private key. The RSA keys were generated with openssl tool.
I found Spacemonkeygo Openssl https://github.com/spacemonkeygo/openssl wrapper for this purpose. But unable to find any sample over & also their is no document available for the same. So that I am unable to use.
Please guide me how can I use Openssl in Golang?
I am using first time encryption decryption & Openssl.
Thank you in advance!
I am trying to write a program which encrypts data using a RSA public
key and and decrypts data using private key. The RSA keys were
generated with openssl tool.
You don't need an OpenSSL library package to do this: you just need some of the crypto, encoding, and other packages in the Go standard library. Namely:
encoding/pem
crypto/x509
math/rand
crypto/sha256
crypto/rsa
encoding/base64
Create a PEM block from the key, setting Type to "RSA PRIVATE KEY" or "RSA PUBLIC KEY", parse the keys with the x509 functions (PKIX for public), use a type assertion to make it the appropriate RSA public/private type, encrypt the message using OAEP padding, an SHA-256 hash function, and rand.Reader for a source of entropy, base64 encode the resulting cipher if you're sending it as text rather than binary, then base64 decode it and decrypt it using the same but with the private key on the other side.
See in particular func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error).
Read the documentation in these packages and some general info about encryption and RSA, there are also usage examples for each of these packages on StackOverflow -- though perhaps not put all together.
Every package you need for the described goal is in the Go standard library.
You may need to check that your PKCS function version (e.g. PKCS8) lines up with the private key produced by your OpenSSL version.

Decryption of a Chef encrypted databag in Ruby

I have been able to use the Ruby code written here Data bag encryption encrypts on Chef server, but how to encrypt local copy? as a basis to encrypt my data bags without having to resort to the knife tool which is great. My problem occurs however with decrypting these encrypting databags as the code written here Data bag encryption encrypts on Chef server, but how to encrypt local copy? doesn't appear to work correctly.
I have successfully downloaded the encrypted databag as a json, converted that to a hash, loaded my symmetrical secret key (which is used for both Chef encryption and decryption) and created a databag using this:
databag_item = Chef::EncryptedDataBagItem.new raw_file_down, secret
If I was to check the class of this databag_item, Ruby tells me it is a Chef::EncryptedDataBagItem object. However when I try to write this to a file
IO.write tmp_item_file, Chef::JSONCompat.to_json_pretty( databag_item.to_hash )
i receive the following error:
Chef::EncryptedDataBagItem::DecryptionFailure: Error decrypting databag value: 'wrong final block length'.
Most likely the provided key is incorrect. When I remove the .to_hash option and do this:
IO.write tmp_item_file, Chef::JSONCompat.to_json_pretty( databag_item)
I receive 233 or a 41. None of these methods produce a decrypted data bag. Can someone please help?
BTW I'm a new user to Chef

Security of private key when using "insecure" OpenSSL methods

I recently came across a situation where I absolutely needed to use the method OpenSSL::PKey::RSA#params. However, the doc says the following:
THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!!
...
Don’t use :-)) (It’s up to you)
What does this mean? How is the private key normally protected within the instance of the RSA key and how is this different from any regular object?
Can I prevent information from leaking by doing something like this, where the method is only accessed within a lambda:
private_key = OpenSSL::PKey::RSA.generate(2048)
save_private = lambda do
key = OpenSSL::Digest::SHA512.new.digest("password")
aes = OpenSSL::Cipher.new("AES-256-CFB")
iv = OpenSSL::Random.random_bytes(aes.iv_len)
aes.encrypt
aes.key, aes.iv = key, iv
aes.update(private_key.params.to_s) + aes.final
end
private_enc, save_private = save_private.call, nil
Also, if this security problem has anything to do with variables lingering in memory awaiting GC, can forcing garbage collection make things more secure?
GC.start
Thanks in advance to anybody who can clear this up.
It seems to give away information of the private key. The key components need to be available to perform any signing operation or decryption so normally the key information is in memory in the clear. Obviously if you retrieve it you must make sure that you keep it safe. I presume that this is where the warning comes in.
You can do all kinds of things like encrypting the private key parameters, but then you get to a point where you have to store the decryption key. Basically this will end up being impossible to solve without an external system (or a person keeping a password).

How can I asymmetrically encrypt data using OpenPGP with Ruby?

This feels like it should be dead simple, yet I'm not having any luck.
The scenario is this: I have a public *.asc key file. I want to use this file (not my personal keyring) to encrypt data on a server, so that I can decrypt it locally with a secret key.
From the command line I can achieve this using gpg, but I'd prefer to use a Ruby library that isn't just a wrapper around the CLI (i.e., presumably one that provides bindings to the C library). I've looked at the GPGME and OpenPGP gems and haven't been able to figure out how to use them. The documentation (especially for OpenPGP) is quite sparse.
Here, for example, is something I've tried using GPGME, without any luck:
key = GPGME::Data.new(File.open(path_to_file))
data = GPGME::Data.new("I want to encrypt this string.")
# Raises GPGME::Error::InvalidValue
GPGME::Ctx.new do |ctx|
e = ctx.encrypt(key, data)
end
Has anyone been through this already? Surely this can't be that complicated?
I believe I've now got this figured out. It was actually just a few simple pieces I was missing:
Initializing the GPGME::Ctx object with a keylist_mode of GPGME::KEYLIST_MODE_EXTERN.
Importing the public key file using GPGME::Ctx#import.
Using GPGME::Crypto#encrypt to perform the encryption and specifying the correct recipient.
So my solution now looks like this:
key = GPGME::Data.new(File.open(path_to_file))
data = GPGME::Data.new("I want to encrypt this string.")
GPGME::Ctx.new(GPGME::KEYLIST_MODE_EXTERN) do |ctx|
ctx.import(key)
crypto = GPGME::Crypto.new(:armor => true, :always_trust => true)
e = crypto.encrypt(data, :recipients => "recipient#domain.com")
end

Resources