How to create AWS signed cookies with elixir? - amazon-web-services

I've been trying to set up CloudFront with signed cookies using elixir. From what I learned the signature must use sha1 encoding. According to AWS in the terminal that would look like this:
cat policy.json | openssl sha1 -sign CloudFront_Key.pem |base64 |tr '+=/' '-_~'
I can't figure out how to do that in elixir. I've been looking into this dependency and tried using :sha1 instead in it's sign method. I did read through here and did see sha, not sha1. Is that how erlang calls it? (I have close to no knowledge about erlang). Tried :sha but I do not get the same key as in the terminal and it does not seem to work. I also noticed that base64 usually adds a few extra chars at the end than the terminal. Not sure what to do.. Resort to try using System.cmd ?
This is the AWS doc I'm following: create & verify signed cookies and create signature for signed cookie

You can use :public_key.sign/3 for this.
For reference,
$ cat pk.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAknEbD26dg4w2JlaN2pmtz7sEBSEjFcfsgcUuV100L60Lz36s
EDySjiTMbhfhKRa46gBvGSEzb8G1l4iD1O5M5mfzEVg/shSOgwVfyvq5XShJBaz6
PcKJ4hsLh/aUIkvnfYWg5zdcPj5FBYT0I+jLnWvNevsolfGwN4rY5S/PuBCUwtVC
m1L3p5bVHvR33tjI4qenHkuRJUnJh4TMLCOTCGHdmZ+yyWJ2rLcYq7yciI0WlRh5
cCI1eYEehUsbcifOyazKNOAmO/9DzXZlo9S0TSYjxrG626mt+j9yD473t/FSfUeg
6Hn67wfGBWAk0HezJ3QGRl9jBAeYhSLbaX7DHQIDAQABAoIBAHo+Blu8d6oe+fjI
2cM389pq/7EUd0gwSmINamCtQenmZux/jjxDhAc5+piQQHlfKV7Um+j7SQeqSN7E
q1+syO6wqTu6UflipZADhXJYFzIHdeVR/tZdNWJUNyz5DbEPcZ7bVHSORucCbfVs
hawQISA4pB9b1wZL6VCEDAhM//ViRgBA3k6tBu5jNi+I1WXE9nrat4DSDV3uklfo
f5KfHLhbHgrl7R7jnAyWBya6O6Pw0r/3FeWrjo6W0wht5BprX4HSgJvpWtqjhodA
iucWjmWV7Gt++HBRX0NEQozajQ7fdfnCOpSD2g8QzVw/5whRBidYngJWLBNt1YQV
UMi7toECgYEAzw4i+m0PnqiExMOSLsvPcEQu0wScOe3vwXTBOcfOS0Uds0kWzZP2
BHQL7IATtN+OAMdtFi1xyN+auikTsZpy1hezUtamfeo75btFhgSrtRXQki5o3pX8
HgDh+31+Ox1RM/Qhm4DSPQ/YImw+HQD+lK9sw3yNZlmPmXwyCARxkCECgYEAtQ75
M5HcBG+zPbPHc4mrOgtQzaUTEs1onu/zDCuFZwYtyBB0RKSLLmacgh/Qcdu13Lli
fdTqego5asKztQfsB88aqkYtdY51dnm+H2sxQ2ef+1D2gbV+sdrsM7gUuVvxBMwv
itFizQ1rgZn/Vee4zfFXEoURjkKfTKT27Hn7A30CgYEAx4ugOiiRPR67lcXFREQ3
jsKnPcbbqRieT5rt/XmKXxAlJ3vw9f76wh/0veBRHae1exq3DwCNAEI/I9oimK94
rMv6joM/wWnUf/qTbi1iLgrwD3Gar6lsaJ4BLBYtaVs/vwowuWTVOPPkIIig8+LZ
dwH5mAyZWWJG+myu6vsdVwECgYAJqT/g4ZKU5gTxcOteneT2Fu572qgW48EGYhVc
++GFas38k+wwUXtfwXfudZYgzTF6EqZPwpG0a2E+8h62tTKCBCoPFemNEUnxRXPA
p26cgyYFOf+9UhrtkJnz9Imejmpg8ChFRwD3ohSveLEoO1IgIxWbVmBmb+WiKFdI
rQWY3QKBgA6Gw/8KB7LxqR3N9/1xgW5b02WVhG7gvFNlSYZMmdd8H3w8MdXCdk22
kJBfrIfkDS6nbF6w4+8q309NPwOqdkt2QNPq6Il1ugWuxFMiNEGHjjT21PrsgEJS
q6K6c+znrnI7O/wijdvEC2n+q+S9h8yv1bT6QfC1Vx88SC9GCBuq
-----END RSA PRIVATE KEY-----
$ cat policy.json
{"Statement":[{"Resource":"https://mycloudfront.net/a.png","​Condition":{"DateLes​sThan":{"AWS:EpochTi​me":1512086400}}}]}
First, read the key:
iex(1)> key = File.read!("pk.pem") |> :public_key.pem_decode |> hd |> :public_key.pem_entry_decode
...
Then sign the data using :public_key.sign/3:
iex(2)> File.read!("policy.json") |> :public_key.sign(:sha, key) |> Base.encode64
"QjLmx3LASRb1zt9eW/EMywGMXB1SwX/0JrTnLOFulYjcRJ1dpacUZBB/AYI1zwaXPEQTgQ8crNDFgje6fqbLKoNwgcpE9mOK/RdDKi963ztJnD6EmtM60YbROSpjQ/LDupEYgipPNZbjCnRCJcqDX43BadbVR75G3B5mFmAwtRSPdslJ5irVnt9PjoDMdi9DYe1wGhgQkoym1tiKEyaTrH5lyrw+KPdAi1tpzuZ60ZEcQFJJbKqYYdA0SslbUFL71mdLLkQ9xz95JPNpsSY3ZJyJsKpRGFJuaL1aMsdNLxlLD91PpNW15FitBpBnAwuiiEfPrwU14zIxsfFszaM6KA=="
The output is identical to openssl:
$ cat policy.json | openssl sha1 -sign pk.pem | base64
QjLmx3LASRb1zt9eW/EMywGMXB1SwX/0JrTnLOFulYjcRJ1dpacUZBB/AYI1zwaXPEQTgQ8crNDFgje6fqbLKoNwgcpE9mOK/RdDKi963ztJnD6EmtM60YbROSpjQ/LDupEYgipPNZbjCnRCJcqDX43BadbVR75G3B5mFmAwtRSPdslJ5irVnt9PjoDMdi9DYe1wGhgQkoym1tiKEyaTrH5lyrw+KPdAi1tpzuZ60ZEcQFJJbKqYYdA0SslbUFL71mdLLkQ9xz95JPNpsSY3ZJyJsKpRGFJuaL1aMsdNLxlLD91PpNW15FitBpBnAwuiiEfPrwU14zIxsfFszaM6KA==

