Decryption of AES created with sjcl.js in ruby - javascript

Hi so lets assume that client-side has a key that is not transfered via the same channel as the encrypted data.
What I am trying to accomplish is to decrypt the result of Stanford Javascript Crypto Library (sjcl) in ruby. or for a generalisation in any other language that has a crypto library that has support for AES.
Here is what I am doing in javascript:
sjcl.encrypt('stack-password', 'overflow-secret')
And this is what I get in return:
{
"iv": "Tbn0mZxQcroWnq4g/Pm+Gg",
"v": 1,
"iter": 1000,
"ks": 128,
"ts": 64,
"mode": "ccm",
"adata": "",
"cipher": "aes",
"salt": "pMQh7m9Scds",
"ct": "H6JRpgSdEzKUw2qEO1+HwIzAdxGTgh0"
}
So what I'm actually asking is, which of these parameters I need(assuming the server already has the "stack-password" key) in order to decrypt the secret server-side, and which library should I use?
May be having AES decryption library is not enough?

The following can not be negotiated a head of time (or hard coded).
ct: cipher-text your encrypted data, obviously
iv: initialization vector, should be unique and not be reused with the same key for AES-CCM
salt: random, and is used to create the key from password with Pbkdf2
adata: additional authenticated data, is plain text data that you want to include but ensure that it has not been tampered when using AES-CCM. If you aren't ever going to include any data then you can ignore it (you would have had to pass it with the plaintext in sjcl).
The following you can negotiated a head of time (or hard coded), in fact you shouldn't plug these values in on the server encryption api, when transmitted unauthenticated and expect security. adata wouldn't be a bad place for v, iter, or ks if you wanted it to be changeable based on the client
iter: iterations for Pbkdf2, this just needs to be high enough to slow down bruteforce on your password needs to change with the speed of hardware in the future.
ks: keysize to know what size key to generate with Pbkdf2, needs to change with the amount of security in future
ts: tagsize to know what size authentication tag is part of your cipher text
cipher: If you will only support AES, then you can just assume.
mode: if you will only support AES-CCM than you can just assume.
v: If you will only support one version of sjcl in the future than you can just assume.
With ruby using the OpenSSL library seems like it could work as long as your OpenSSL supports AES-128-CCM puts OpenSSL::Cipher.ciphers to check. And it does support generating keys using pbkdf2, but you do need to use Sha256 digest to be compatible with sjcl, which is again dependent on your version of openssl.

For those coming here from Google, I managed to use the sjcl library to encrypt on Appcelerator's Titanium and decrypt on Ruby/Rails.
Encrypt (javascript):
var data = SJCL.encrypt('your key here',
'plain text',
{ mode: 'gcm',
iv: SJCL.random.randomWords(3, 0) });
The important bit is using a smaller IV.
Decrypt (ruby):
def self.decrypt(h)
h = HashWithIndifferentAccess.new(JSON.parse(h))
key = OpenSSL::PKCS5.pbkdf2_hmac('your key here', Base64.decode64(h[:salt]), 1000, h[:ks]/8, 'SHA256')
puts "Key: #{key.unpack('H*')}"
puts "Salt: #{Base64.decode64(h[:salt]).unpack('H*')}"
c = OpenSSL::Cipher.new("#{h[:cipher]}-#{h[:ks]}-#{h[:mode]}")
c.decrypt
c.key = key
c.iv = Base64.decode64(h[:iv])
puts "IV: #{Base64.decode64(h[:iv]).unpack('H*')}"
c.auth_data = ""
c.update(Base64.decode64(h[:ct]))
end

Alternatively, I wrote up a translation of SJCL into pure Ruby (at least for the CCM parts). This doesn't require updating OpenSSL, but it's a bit slower.
You can find it at https://github.com/mdp/sjcl_rb
Hope this helps

Related

