I'm trying to create an expiring link to allow access to a private file on S3 using Ruby (1.9.3).
I've been following the instructions from here: http://docs.aws.amazon.com/AmazonS3/2006-03-01/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth - However the final values that I'm getting are wrong. The example only gives the final result, not the values from each step, so I'm not sure where this is going wrong. The intermediate values should be the same regardless of implementation.
The Ruby code that I'm using (including the same key and expires from the above link):
require "cgi"
require "base64"
require "openssl"
require "digest/sha1"
key_id = 'AKIAIOSFODNN7EXAMPLE' # Example Amazon key id and secret key
key = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
expires = 1141889120
string_to_sign = "GET\n\n\n#{expires.to_s}\n/johnsmith/photos/puppy.jpg"
digest = OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new("sha1"), key, string_to_sign)
base64 = Base64.encode64(digest).strip
signature = CGI::escape(base64)
puts "Digest: #{digest}"
puts "Base64: #{base64}"
puts "Signature: #{signature}"
Outputs:
Digest: }'\n\x18p\x83#CX\xE4N\xC2b\x9FUs\xC5J1\xB6
Base64: fScKGHCDI0NY5E7CYp9Vc8VKMbY=
Signature: fScKGHCDI0NY5E7CYp9Vc8VKMbY%3D
However the signature on the Amazon page is: NpgCjnDzrM%2BWFzoENXmpNDUsSn8%3D
Any ideas on where this is going wrong?
Related
Can anyone advise how to encrypt + decrypt a message in Ruby using OpenSSL::PKCS7? (I'm not trying to sign + verify. That much I can do. I need to write tests for some existing code which calls OpenSSL::PKCS7#decrypt.)
I've pored over what little documentation I can find online, but to no effect. When I call decrypt, I get the following on stderr:
example.rb:13:in `decrypt': decrypt error (OpenSSL::PKCS7::PKCS7Error)
require 'openssl'
# Create a key & self-signed certificate
key = OpenSSL::PKey::RSA.new 1024
cert = OpenSSL::X509::Certificate.new
cert.public_key = key.public_key
cert.not_before = Time.now
cert.not_after = Time.now + 2**12
cert.sign key, OpenSSL::Digest.new('SHA256')
# Encrypt & decrypt
ciphertext = OpenSSL::PKCS7.encrypt([cert], "plaintext").to_pem
decrypted = OpenSSL::PKCS7.new(ciphertext).decrypt(key, cert) # => error
I've furthermore experimented with specifying a cipher and flags for the OpenSSL::PKCS7::encrypt call, but to no effect.
I'm using Ruby 2.6.9.
I've been struggling to sign the jwt and I'm not familiar with the ruby file provided by apple on WWDC.. the code reads
require "base64"
require "jwt"
ISSUER_ID = "your-ID"
KEY_ID = "your-KeyID"
private_key = OpenSSL::PKey.read(File.read())
token = JWT.encode(
{
iss: ISSUER_ID,
exp: Time.now.to_i + 20 * 60,
aud: "appstoreconnect-v1"
},
private_key,
"ES256",
header_fields={
kid: KEY_ID }
)
puts token
the code keeps giving me this error when I run it on terminal.
enter image description here
my goal is simple, I just want to return some data from the GET api but am struggling with the 401 error on postman.
You have a syntax error in your code according to the screenshot you posted.
There's also what appears to be an error or misconfiguration in your code sample.
The gem most frequently used to encode/decode JWTs in ruby is here; this is the gem you're using in your example (via require "jwt") There are a number of examples on that page you can reference, but if you look thru the README you'll note they mention you can only use a kid with RSA and you're not using RSA in your example.
Try this:
token = JWT.encode(
{
iss: ISSUER_ID,
exp: Time.now.to_i + 20 * 60,
aud: "appstoreconnect-v1"
},
private_key,
"ES256"
)
Here's a link to using a JWK with the kid and the RSA algorithm.
Search for ecdsa_key on that same page for examples with ES256 (you'll find the one I posted above).
Seeing the original example from the WWDC docs might help provide additional context for the correct configuration but I'm not sure where to find that document. If it's public and you can link to it I can follow up.
I am using puppet to manage my MySQL deployments. Service accounts are also managed via puppet because AWS doesn't provide the ability for me to leverage OpenLDAP as an authentication provider to RDS. I was given the requirement to use the sha256_password plugin for mysql. The puppet module I am using to manage everything, https://forge.puppet.com/puppetlabs/mysql, requires me to provide a hashed password to the user resource and the function included in the module only generates the mysql_native password. How can I use Ruby to generate the required hash? Below is the code I have so far.
require 'digest'
salt = Array.new(20){rand(256).chr}.join
hash = salt + 'Password'
rounds = 5000 # 5 iteration count * 1000 multipler per mysql code
x = 0
while x < rounds do
hash = Digest::SHA2.new(256).hexdigest(hash)
x += 1
end
h_salt = [salt].pack('H*')
b_hash = [hash[0..42]].pack('m')
h_hash = [hash].pack('H*')
value = '$A$005$' + h_salt + h_hash
print value
I can't determine if I'm encoding the salt and digest correctly. I believe I have the correct format other wise.
From https://crypto.stackexchange.com/questions/77427/whats-the-algorithm-behind-mysqls-sha256-password-hashing-scheme,
the expected format:
DELIMITER[digest_type]DELIMITER[iterations]DELIMITER[salt][digest]
It seems after reviewing the source code and others implementation of it, the salt and digest are base64 encoded HEX values. Is that correct?
Some examples I leveraged were from:
https://github.com/hashcat/hashcat/issues/2305
I was going through the Amazon Product Advertising API REST signature docs and I got stuck at #8
Calculate an RFC 2104-compliant HMAC with the SHA256 hash algorithm using the string above with our "dummy" Secret Access Key: 1234567890. For more information about this step, see documentation and code samples for your programming language.
I managed to get it on one more try with the help of Calculating a SHA hash with a string + secret key in python.
The following creates the correct signature:
require 'openssl'
secret_key = '1234567890'
query = 'AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06'
data = ['GET', 'ecs.amazonaws.com', '/onca/xml', query].join("\n")
sha256 = OpenSSL::Digest::SHA256.new
sig = OpenSSL::HMAC.digest(sha256, secret_key, data)
signature = Base64.encode64(sig)
Adding to AJcodez answer:
I would do:
...
signature_raw = Base64.strict_encode64(sig)
signature = CGI::escape(signature_raw)
encode64adds a newline at the end, strict_encode64() does not.
https://stackoverflow.com/a/2621023/2760406
Amazon wants you to "URL encode the plus (+) and equal (=) characters in the signature" #9 - won't work now if you don't.
http://docs.aws.amazon.com/AWSECommerceService/latest/DG/rest-signature.html#rest_detailedexample
You can calculate a keyed-hash message authentication code (HMAC-SHA256) signature with your secret access key by using cryptoJs
First install cryptoJs locally in your system by typing
npm install crypto-js
to install it globally you node a flag -g to the above command. Then add this code and run it.
var CryptoJS = require("crypto-js");
// Calculate an RFC 2104-compliant HMAC with the SHA256 hash algorithm
var exampleString =
"GET\n" +
"webservices.amazon.com\n" +
"/onca/xml\n" +
"AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=Images%2CItemAttributes%2COffers%2CReviews&Service=AWSECommerceService&Timestamp=2014-08-18T12%3A00%3A00Z&Version=2013-08-01";
var signature = CryptoJS.HmacSHA256(exampleString, "1234567890");
console.log("test signature", signature.toString(CryptoJS.enc.Base64));
I am hoping someone can shed some light on a problem that has been vexing me for the last few hours.
I am trying to decode a string that has been encoded in Ruby thus:
#!/usr/bin/env ruby
require 'base64'
require 'openssl'
require 'openssl/cipher'
require 'openssl/digest'
aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
aes.encrypt
aes.key = Digest::SHA256.digest('IHazSekretKey')
p Base64.encode64( aes.update('text to be encrypted') << aes.final )
Executing the above spits out: "3P86KyOrN2QJ/HFxxo3b7kAsxTgpDMMjROUPclsuXj0=\n"
I now try to decrypt this string in NodeJS 0.6.17
#!/usr/bin/env node
var crypto = require('crypto');
function decrypto(toDecryptStr) {
var result,
encoded = new Buffer(toDecryptStr, 'base64'),
decodeKey = crypto.createHash('sha256').update('IHazSekretKey', 'ascii').digest(),
decipher = crypto.createDecipher('aes-256-cbc', decodeKey);
result = decipher.update(encoded);
result += decipher.final();
return result;
};
console.log(decrypto('3P86KyOrN2QJ/HFxxo3b7kAsxTgpDMMjROUPclsuXj0='));
console.log(decrypto('3P86KyOrN2QJ/HFxxo3b7kAsxTgpDMMjROUPclsuXj0=\n')
The second script yields:
nazar#xfce:~/tmp/tst$ ./js_decrypt
Å'{ H£V)ÜB
Å'{ H£V)ÜB
Any help would be very much appreciated as my only remaining option now is to drown myself in a barrel of [Jamerson || Kirin Ichiban] (I'm only kidding)
PS there is a similar question on SO here, which sadly hasn't yielded any inspiration for my case.
The critical missing piece is the IV, which is required when encryption/decryption is to be made across language boundaries as apparently the encrypter will generate a random IV (or something like that - still don't understand how Ruby decrypts the string without an IV.... but then what do I know....), if one is not provided.
The following snippets show how to encrypt a string in Ruby and decrypt in NodeJS.
#!/usr/bin/env ruby
require 'openssl'
require 'base64'
require 'openssl/cipher'
require 'openssl/digest'
aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
aes.encrypt
aes.key = Digest::SHA256.digest('IHazSekretKey')
aes.iv = '1234567890123456'
p Base64.encode64( aes.update('text to be encrypted') << aes.final )
The above prints: "eiLbdhFSFrDqvUJmjbUgwD8REjBRoRWWwHHImmMLNZA=\n"
#!/usr/bin/env node
var crypto = require('crypto');
function decrypto(toDecryptStr) {
var result,
encoded = new Buffer(toDecryptStr, 'base64'),
decodeKey = crypto.createHash('sha256').update('IHazSekretKey', 'ascii').digest(),
decipher = crypto.createDecipheriv('aes-256-cbc', decodeKey, '1234567890123456');
result = decipher.update(encoded);
result += decipher.final();
return result;
}
console.log(decrypto('eiLbdhFSFrDqvUJmjbUgwD8REjBRoRWWwHHImmMLNZA=\n'))
The JS script now properly decrypts the string.
One unfortunate side effect is that existing encrypted data will need to be decrypted and then re-encrypted with an IV that is then used in the decrypting implementation.
A PITA but nonetheless a working solution.