Related

Trying to encyption file upload cloud storage through the curl

Please help me I am using curl file encrypt upload in cloud storage but file is not uploaded , but when I am trying to upload without encryption key and hash key then file gets uploaded successfully please solve this problem.
curl -X POST --data-binary #OBJECT\
-H "Authorization: Bearer ya12.a0ARrdaM8ZPiR_ukSDNO_VPYAJa2W2O67Ds91CKwwLGnWU1DTZF02K237YsXFCqePCi3xSgD0s_cvhIIc_474-Y3h0bDZof69K0snlOAYlwwQw1fBM2QrBUQRKsQOZj1qHILgcZOxptqBxp0e8mx" \
-H "Content-Type: image/jpg" \
-H "x-goog-encryption-algorithm: AES256" \
-H "x-goog-encryption-key: NSOgD4929vFoA8zawwmkuaizAdtdydWGQRuOeZID+GY=" \
-H "x-goog-encryption-key-sha256:ca2f7dac23426d0ba16911be8911f9b71b1fa7f9ecc53ac87100932677d92319" \
"https://storage.googleapis.com/upload/storage/v1/b/bucketName/o?uploadType=media&name=1.jpg"
Note: I am generating encryption key base64 in php and hash the key
$key = random_bytes(32);
$encodedKey = base64_encode($key);
$hash = hash('SHA256',$key);
I am getting this error
error": {
"code": 400,
"message": "Missing an encryption key, or it is not base64 encoded, or it does not meet the required length of the encryption algorithm.", "message": "Missing an encryption key, or it is not base64 encoded, or it does not meet the required length of the encryption algorithm."
Both x-goog-encryption-key and x-goog-encryption-key should be base64 encoded.
Based on the code in PHP you probably use the encryption key that is base64 encoded (that is OK), but the hash is not base64 encoded.
Try encode the hash at the end of the PHP, with:
base64-hash=base64_encode(hash)
More detail can be found here: https://cloud.google.com/storage/docs/encryption/customer-supplied-keys
Quote:
Include the following HTTP headers in your JSON or XML request:
x-goog-encryption-algorithm string The encryption algorithm to use. You must use the value AES256.
x-goog-encryption-key string An RFC 4648 Base64-encoded string of your AES-256 encryption key.
x-goog-encryption-key-sha256 string An RFC 4648 Base64-encoded string of the SHA256 hash of your encryption key.