Decrypting OAEP RSA In JavaScript (Like PHP's openssl_private_decrypt)

I'm currently encrypting data in PHP as follows:
//$sMessage is the input to encrypt.
//$sPublicKey is the public key in PEM format.
openssl_public_encrypt($sMessage, $sEncrypted, $PublicKey, OPENSSL_PKCS1_OAEP_PADDING);
//$sEncrypted will now store the resulting text.
Here's an example of the code used to generate the key in PHP:
$aConfig = array(
"digest_alg" => "sha256",
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
$res = openssl_pkey_new($aConfig);
//$sPassPhrase is the pass phrase.
openssl_pkey_export($res, $sPrivateKey, $sPassPhrase);
//$sPrivateKey is the key (in PEM format).
And my decryption is using a private key that's protected by a pass phrase.
//$sPrivateKeyWithPassPhrase is the protected private key in PEM format.
//$sPassPhrase is the pass phrase to protect the key.
$newRes = openssl_pkey_get_private($sPrivateKeyWithPassPhrase, $sPassPhrase);
//$sEncrypted is the encrypted text (ciphertext).
openssl_private_decrypt($sEncrypted, $sDecrypted, $newRes, OPENSSL_PKCS1_OAEP_PADDING);
//$sDecrypted is the decrypted text (plaintext)
I've also found out how to decrypt with Python using the cryptography library (which you can learn about here):
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
#passphrase is the pass phrase
passphrase_bytes = bytes(passphrase, 'utf-8')
#privatekey is the encrypted private key
private_key = serialization.load_pem_private_key(
privatekey,
password=passphrase_bytes,
)
#ciphertext is the text to be decrypted
plaintext = private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
)
)
#plaintext will be the decrypted result.
However, I've been struggling to find a comparable function in JavaScript.
The best answer I found so far was this one: RSA Encryption Javascript
However, it doesn't seem like keys are in PEM format and when I go to the pidCrypt homepage to get more documentation, it's a 404 error.
I found this one as well: PHP's openssl_sign Equivalent in Node JS
The NodeJS library in general seems to have the right functions but when I went through the NodeJS documentation the Quick Start Guide seems to suggest that in order to use NodeJS I have to first install software and set up a web server. According to how to run node.js client on browser, it's not possible "Node.js is not browser javascript. There are many parts of it that use OS features not available in a browser context." (I'm hoping for something I can run in a native browser on the client-side.)
Someone suggested in a comment that I could use WebCrypto. After some struggle to figure out where to download it, I found it's actually native to the browsers (which was a pleasant surprise). However, I haven't been able to figure out how to import a PEM key from the documentation, and when I went to generate a key then export it, the only available formats were "jwk" (some sort of JavaScript object), "spki" (some kind of array buffer), and "pkcs8" (which I only got DOMException: key is not extractable). Nothing looks remotely like the OpenSSL PEM format. (I believe this is PKCS1.)
When I did "crypto.subtle.importKey" it wanted 5 different parameters (format, keyData, algorithm, extractable, keyUsages). None of those accept the PEM string. After a bit of struggle I did find SubtleCrypto importKey from PEM which confirmed that it's possible if you do fancy manipulation to import a PEM public key, however that's for public keys in PEM format and I have no idea how to handle pass phrases (and really I'd rather not program that all by hand). Then I found How can I import an RSA private key in PEM format for use with WebCrypto? and Javascript, crypto.subtle : how to import RSA private key? however those basically told me it's not possible unless I go and convert the private key to PKCS8 using a command line OpenSSL (that also has to be installed separately and I remember was a huge pain to get on Windows). And once I get past that hurdle, I'm still not sure how I would go about getting an equivalent pass phrase protection in PKCS8. I'm hoping there's something similar that ideally supports PKCS1 to do the same as the PHP and Python code above, and I can run it 100% in the native browser. (Importing more JavaScript code is okay.)
I'd ideally like to find a simple working JavaScript example for OpenSSL RSA decryption, equivalent to the PHP or Python ones above. If I have the private key PEM string, the pass phrase, and the cipher text it can give back the plaintext.
Thank you very much! I appreciate all your help a ton and you can save me many hours!
PKCS#1 Versus PKCS#8
Note that while private keys generated without pass phrases in PHP are PKCS#1, the private key generated from PHP with a pass phrase is actually PKCS#8 (see this link for how to determine which standard applies to a private key).
Third Party Library Required
In order to work with PEM-format keys, a custom JavaScript library (or other custom code) is required. There are various libraries at this link which you can look at. Many are not maintained anymore.
This example uses the Forge JavaScript library, but you could also use a different library.
Installing/Importing Forge
While there are various ways to build the Forge environment, the easiest is to add the following HTML code prior to where you need to do encryption/decryption:
<script src="https://cdn.jsdelivr.net/npm/node-forge#0.7.0/dist/forge.min.js"></script>
Performing RSA Decryption
The following JavaScript code will perform decryption using the Forge library with a private key that's passphrase-protected:
// Import an PKCS#8 encrypted key from PHP
//sEncPkcs8Pem = The encrypted PEM key in a string.
//sPassPhrase = A pass phrase string to use for decryption.
//bCipherText = The encrypted cipher text (string of bytes).
var encryptedPrivateKeyInfo = forge.pki.encryptedPrivateKeyFromPem(sEncPkcs8Pem);
var privateKeyInfo = forge.pki.decryptPrivateKeyInfo(encryptedPrivateKeyInfo, sPassPhrase);
var pkcs8Pem = forge.pki.privateKeyInfoToPem(privateKeyInfo);
var privateKey = forge.pki.privateKeyFromPem(pkcs8Pem);
var sDecrypted = privateKey.decrypt(bCipherText, 'RSA-OAEP');
//sDecrypted will store the result.
Full Working Example
The following is a full working example with a 4096-bit key. The JavaScript imports an encrypted private key (PKCS#8 format, PEM encoded) and successfully decrypts a ciphertext.
The encrypted key and ciphertext were both generated using the posted PHP code.
The JavaScript library applied is forge.
<script src="https://cdn.jsdelivr.net/npm/node-forge#0.7.0/dist/forge.min.js"></script>
<p style="font-family:'Courier New', monospace;" id="pt"></p>
<script>
var encPkcs8Pem = `-----BEGIN ENCRYPTED PRIVATE KEY-----
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
ij9mm4GhutigIoP4Wx/NUA==
-----END ENCRYPTED PRIVATE KEY-----`; // 4096 bit key!
// Import an PKCS#8 encrypted key from PHP
var encryptedPrivateKeyInfo = forge.pki.encryptedPrivateKeyFromPem(encPkcs8Pem);
var privateKeyInfo = forge.pki.decryptPrivateKeyInfo(encryptedPrivateKeyInfo, prompt("Enter pass phrase", "testkey"));
var pkcs8Pem = forge.pki.privateKeyInfoToPem(privateKeyInfo);
var privateKey = forge.pki.privateKeyFromPem(pkcs8Pem);
// Decrypt ciphertext from PHP
var ciphertext = forge.util.decode64("lHIspL7aAlnbUdQn6VVY2AS3Ybs/H0fK7N4owfPe6OtzMmX88XOI+2FyEnNyCal6krnfdHYa2JWeWAsyLf484sT2BmvNPxWyYsrnpulKog0lhkeUFppGdhdOHCf7KU47L5p6uYtZmv926ACfhwqMo4M66n/Gkliocol+esavVcfZcjuAw+ELnYkx/TufinR772jBLxVWueGyGcEokI9osDCDpXptmJqpiNRzIrf2kHdk2F/bXRPyE+vrQKFzbyMSpBj3xKtsYDiJ5xDq0qtYY+GCRJNMucPFqOTiFN18EE3z8y/tq5n9Ae/VS8wMxn51rB5JBRmYg3vlM4JLolqNnP3t8OD+DqAeavYjAnairLV0esxzjdkUDpfmFrsYBZWfe5rMzO55drDdsQpGI9LGeX5/TUQpqJWBNZ7QXnbxEiQ2ATXH+ToaeZe3mMkPKuogKM2aEViLzdYB0plbQnoWPAG4OH9pYPGj1KYwdQG5UoF0Ew6Y93uSG3uJ6mcdZZ9gJA0+hpVHOchuAvtZ+vuIV4tzWGfvvnAND54U5sS95EGRj8w/ukgswgOfg/k9iKN3Xlh5N23BYyoLajZVN92cVw6I3XpNOWjlHcO04EhLXaxIeaEMsYYqq3GDBtxpbXfARVjokMeKkKUKNwVscpVneq6WXGtfiMGwXpF8MfoGigE=");
var decrypted = privateKey.decrypt(ciphertext, 'RSA-OAEP');
document.getElementById("pt").innerHTML = "Decrypted: " + decrypted;
</script>
Note: I'd like to give credit to Topaco for their considerable help and original answer, which this one is based off of.

Unable to verify a raw UInt8Array ECDSA (secp256r1) message/signature/pubkey using WebCrypto API (subtlecrypto), generated from secure embedded chip

I am using a secure embedded chip (ATECC508) to generate an ECDSA message/signature combination using the secp256r1 curve.
The information from the chip outputs in Uint8Arrays: message[32], signature[64], and publickey[64];
The public key is in raw byte format, giving both X/Y coordinates in a 64 byte array, without the 0x04 padding on the front.
The public key will only successfully import into WebCrypto as long as I pad the 64 byte array with 0x04 on the front.
However, when I attempt to verify the message and signature, it always fails.
I created a simulator script to help debug. It uses WebCrypto to generate the message, signature, and public key instead of the crypto chip. The simulator generates a 32 byte Uint8array message array, and signs the message returning a 64byte Uint8array for its signature. The public key that WebCrypto generates is also verified to have the 0x04 padding in the front, before the 64bytes representing the X/Y coordinates of the curve. This means I believe that the information generated by the simulator should be in the exact same format as what the chip provides.
Any message, signature and public key which is generated from WebCrypto can be successfully verified...but I cant authenticate the information from the chip...it is always unsuccessful.
I have no reason to suspect the values from the chip are wrong, and I have generated many messages/signatures from the chip...none of them work. I have also tried a second chip with the same result.
Output example from chip (does not verify):
uint8_t message[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
uint8_t signature[64] = {
0xD6,0x82,0x25,0xCC,0x68,0x6F,0x4F,0x84,0x91,0x48,0x63,0x6E,0x67,0x3C,0xD4,0xC0,0xF8,0xE5,0x9D,0x7B,0xAD,0x6B,0xB3,0xF1,0x1C,0xDB,0x90,0xB7,0x1A,0x5E,0x43,0xCF,0xD8,0xC3,0x8C,0x77,0x74,0xE2,0xA0,0x29,0xFF,0x43,0x22,0x7D,0xF9,0x41,0x56,0x12,0x8A,0x1B,0xEA,0x4D,0x57,0x8A,0x37,0x9C,0x6A,0x85,0x0A,0x56,0xBE,0xEC,0x1A,0x69
};
uint8_t publicKey[64] = {
0x39,0xC3,0xDD,0x74,0x13,0x17,0x29,0x44,0x6D,0xC1,0xB3,0xDA,0x67,0xD4,0x9F,0xC0,
0x46,0xFC,0xBF,0x07,0x2F,0xCC,0x5B,0x9F,0xA5,0x1C,0x05,0xB9,0x74,0x30,0x7F,0x96,
0x9C,0x40,0x3B,0x16,0x35,0xF0,0x44,0x9F,0x02,0xBD,0x42,0x27,0x51,0xE3,0x31,0x21,
0xA4,0x43,0x4F,0x15,0x2F,0x2B,0x2B,0x2A,0x3F,0x67,0x52,0x19,0xC5,0xD9,0x25,0xF6,
};
Note: The public key above is padded with 0x04 before importing, otherwise WebCrypto throws an error.
Output example from WebCrypto simulator javascript console (this verifies OK):
Message to import in hex: 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F
Message Uint8Array conversion: Uint8Array(32) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, … ]
the signature in hex: 5d4575a19cad9a56b554bafbb1db212d8badd48b5d29f6556fa33f0e8650175f48157e70377c7aa5f97e55bc455b6520060dc86f32a00577a254a97e1b7b394c
the signature buffer:
Uint8Array(64) [ 93, 69, 117, 161, 156, 173, 154, 86, 181, 84, … ]
public key in hex:
04d18669eb64b54118fbcdacbf79d2f185383abadb1982382a51675650f4596d9f917a7b9b215f2424418b4e7c824500dc29ea507f08b4ef51ca35f58de75ca274
note: above are hex strings, but I am converting to a UintArray8 before calling WebCrypto API using. PublicKey is converted into the correct CryptoKey() type before using to verify.
The javascript code I am using for verification:
let result = await window.crypto.subtle.verify({
name: "ECDSA",
hash: { name: "SHA-256" }
},
publicKey, sigbuffer, msgbuffer);
And finally, the import:
return window.crypto.subtle.importKey(
'raw',
key, {
name: 'ECDSA',
namedCurve: 'P-256'
},
true, ["verify"]
);
My intention is to be able to import and verify the information from the chip successfully into the WebCrypto API, but I have so far been unsuccessful. At the moment I am completely stuck and would be extremely grateful for any help...thanks in advance
Verification of the posted data is successful if the unhashed data is verified (I tested this with a C# solution, see online here). In conclusion, this proves that the unhashed data was signed.
However, signing the unhashed data is uncommon, generally the hashed data is signed. On the one hand, this has purely practical reasons (asymmetric algorithms can generally only be used to encrypt short messages), but on the other hand it is also necessary for security reasons, s. e.g. here.
For this reason, many libraries perform hashing implicitly, which is why a digest must be specified. This is also true for the WebCrypto API, causing the hashed data to be verified with a signature generated for the unhashed data, which of course fails.
It is now also not possible to derive data whose hash just generates your message in order to then use this data as input for the WebCrypto API. Cryptographic hashes are supposed to prevent such an inference.
For these reasons, the data cannot be verified using the WebCrypto API!
So you need a JavaScript library that does not hash implicitly, e.g. the SJCL:
var pubHex = '39c3dd74131729446dc1b3da67d49fc046fcbf072fcc5b9fa51c05b974307f969c403b1635f0449f02bd422751e33121a4434f152f2b2b2a3f675219c5d925f6';
var pub = new sjcl.ecc.ecdsa.publicKey(
sjcl.ecc.curves.c256,
sjcl.codec.hex.toBits(pubHex)
)
var msgHex = '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f';
var msg = sjcl.codec.hex.toBits(msgHex)
var sigHex = 'd68225cc686f4f849148636e673cd4c0f8e59d7bad6bb3f11cdb90b71a5e43cfd8c38c7774e2a029ff43227df94156128a1bea4d578a379c6a850a56beec1a69';
var sig = sjcl.codec.hex.toBits(sigHex)
var verified = false;
try {
var verified = pub.verify(msg, sig)
} catch (e) {}
console.log('Verification:', verified)
<script src="https://cdn.jsdelivr.net/npm/sjcl-full#1.0.0/sjcl.min.js"></script>
With this, the posted data is successfully verified.

how to fix yandex api code leak in twitter bot?

I'm a beginner in programming and I'm developing a bot on node-red in the ibm cloud, and I've had problems with the return of the yandex translation API. It returns part of the api code in the tweets, which is not pleasant at all.
The api of yandex allows the api to return in json or xml, I tried both and I could not solve the problem. The bot in question has other api's in use and I was able to configure them normally, something that does not occur with the result of that, which would be the final result for the tweet to be released.
to send the translation to be made use the following request in a function of node red:
var translate = msg.method ='GET';
msg.url = "https://translate.yandex.net/api/v1.5/tr/translate?key= *API KEY* &text=" + recipe + "&lang=pt"
return [msg,null];]
in the next block, and the last one before sending the message, I'm using something like:
var yandex= msg.payload;
yandex = 'a' + msg.payload.text;
return msg;
and this makes me return something like this in the public tweet
"<?xml version="1.0" encoding="utf-8"?>
< Translation code="200" lang="en-pt"><text>é uma receita com Estilo grego Desfrutar de sua comida!</text>< / T"
hope to remove all this code that is being sent to output and only send the translation to the tweet, which is what is inside .
Forgive me code redundancies, but I do not know javascript fully and my college is teaching languages a bit old, like pascal.
You can perform the translation without writing a single line of code. This is the power of Node-RED. All you need is to send a properly formatted payload to an http request node. In the configuration dialog of this node tick the option Append msg.payload as query string parameters. You will get the translation by extracting msg.payload.text.
The payload to send to the http request has to be structured as follows:
{
"key": "you key",
"lang": "en-pt",
"format": "plain",
"text": "Life is like a game"
}
The code you posted has syntax errors and will not produce the output you want.
I recommend you to study a little bit more Node-RED and ask questions their forum in case you did not understand what is explained above.

AWS S3 browser upload using HTTP POST gives invalid signature

I'm working on a website where the users should be able to upload video files to AWS. In order to avoid unnecessary traffic I would like the user to upload directly to AWS (and not through the API server). In order to not expose my secret key in the JavaScript I'm trying to generate a signature in the API. It does, however, tell me when I try to upload, that the signature does not match.
For signature generation I have been using http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
On the backend I'm running C#.
I generate the signature using
string policy = $#"{{""expiration"":""{expiration}"",""conditions"":[{{""bucket"":""dennisjakobsentestbucket""}},[""starts-with"",""$key"",""""],{{""acl"":""private""}},[""starts-with"",""$Content-Type"",""""],{{""x-amz-algorithm"":""AWS4-HMAC-SHA256""}}]}}";
which generates the following
{"expiration":"2016-11-27T13:59:32Z","conditions":[{"bucket":"dennisjakobsentestbucket"},["starts-with","$key",""],{"acl":"private"},["starts-with","$Content-Type",""],{"x-amz-algorithm":"AWS4-HMAC-SHA256"}]}
based on http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html (I base64 encode the policy). I have tried to keep it very simple, just as a starting point.
For generating the signature, I use code found on the AWS site.
static byte[] HmacSHA256(String data, byte[] key)
{
String algorithm = "HmacSHA256";
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(algorithm);
kha.Key = key;
return kha.ComputeHash(Encoding.UTF8.GetBytes(data));
}
static byte[] GetSignatureKey(String key, String dateStamp, String regionName, String serviceName)
{
byte[] kSecret = Encoding.UTF8.GetBytes(("AWS4" + key).ToCharArray());
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
Which I use like this:
byte[] signingKey = GetSignatureKey(appSettings["aws:SecretKey"], dateString, appSettings["aws:Region"], "s3");
byte[] signature = HmacSHA256(encodedPolicy, signingKey);
where dateString is on the format yyyymmdd
I POST information from JavaScript using
let xmlHttpRequest = new XMLHttpRequest();
let formData = new FormData();
formData.append("key", "<path-to-upload-location>");
formData.append("acl", signature.acl); // private
formData.append("Content-Type", "$Content-Type");
formData.append("AWSAccessKeyId", signature.accessKey);
formData.append("policy", signature.policy); //base64 of policy
formData.append("x-amz-credential", signature.credentials); // <accesskey>/20161126/eu-west-1/s3/aws4_request
formData.append("x-amz-date", signature.date);
formData.append("x-amz-algorithm", "AWS4-HMAC-SHA256");
formData.append("Signature", signature.signature);
formData.append("file", file);
xmlHttpRequest.open("post", "http://<bucketname>.s3-eu-west-1.amazonaws.com/");
xmlHttpRequest.send(formData);
I have been using UTF8 everywhere as prescribed by AWS. In their examples the signature is on a hex format, which I have tried as well.
No matter what I try I get an error 403
The request signature we calculated does not match the signature you provided. Check your key and signing method.
My policy on AWS has "s3:Get*", "s3:Put*"
Am I missing something or does it just work completely different than what I expect?
Edit: The answer below is one of the steps. The other is that AWS distinguish between upper and lowercase hex strings. 0xFF != 0xff in the eyes of AWS. They want the signature in all lowercase.
You are generating the signature using Signature Version 4, but you are constructing the form as though you were using Signature Version 2... well, sort of.
formData.append("AWSAccessKeyId", signature.accessKey);
That's V2. It shouldn't be here at all.
formData.append("x-amz-credential", signature.credentials); // <accesskey>/20161126/eu-west-1/s3/aws4_request
This is V4. Note the redundant submission of the AWS Access Key ID here and above. This one is probably correct, although the examples have capitalization like X-Amz-Credential.
formData.append("x-amz-algorithm", "AWS4-HMAC-SHA256");
That is also correct, except it may need to be X-Amz-Algorithm. (The example seems to imply that capitalization is ignored).
formData.append("Signature", signature.signature);
This one is incorrect. This should be X-Amz-Signature. V4 signatures are hex, so that is what you should have here. V2 signatures are base64.
There's a full V4 example here, which even provides you with an example aws key and secret, date, region, bucket name, etc., that you can use with your code to verify that you indeed get the same response. The form won't actually work but the important question is whether your code can generate the same form, policy, and signature.
For any given request, there is only ever exactly one correct signature; however, for any given policy, there may be more than one valid JSON encoding (due to JSON's flexibility with whitespace) -- but for any given JSON encoding there is only one possible valid base64-encoding of the policy. This means that your code, using the example data, is certified as working correctly if it generates exactly the same form and signature as shown in the example -- and it means that your code is proven invalid if it generates the same form and policy with a different signature -- but there is a third possibility: the test actually proves nothing conclusive about your code if your code generates a different base64 encoding of the policy, because that will necessarily change the signature to not match, yet might still be a valid policy.
Note that Signature V2 is only suported on older S3 regions, while Signature V4 is supported by all S3 regions, so, even though you could alternately fix this by making your entire signing process use V2, that wouldn't be recommended.
Note also that The request signature we calculated does not match the signature you provided. Check your key and signing method does not tell you anything about whether the bucket policy or any users policies allow or deny the request. This error is not a permissions error. It will be thrown prior to the permissions checks, based solely on the validity of the signature, not whether the AWS Access Key id is authorized to perform the requested operation, which is something that is only tested after the signature is validated.
I suggest you to create a pair auth token with permission to POST only, and send an http request like this:
require 'rest-client'
class S3Uploader
def initialize
#options = {
aws_access_key_id: "ACCESS_KEY",
aws_secret_access_key: "ACCESS_SECRET",
bucket: "BUCKET",
acl: "private",
expiration: 3.hours.from_now.utc,
max_file_size: 524288000
}
end
def fields
{
:key => key,
:acl => #options[:acl],
:policy => policy,
:signature => signature,
"AWSAccessKeyId" => #options[:aws_access_key_id],
:success_action_status => "201"
}
end
def key
#key ||= "temp/${filename}"
end
def url
"http://#{#options[:bucket]}.s3.amazonaws.com/"
end
def policy
Base64.encode64(policy_data.to_json).delete("\n")
end
def policy_data
{
expiration: #options[:expiration],
conditions: [
["starts-with", "$key", ""],
["content-length-range", 0, #options[:max_file_size]],
{ bucket: #options[:bucket] },
{ acl: #options[:acl] },
{ success_action_status: "201" }
]
}
end
def signature
Base64.encode64(
OpenSSL::HMAC.digest(
OpenSSL::Digest.new("sha1"),
#options[:aws_secret_access_key], policy
)
).delete("\n")
end
end
uploader = S3Uploader.new
puts uploader.fields
puts uploader.url
begin
RestClient.post(uploader.url, uploader.fields.merge(file: File.new('51bb26652134e98eae931fbaa10dc3a1.jpeg'), :multipart => true))
rescue RestClient::ExceptionWithResponse => e
puts e.response
end

SHA1 varies in java and javascript for same input

Facing an issue while creating SHA1 from javascript and java. The problem is both are different. It is used for validating the client request to web server. That means client send a based64 encoded security key to server and server regenerate the same key and equate both are same. Please find below code for generating secret keys in client and server.
Server
MessageDigest mDigest = null;
try {
mDigest = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
String input = value1 + value1 + server_key;
byte[] result = mDigest.digest(input.getBytes());
String secret = Base64.encodeToString(result, Base64.NO_WRAP);
...
//comparison logic goes here
...
Client (java script)
var input = value1 + value2 + server_key;
//http://code.google.com/p/crypto-js/
var hash = CryptoJS.SHA1(input);
var encoded = base64Encode(hash.toString());
//WEB SERVICE INVOCATION FROM JAVASCRIPT GIES HERE.
The values value1, value1, server_key will be available in both client and server. The issue we are facing is, the SHA1 generated in both client and server is not matching. I understand the issue is in java its using getBytes() and in javascript using string value for generating SHA1. The CryptoJS.SHA1 does not support bytearray as parameter. We cannot change the server code as it is used by many client applications. Any help will be much appreciated.
In Java ->
byte[] result = mDigest.digest(input.getBytes());
and in JavaScript ->
var hash = CryptoJS.SHA1(input);.
I belief this is the problem. In java the parameter is a bytearray and output is also a bytearray. But in javascript the parameter is var (string) and return is also var (string). I 've also compared the output of CryptoJS.SHA1 with some online SHA1 generating tools. The comparison is true. I am not an expert in this area. If you can explain more, it will be more helpful.
I managed it to do in another way. My application is a cordova based application. So generated the sha1 and encoded it from java and objC and invoked it using cordova plugins.

Categories

Resources