How can I generate a WM_SEC.AUTH_SIGNATURE in Ruby? - ruby

(What I'm ultimately trying to accomplish is to look up individual products on Walmart.com using product codes, so if anyone has another way of doing that, that works too)
Walmart.io's Product Lookup API requires a header called "WM_SEC.AUTH_SIGNATURE", but I can't figure out how to generate it using Ruby. Some Java code is given here (I'm not sure about the legality of reproducing that code here) but I don't know Java so I don't understand what they're doing.
This SO question tries to explain the process, but again I'm not clear on exactly what's being done.
To get the digital signature using your own code, follow these steps:
Get the Consumer ID and your Base 64-encoded Private Key you generated
in Seller Center.
Get the full URL you wish to call, including any
path and query parameters.
Use the GET method to construct an input
for the digital signature.
Use the structure listed below: The
Consumer ID issued to you_ + "\n" + the URL of the API call you are
making + "\n" + the request method of the API call you are making in
all capitals + "\n" + the Unix Epoch timestamp now (in milliseconds
since Jan 01 1970 UTC) + "\n"
** Note: The order of the parameters and the line returns \n are important to generate the signature properly
Generate the byte array of the structured data listed in step 3 using
the following steps:
a. Decode the byte array with Base-64.
b. Encode the resulting value using PKCS#8 to represent your Private
Key. Libraries in various languages offer the ability to identify that
the Private Key is in PKCS#8 format and not in other conflicting
formats such as PKCS#1.
c. Use this byte representation of your
private key to sign the data using SHA-256 with RSA.
d. Encode the generated digital signature using Base-64.
Use the generated digital signature and the timestamp to make your API
call.
I've gotten as far as this:
time = DateTime.now.strftime('%Q')
customerid = "customerid"
link = "https://developer.api.walmart.com/api-proxy/service/affil/product/v2/items/4837473"
method = "POST"
uncoded = customerid + "\n" + link + "\n" + method + "\n" + time + "\n"
encoded = Base64.encode64(uncoded)
privatekey = "longrsakey"
But I don't know how to continue. Do I have to encode my privatekey into PKCS#8 and then use the result to encode my encoded? I can't even find a PKCS#8 encoder in Ruby.
Can anyone help?

Here is how you can create the signature needed to make the request, assuming you are trying to access the affiliate API:
version = 'YOUR VERSION'
consumer_id = "YOUR CONSUMER ID"
time_stamp = (Time.now.to_i * 1000).to_s
p_key = "YOUR PRIVATE KEY"
digest = OpenSSL::Digest.new('sha256')
data = consumer_id + "\n" + time_stamp + "\n" + version + "\n"
k = OpenSSL::PKey::RSA.new(p_key.to_s)
digest = OpenSSL::Digest::SHA256.new
signature = k.sign(digest,data)
signature = Base64.strict_encode64(signature)
headers = {
"WM_SEC.KEY_VERSION": version,
"WM_CONSUMER.ID": consumer_id,
"WM_CONSUMER.INTIMESTAMP": time_stamp,
"WM_SEC.AUTH_SIGNATURE": signature
}
puts HTTParty.get("https://developer.api.walmart.com/api-proxy/service/affil/product/v2/taxonomy", headers: headers).parsed_response

Related

Jmeter how to get value of Httprequest GET at runtime

I have done lot of cases where value is extracted from response of first request and passed to the subsequent request using Regular expression/CSS extractor etc but now i want to extract a value at GET and pass it to same GET (When i click on my page a random code is generated which has to be passed to the same page, this code will be unique for every click). How i can create as a variable and store value of code and pass ?
how to extract GET value from request in jmeter at runtime and pass it as variable. i want to pass this value as variable in same request
See below screenshot
My C# code for encrytion/decrytion is as follows -
string handlerPath = HttpUtility.UrlEncode(appPath + "/ReportFileUpload.ashx");
string cipherText = string.Empty;
string passPhrase = "!MMFileUploader123"; // can be any string
string initVector = "2038459710286532"; // must be 16 bytes
// Before encrypting data, we will append plain text to a random
// salt value, which will be between 4 and 8 bytes long (implicitly
// used defaults).
RijndaelEnhanced cipher = new RijndaelEnhanced(passPhrase, initVector); //, 4, 16, 256, "SHA1", DateTime.Today.ToBinary().ToString());
cipherText = cipher.Encrypt("?rn=" + repNum + "&uid=" + user.UserId + "&op=" + originPath + "&hp=" + handlerPath + "&p=" + pass + "&domain=" + domain + "&org=" + org + "&entitytype=" + entityType);
cipherText = HttpUtility.UrlEncode(cipherText);
//Decrypt
//decipher the code get the parameters and assign them to inParams
string passPhrase = "!MMFileUploader123";
string initVector = "2038459710286532"; // must be 16 bytes
RijndaelEnhanced cipher = new RijndaelEnhanced(passPhrase, initVector);
string cipherText = HttpUtility.ParseQueryString(
ApplicationDeployment.CurrentDeployment.ActivationUri.Query)["code"];
inParams = cipher.Decrypt(cipherText);
You need to have a pre-generated random code stored in CSV (CSV Data Set Config) and pass it to the request.
Ask your developer about the logic to generate the random code, it should be similar to some md5 encryption.
Use JSR223 Pre processor to generate random value before your request and pass that value to your request.

Installscript Syntax: Calling a Property, converting to variable, then setting property

So I have this pseudo code I need Installscript syntax for, for a Basic MSI installation I am creating with Installshield.
I need to call in a property previously set, [SERIAL] which is three digits long.
If the value is higher than 235 I need to subtract 235 from the value and save it as a string, and another string seperate as 2, otherwise 1. The two result strings then need to be set as new global properties for use later in the installation.
Pseudo code:
// call in the SERIAL property here.
if SERIAL > 235 then
string sIOVERSION = 2;
string sIOIP = "192.22." + (SERIAL - 235) + ".1";
else
string sIOVERSION = 1;
string sIOIP = "192.2." + SERIAL + ".1";
endif
// update properties [IOVER] and [IOIP] to the values of sIOVERSION and sIOIP
There are other approaches, but as a starting point for doing this in an InstallScript custom action, look for MsiGetProperty and MsiSetProperty, StrToNum, and Sprintf.

how to encrypt and decrypt with AES CBC 128 in Elixir

I have an app in Rails with following methods to encrypt and decrypt a text and communicate with java clients.
def encrypt(string, key)
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher.encrypt
cipher.padding = 1
cipher.key = hex_to_bin(Digest::SHA1.hexdigest(key)[0..32])
cipher_text = cipher.update(string)
cipher_text << cipher.final
return bin_to_hex(cipher_text).upcase
end
def decrypt(encrypted, key)
encrypted = hex_to_bin(encrypted.downcase)
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher.decrypt
cipher.padding = 1
cipher.key = hex_to_bin(Digest::SHA1.hexdigest(key)[0..32])
d = cipher.update(encrypted)
d << cipher.final
rescue Exception => exc
end
def hex_to_bin(str)
[str].pack "H*"
end
def bin_to_hex(str)
str.unpack('C*').map{ |b| "%02X" % b }.join('')
end
I need do the same in Elixir for phoenix framework. Since I'm new to Elixir I couldn't find a way for that.
I Found that Elixir uses Erlang's :crypto Module for that. In documentations there was no method for AES CBC encryption.
The block_encrypt/4 function from the Erlang crypto module is the function you want. Unlike the Ruby OpenSSL bindings, the Erlang code doesn’t handle padding, so you will need to do that yourself before encrypting (and remove it after decrypting).
NOTE: As of Erlang v23, the block_encrypt/4 and block_decrypt/4 functions (and their /3 sisters) are deprecated and will be removed from the Erlang crypto module in Erlang v24. The new API functions that have replaced them are crypto_one_time/4 and crypto_one_time/5 and these functions should be used for all new Erlang/Elixir programs. The new API functions support IVs and other improvements over the old functions.
However, unless this is just a toy app for learning purposes, I would recommend not doing this kind of crypto stuff yourself if you can avoid it. Rather you should find a higher level API that takes care of the various details where you can go wrong. I have listed some potential issues with your code as it is below, as well as a suggestion of what to do instead.
The padding that OpenSSL uses (sometimes called PKCS7 padding) is fairly simple. First you need to work out how many bytes you need to add to your data to make the length into a multiple of the block size (16 for AES). Then you simply add that many bytes of that value to the end. For example if your data was 14 bytes long then you would need to add two bytes, and each of those bytes would have the value 0x02 (2 bytes each with value 2). Note that you always add padding, so if your data is already a multiple of 16 byte then you add another 16 bytes (all with value 0x10).
To strip the padding you simply look at the value of the last byte and remove that many bytes from the end (you should probably check that the padding is correct too, i.e. all the bytes have the expected value).
Here is a simple implementation in Elixir (there may be a better / clearer / more idiomatic way to do this):
# These will need to be in a module of course
def pad(data, block_size) do
to_add = block_size - rem(byte_size(data), block_size)
data <> to_string(:string.chars(to_add, to_add))
end
def unpad(data) do
to_remove = :binary.last(data)
:binary.part(data, 0, byte_size(data) - to_remove)
end
You can now use these along with the :crypto.block_encrypt function to get AES CBC encryption like your Ruby code:
# BAD, don't do this!
# This is just to reproduce your code, where you are not using
# an initialisation vector.
#zero_iv to_string(:string.chars(0, 16))
#aes_block_size 16
def encrypt(data, key) do
:crypto.block_encrypt(:aes_cbc128, key, #zero_iv, pad(data, #aes_block_size))
end
def decrypt(data, key) do
padded = :crypto.block_decrypt(:aes_cbc128, key, #zero_iv, data)
unpad(padded)
end
Some issues
Here are some potential problems with your code. This is not an exhaustive list, just some things I noticed (I am not an expert in crypto).
No authentication. Unless you’re checking the authentication in another method before the code you show, then you don’t have any authentication of the messages. This is very bad. You are exposing yourself to potential padding oracle attacks (where an attacker could decrypt the messages) and things like bit-flipping attacks, where an attacker can send specially modified messages that your code might not recognise as bad, and cause some undesired action to take place.
You should be using something like HMAC. But even if you decide to use a HMAC, there are still several questions you need to work out. Where does the HMAC key come from? Can we use the same key for encryption and authentication? Do we calculate the HMAC over the plaintext or the ciphertext? Should it cover the IV as well?
No Initialisation Vector. CBC mode should make use of an initialisation vector, or IV. In the Ruby OpenSSL bindings if you don’t specify one it just uses zero bytes (which is why we needed to create the #zero_iv in the code above. Each message should have its own IV. This can just be a random series of bytes, and doesn’t need to be kept secret (it can just be sent prepended to the ciphertext).
Weak key generation. I could be wrong with this one, but since you are calculating the SHA1 hash of the provided key argument to use as the encryption/decryption key it suggest that this argument is actually a password. If this is the case then you should be using a better key derivation function (and if not then what’s the purpose of the hashing?). If you are using an easy for a human to remember password (or a single hash of one) you could be vulnerable to brute force attacks where an attacker tries lots of dictionary words as the key.
You should be using a proper key derivation function, such as PBKDF2. Even then you will still have complications since you might need two keys (encryption and authentication), so you need to work out how to generate them both.
What to use instead
If possible you should look for a higher level library that takes into account these factors and provides a simpler API. I would recommend Libsodium, which has bindings for many languages including Ruby, Elixir, Erlang, and Java/Android.
I'd recommend not using CBC mode directly but use GCM mode as this will provide authentication as well.
In Elixir (for a 256bit AES key)
# Gen once (see also https://hexdocs.pm/plug/Plug.Crypto.KeyGenerator.html#content)
k = :crypto.strong_rand_bytes(32)
# Gen every time you encrypt a message
iv = :crypto.strong_rand_bytes(32)
{ct, tag} = :crypto.block_encrypt(:aes_gcm, k, iv, {"AES128GCM", msg})
payload = Base.encode16(iv <> tag <> ct)
To decrypt:
<<iv::binary-32, tag::binary-16, ct::binary>> = Base.decode16!(payload)
:crypto.block_decrypt(:aes_gcm, k, iv, {"AES128GCM", ct, tag})
Here is what I use for ECB, CBC should be the same with the added need to pass the previous block cipher in the accumulator. Don't forget that you also need to write a function to pad the term to 16 byte blocks(the ruby seems to do that automatically).
Key = "12345678"
AES_ECB_Encrypt =
fun Crypt(<<Block:16/binary, Rest/binary>>, Acc) ->
NewAcc = erlang:iolist_to_binary( [Acc, crypto:block_encrypt(aes_ecb, Key, Block)] ),
Crypt(Rest, NewAcc);
Crypt(_, Acc) ->
Acc
end,
AES_ECB_Encrypt(<<"hello00000000000">>, <<>>)
JOSE.JWA component from JOSE package has block_decrypt/4 and block_encrypt/4 functions.
iex> JOSE.JWA.crypto_supports()
[ciphers: [aes_cbc: 128, aes_cbc: 192, aes_cbc: 256, aes_ecb: 128, aes_ecb: 192,
aes_ecb: 256, aes_gcm: 128, aes_gcm: 192, aes_gcm: 256,
chacha20_poly1305: 256],
hashs: [:md5, :poly1305, :sha, :sha256, :sha384, :sha512, :shake256],
public_keys: [:ec_gf2m, :ecdh, :ecdsa, :ed25519, :ed25519ph, :ed448, :ed448ph,
:rsa, :x25519, :x448], rsa_crypt: [:rsa1_5, :rsa_oaep, :rsa_oaep_256],
rsa_sign: [:rsa_pkcs1_padding, :rsa_pkcs1_pss_padding]]
Thanks #matt, I wrote my AES_ECB in Elixir.
Hope that it helps you, CBC should be the same.
def encrypt(data, key) do
:crypto.block_encrypt(:aes_ecb, key, pad(data, #aes_block_size))
end
# PKCS5Padding
defp pad(data, block_size) do
to_add = block_size - rem(byte_size(data), block_size)
data <> :binary.copy(<<to_add>>, to_add)
end
def decrypt(data, key) do
padded = :crypto.block_decrypt(:aes_ecb, key, data)
unpad(padded)
end
defp unpad(data) do
to_remove = :binary.last(data)
:binary.part(data, 0, byte_size(data) - to_remove)
end

Why don't I need a 32 bit key, or initialization vector for NodeJS crypto?

I was working interchangeably with Node's crypto library and Ruby's OpenSSL library.
The challenge I was coming across was that I could encrypt usingaes256 in both libraries.
However, in node using the crypto.createDecipher('aes256', key) I could have a key that was less than 32 bits long, but ruby would throw an error saying the key is not long enough when using:
cipher = OpenSSL::Cipher.new 'aes256'
cipher.encrypt
key = 'geeses'
I also don't have to set an initialization vector for node, but ruby seems to set one under the covers. I'm pretty new to this crypto stuff, what's going on here?
While #mscdex answers is perfectly, I want to add how to get a cipher with a specific key when your algorithm does not require Initialization Vector using the crypto.createCipheriv or crypto.createDecipheriv
Taking the case of AES-256-ECB where chaining is not done and hence IV is not used.
You can pass empty Buffer as IV.
var data = "plaintext";
const key = crypto.randomBytes(32);
var iv = new Buffer('');
var cipher = crypto.createCipheriv('AES-256-ECB',key,iv);
var encrypted = cipher.update(data,'utf8','base64');
encrypted += cipheriv.final('base64');
console.log('encrypted AES-256-ECB',encrypted);
And decrypt fairly simply using the same pattern:
var decipheriv = crypto.createDecipheriv('AES-256-ECB',key,iv);
var decryptediv = decipheriv.update(encrypted,'base64','utf8');
decryptediv += decipheriv.final('utf8');
console.log('decrypted base64 aes-256 ',decryptediv);
When you use crypto.createDecipher(), the value you pass as the second argument is a password from which a key and IV will be derived (using one iteration of MD5 hashing). This is accomplished by using EVP_BytesToKey() to create those two values. OpenSSL knows the correct lengths both values need to be because the cipher is also passed to EVP_BytesToKey().
So most likely the Ruby function is more analogous to node's crypto.createDecipheriv() which accepts both a key and an IV (which need to be the right lengths for the cipher).

Ruby OpenSSL AES-128-CTR

I can't figure out what I am doing wrong here trying to decrypt a string of hex values with a given key using ruby's OpenSSL cipher AES-128-CTR.
I am using the gem hex_string to convert my hex to bytes
ctrkey = "36f18357be4dbd77f050515c73fcf9f2"
ciphertext3 = "69dda8455c7dd4254bf353b773304eec0ec7702330098ce7f7520d1cbbb20fc3\
88d1b0adb5054dbd7370849dbf0b88d393f252e764f1f5f7ad97ef79d59ce29f5f51eeca32eabedd9afa9329"
cipher2 = OpenSSL::Cipher.new('AES-128-CTR')
cipher2.decrypt
ctrkey = ctrkey.to_byte_string
cipher2.key = ctrkey
iv = cipher2.random_iv
cipher2.iv = iv
ciphertext3 = ciphertext3.to_byte_string
plain = cipher2.update(ciphertext3) + cipher2.final
puts "plaintext of Q3: #{plain}"
I know I am missing something small because I have similar code implementing AES-128-CBC. Do I need to have a counter that increments the IV for each block of 128 bytes in the ciphertext?
No, you're not missing something small, you are missing something huge.
Instead of using the same IV as used for encryption, you are generating a new one. For CTR, if the IV is random then each counter value is different, resulting in random looking output.
Often the IV (or nonce in the case of CTR) is prefixed to the ciphertext. For CTR that may be fewer bytes than 16 - although that is still the most probable size to try.

Resources