How can I verify a pem_cert and signiture (RSA-SHA1)?

I'm trying to check if a signature is valid, in Node.js I would use
https://nodejs.org/api/crypto.html#crypto_verify_verify_object_signature_signatureencoding
I can't seem to find anything for crystal that provides this functionality?
This is currently not implemented in the standard library, but we can find a shard that does it: openssl_ext. This shards closely resembles the Ruby API, so let's get going!
First we need a key:
$ openssl genpkey -algorithm rsa -out key.pem
$ openssl rsa -in key.pem -pubout -out pub.pem
Then we need to create a shard and add the dependency:
$ shards init
$ $EDITOR shards.yml
dependencies:
openssl_ext:
github: randomstate/openssl_ext
$ shards
A small program to sign something:
$ $EDITOR sign.cr
require "openssl_ext"
require "base64"
private_key = OpenSSL::RSA.new File.read(ARGV[0])
digest = OpenSSL::Digest.new("SHA1")
data = ARGV[1]
signature = private_key.sign digest, data
puts Base64.encode signature
$ crystal build sign.cr
And a small program to verify a signature:
$ $EDITOR verify.cr
require "openssl_ext"
require "base64"
public_key = OpenSSL::RSA.new File.read(ARGV[0]), is_private: false
digest = OpenSSL::Digest.new("SHA1")
data = ARGV[1]
signature = Base64.decode STDIN.gets_to_end
puts public_key.verify(digest, signature, data) ? "Valid" : "Invalid"
$ crystal build verify.cr
Let's test our work:
$ ./sign key.pem Hello | ./verify pub.pem Hello
Valid
$ ./sign key.pem Hello | ./verify pub.pem Bye
Invalid

OpenSSL - digital signature generated through code does not match signature generated on command line

UPDATE
I didn't realize this question was still getting eyeballs.
I found and fixed the problem some time ago, and it was basically me not understanding how to do things properly. I wasn't using the right file type for the certificate, I was not reading the private key properly (should have been using PEM_read_RSAPrivateKey, not PEM_read_PrivateKey), I was not computing the signature properly, etc.
It just took me more time than it should have to figure it out.
Thanks for everyone who took the time to answer.
ORIGINAL
I'm working on some code to digitally sign some requests, but the signature I am generating from the code is incorrect, and it doesn't jibe with a signature generated from the command line. I suspect the problem is that I am not reading the private key in properly, but I would expect to get some errors back if that were the case, and I'm not.
This is the first time I've worked with openssl, and I'm sure I'm doing things incorrectly, but I don't yet understand enough to ask intelligent questions.
I've created a MessageSigner class to handle the libcrypto chores. The private key and certificate are attributes of the class:
class MessageSigner
{
...
private:
EVP_PKEY *private_key;
X509 *certificate;
};
I initialize these to NULL when an instance is created:
MessageSigner::MessageSigner() :
private_key( NULL ),
certificate( NULL )
{
}
I load the private key as follows:
void MessageSigner::addKeyFile( const std::string& filename, const std::string& passphrase )
{
FILE *fp = ::fopen( keyfile.c_str(), "r" );
if ( !fp )
// throw exception
private_key = PEM_read_PrivateKey( fp, NULL, NULL, passphrase.c_str() );
::fclose( fp );
if ( !private_key )
// throw exception
}
And I am generating the signature as
void MessageSigner::signMessage( const std::vector< unsigned char >& msg, std::vector< unsigned char >& signature )
{
unsigned char *msgbuf = new unsigned char [msg.size()];
std::copy( msg.begin(), msg.end(), msgbuf );
unsigned char *sigbuf;
size_t sigbuf_length;
EVP_MD_CTX *ctx = EVP_MT_CTX_create();
if ( !ctx )
// throw exception
if ( EVP_DigestSignInit( ctx, NULL, EVP_sha256(), NULL, private_key ) != 1)
// throw exception
if ( EVP_DigestSignUpdate( ctx, msgbuf, msg.size() ) != 1 )
// throw exception
if ( EVP_DigestSignFinal( ctx, NULL, &sigbuf_length ) != 1 )
// throw exception
sigbuf = (unsigned char *)OPENSSL_malloc( sizeof *sigbuf * sigbuf_length );
if ( !sigbuf )
// throw exception
if ( EVP_DigestSignFinal( ctx, sigbuf, &sigbuf_length ) != 1 )
// throw exception
std::copy( sigbuf, sigbuf + sigbbuf_length, std::back_inserter( signature ) );
EVP_MD_CTX_destroy( ctx );
OPENSSL_free( sigbuf ); // yes, there's potential for a memory leak, but I'm just trying to get this bastard to work.
}
Again, the signature generated from this code does not jibe with the result of
openssl rsautl -sign -inkey keyfile.pem -keyform PEM -in msg.txt -out signature
I'm convinced the problem is with how I'm loading the private key, but all the examples I've seen so far indicate that this should work.
I've been staring at this and pulling my hair our for two days. Any suggestions, hints, guidance, rude remarks, etc., will be much appreciated.
Thanks.
EDIT
Some examples should illustrate what I'm up against.
Here's the SignedInfo generated for a given message:
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="alws soapenv"/>
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#TS-5b171864-232b-11e9-846f-00505695541c">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2006/12/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wsse alws soapenv"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestValue>eeLn6ak1glbbbWE48q7olsxO0CO/fL85bZ+8hzcjrvE=
</ds:DigestValue>
<ds:DigestMethod Algorithm="https//www.w3.org/2001/04/xmlenc#sha256"/>
</ds:Reference>
</ds:SignedInfo>
The digest value is calculated from the timestamp:
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="TS-5b171864-232b-11e9-846f-00505695541c">
<wsu:Created>2019-01-28T18:34:33Z</wsu:Created>
<wsu:Expires>2019-01-28T18:34:38Z</wsu:Expires>
</wsu:Timestamp>
When I calculate the digest for that timestamp from the command line as so:
$ openssl dgst -sha256 -binary timestamp > timestamp.dgst
I get the same digest value:
$ openssl base64 -in timestamp.dgst
eeLn6ak1glbbbWE48q7olsxO0CO/fL85bZ+8hzcjrvE=
So far so good. That hash is added to SignedInfo, then I take the hash of SignedInfo:
cQaWLGHi8D/c1kXPG9i49xzAupeBuypvMvuMQlzA/wo=
And use it to generate the signature. Now, here's where things go completely off the rails. When I generate the signature using the above code, I get:
HtQ4LkYq4Eao4bMOpV4SBpMxHi2a+0ilxDXS9jIQZWdCC8HCNlpvVU4rWMZG2Zd/
LplCWmUHIaB35FKv6uKjCjJPVDAJT2agyp7FnSKxaBI44Y/YsdvKyxJTAMiAlF8i
dd1MB8ljYsfayrzq5e76kt2cbHlYkT/RM3SvwJtjZiYsNpfcXD0Bi6JhRshHxQ8s
6/errruOe7jUqbKh7UOPJokadCX0OTSSwRgcs+sm7VjnS9MYILaGzFFT3Js9xI6d
TL4B6A/JGIkEqLO+GA1lrokAeIBr9OVUu7OEzaBb7DaiP9Gv1diu0j1sbZ4uT5Cf
CjYJPYU72Xx8F+MKdSJteg==
When I use the command-line tool, I get:
PvfCDqPl86/8USbFU0XR5r1Dhl5JbWd2va3L4W1IW1zw6xdes04F4lYjol6gMKio
jyr8DdmWBquroVlo4vW8kmhr6760qMcpK6mfsZ26ftu7XRC+Z4b9ge6ICOemsGlE
04Yoh9EpECP+ei5yS4E1sbntteiSoQcjotmVcIbPaEG5DIDcd4JKfoCWmsnuZESs
qctIJAQy4YY9HJsVGJ2JG7QashFcEQJabtInFgYeKuxla0ZSXBfOBkwHZT/cSv+k
n/NqPMCyEl4B2LiPBVa36GaTUd6fx0SXnIh0Fm+jw6b6j3EjU0QfMJ/JBAlL+oWZ
fXO/pS5L7W+OWk8Fh//iKA==
When I verify the signature generated on the command line, I get the original hash back:
$ openssl base64 -d -in signature.b64 > signature.reversed
$ openssl rsautl -verify -inkey cert.pem -certin -in signature.reversed > signature.reversed.dgst
$ openssl base64 -in signature.reversed.dgst
cQaWLGHi8D/c1kXPG9i49xzAupeBuypvMvuMQlzA/wo=
When I try to verify the signature generated by my code, I do not get the original hash back:
$ openssl base64 -d -in badsig.b64 > badsig
$ openssl rsautl -verify -inkey cert.pem -certin -in badsig > badsig.dgst
$ openssl base64 -in badsig.dgst
MDEwDQYJYIZIAWUDBAIBBQAEIHEGlixh4vA/3NZFzxvYuPccwLqXgbsqbzL7jEJc
wP8K
What's especially frustrating is that I've since added code to verify the signature after I create it, and that code passes. But it's obviously wrong - the remote service rejects our requests because the hashes don't match, and it doesn't match with what's generated by the openssl command line tool. I've verified that both the command line tool and the library I'm using are the same version (1.0.1).
I've added code to dump the private key to compare against what's in the keyfile, and it matches, so I'm reading the key in correctly.
There appear to be no less than 3 different ways to sign a message using libcrypto routines - EVP_Sign(), EVP_DigestSign(), EVP_PKEY_sign() - and I'm not sure which one is the correct one to use in my case. All three give me bad signatures (meaning, when decrypted against the public key, do not result in the original hash value).
So, again, if anyone can point to anything I'm doing that's obviously wrong, I would appreciate it.
MORE EDIT
Also attempting to verify using dgst as follows, again using the SignedInfo block above:
$ openssl dgst -sha256 -binary -sign fx-realtime.fundsxpress.com.pem -out signature signedinfo
$ openssl dgst -sha256 -binary -verify publickey.pem -signature signature signedinfo
Verified OK
Reversing the signature my code generates:
$ echo -n 'XfgP1A08UTwz3sUHIVvvV+fq1n3act6+lVBZ8ieDtgh28k1r1/M0tm9MntvK+Hm4
> Be+LjguX2gxhZ4PvVcoCBCugDIsrhxplDeB4bYeY2PEedQL6+IZFX+kFrz6o3RQa
> W7sXK7czogxWpdLAmKnhDJOk2BmKFihkRMTjo9D4z/qylZI9nnX29HNdg3uV2BYw
> zHh8GvYO8fy1ugqfFW80na+hLBAtBP6fwTTv10DS2L8n+ixQcnxlKW5pyBOXlR/r
> mZEqwU+A996G0573HkGFeFvXzArlRFg/7mkKoyUHyqyDzkf5eC+vTnpEy1CP75Yc
> lvd7ldSrwREisPnyxu47sg=='> computed_signature.b64
$ openssl base64 -e -in computed_signature.b64 > computed_signature
$ openssl dgst -sha256 -binary -verify publickey.pem -signature computed_signature signedinfo
Verification Failure
The code is fine and the signature generated is fine. What you are missing is that every time you generate a signature it will be different each time. If you run the signatures through the verify they will all work. I don’t known the reason why but I assume there is a security reason why this is done this way.
Its not clear from the details here how the signed data (input) is treated.
the openssll commands will read these as binary (not text) and there may be newline data that is changed if you use text reads (ie on windows 2 chars CR/LF -> transition to 1 CR) before you sign the input.
not signing the same input will create a diff digest (obviously).

How to use amazonka presignURL

I'm trying to get to grips with the amazonka library. I want to pre-sign a PUT request for S3. Looking at the example here, I put together the code below. It doesn't work when trying to send up a JPEG image from the client side. AWS complains that the signature is not correct.
My client-side code works with the nodejs aws sdk, so I can only guess that I am configuring things incorrectly here.
<Error>
<Code>
SignatureDoesNotMatch
</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>
<CanonicalRequest>
PUT
/my-bucket/test.jpg
X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-
Credential=[MY_AWS_ACCESS_KEY]%2F20171010%2Feu-west-
1%2Fs3%2Faws4_request&X-Amz-Date=20171010T084724Z&X-Amz-
Expires=100000&X-Amz-SignedHeaders=expect%3Bhost
expect:
host:s3-eu-west-1.amazonaws.com
expect;host
UNSIGNED-PAYLOAD
</CanonicalRequest>
...
Here's my code:
getS3UrlR :: Handler Value
getS3UrlR = do
y <- getYesod
let settings = appSettings y
env <- newEnv $ FromKeys (AccessKey $ encodeUtf8 $ appAwsAccessKey settings) (SecretKey $ encodeUtf8 $ appAwsSecretKey settings)
ts <- liftIO $ getCurrentTime
let b = BucketName $ appS3Bucket settings
k = ObjectKey "profile-pic-test"
url <- liftIO $ runResourceT . runAWST env . within Ireland $ presignURL ts 100000 (putObject b k (toBody ("" :: ByteString)))
return $ toJSON $ decodeUtf8 url
How can I go about debugging this?
Note - putObject requires a RqBody argument, but I don't have one to provide (as the client is going to supply the request body). So I just supplied an empty string. Could that be a problem?
Edit - I tried signing a GET request to see if that would work. It works fine! But PUT does not. This is the changed line that works fine:
url <- liftIO $ runResourceT . runAWST env . within Ireland $ presignURL ts 100000 (getObject b k)
I found an issue from Feb which led me to a workround. It seems that presignURL is adding an extraneous header to the signed URL. When I rewrite the request to remove the Expect header before signing, the resulting URL works with a simple PUT.
My workaround is included in my comment on the github issue.

Amazon SES 535 Authentication Credentials Invalid trying to rotate access key

We have an Amazon SES setup that works well and sends thousands of emails a day via SMTP. Trying to follow a best practice of "rotating" access keys we went to
https://console.aws.amazon.com/iam/home and creating a new access key for the exact same user which is used to send emails. The new key is supposedly active but when trying to email with the access keys, we keep getting
535 Authentication Credentials Invalid
Switching to the old access keys works well and emails are sent. Tried a couple of times to delete the new access keys and create others. Same machine, same software. We have proper copy+paste skills to ensure we're using the same ID/Password provided in the CSV coming from Amazon. Here the dialog from Amazon:
So what's going on? Is there a time limit till the new key becomes active? Is there some other hidden limitation somewhere?
You are confusing the SMTP credentials with access_key and secret. They are different.
access_key/secret --> Use in SDK and CLI
SMTP credentials --> Use to configure SES SMTP
You are creating a new access_key/secret and using it as SMTP credentials
Instead you create a new SMTP credentials and use it
Key rotation is different from SMTP credential rotation
No need to create a new user
It is likely you are using the SMTP credentials that does not change even if you generate another set of access_key/secret. In your case it looks like you are using the SMTP server and not the SDK. So generating a new set of access_key/secret has no effect on SMTP credentials.
If you want to create a new set of SMTP credentials, go to AWS SES dashboard and create SMTP credentials.
For more information: Obtaining Your Amazon SES SMTP Credentials
Yes, there's a hidden limitation in the way AWS approaches the SMTP password for SES. And they are using a very confusing way of handling these credentials.
The answer from helloV is on the right track, but it's not entirely correct. Both AWS and his answer tell us that Access_key/Secret_key and SES SMTP credentials are different things, but:
If you create fresh SES SMTP credentials, it creates a new IAM User with an Access Key/Secret Key pair
The Access Key Id is the same as the username for SMTP
If you delete or disable this key, you lose your SMTP access. So they are clearly very related.
The password for SMTP is derived from the Secret Key
It turns out that a new access_key/secret_key pair on an existing IAM user, can be used for SMTP, and therefore keys can be rotated without creating new users.
AWS converts the Secret Access Key to generate the SMTP password, as they explain in this documentation page:
The following pseudocode shows the algorithm that converts an AWS Secret Access Key to an Amazon SES SMTP password.
key = AWS Secret Access Key;
message = "SendRawEmail";
versionInBytes = 0x02;
signatureInBytes = HmacSha256(message, key);
signatureAndVer = Concatenate(versionInBytes, signatureInBytes);
smtpPassword = Base64(signatureAndVer);
So using the Secret Access key, the SMTP password can be generated
With bash and openssl installed, the following command will output the password for use in SMTP:
(echo -en "\x02"; echo -n 'SendRawEmail' \
| openssl dgst -sha256 -hmac $AWS_SECRET_ACCESS_KEY -binary) \
| openssl enc -base64
Just replace $AWS_SECRET_ACCESS_KEY with your key, or set the variable beforehand
Since, both Secret Key and SMTP Password are in different format, you need to convert Secret Key to SMTP Password using algorithm provided by AWS.
You can find it here:
https://aws.amazon.com/premiumsupport/knowledge-center/ses-rotate-smtp-access-keys/
Here is a working piece of code for transformning your secret key to smtp password using bash :
#!/usr/bin/env bash
# Convert AWS Secret Access Key to an Amazon SES SMTP password
# using the following pseudocode:
#
# date = "11111111";
# service = "ses";
# terminal = "aws4_request";
# message = "SendRawEmail";
# version = 0x04;
#
# kDate = HmacSha256(date, "AWS4" + key);
# kRegion = HmacSha256(region, kDate);
# kService = HmacSha256(service, kRegion);
# kTerminal = HmacSha256(terminal, kService);
# kMessage = HmacSha256(message, kTerminal);
# signatureAndVersion = Concatenate(version, kMessage);
# smtpPassword = Base64(signatureAndVersion);
#
# Usage:
# chmod u+x aws-ses-smtp-password.sh
# ./aws-ses-smtp-password.sh secret-key-here
# See: http://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html
#
if [ "$#" -ne 1 ]; then
echo "Usage: ./aws-ses-smtp-password.sh secret-key-here"
exit 1
fi
KEY="${1}"
DATE="11111111"
REGION="eu-west-1"
SERVICE="ses"
TERMINAL="aws4_request"
MESSAGE="SendRawEmail"
VERSION="4"
VERSION_IN_BYTES=$(printf \\$(printf '%03o' "${VERSION}"));
#SIGNATURE_IN_BYTES=$(echo -n "${MESSAGE}" | openssl dgst -sha256 -hmac "${KEY}" -binary);
SIGNATURE_IN_BYTES=$(echo -n "${DATE}" | openssl dgst -sha256 -mac HMAC -macopt "key:AWS4${KEY}" | sed 's/^.* //');
SIGNATURE_IN_BYTES=$(echo -n "${REGION}" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${SIGNATURE_IN_BYTES}" | sed 's/^.* //');
SIGNATURE_IN_BYTES=$(echo -n "${SERVICE}" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${SIGNATURE_IN_BYTES}" | sed 's/^.* //');
SIGNATURE_IN_BYTES=$(echo -n "${TERMINAL}" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${SIGNATURE_IN_BYTES}" | sed 's/^.* //');
SIGNATURE_IN_BYTES=$(echo -n "${MESSAGE}" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${SIGNATURE_IN_BYTES}" -binary | sed 's/^.* //');
SIGNATURE_AND_VERSION="${VERSION_IN_BYTES}${SIGNATURE_IN_BYTES}"
SMTP_PASSWORD=$(echo -n "${SIGNATURE_AND_VERSION}" | base64);
echo "${SMTP_PASSWORD}"