Encrypt value using AWS KMS - Can I have encrypted value in UTF8? - amazon-web-services

I can successfully encrypt the value using the following code:
final static Charset ENCODING = StandardCharsets.ISO_8859_1;
var awsCreds = AwsBasicCredentials.create(KEY, SECRET_KEY);
kmsClient = KmsClient.builder().credentialsProvider(StaticCredentialsProvider.create(awsCreds)).region(Region.US_EAST_1).build();
var sdkBytesString = SdkBytes.fromString(stringToEncrypt, ENCODING);
var encryptRequest = EncryptRequest.builder().keyId(KEY_ARN).plaintext(sdkBytesString).build();
var encryptResponse = this.kmsClient.encrypt(encryptRequest);
var result = encryptResponse.ciphertextBlob().asString(ENCODING);
In the result I can see encrypted value.
BUT The problem is that I need this value in UTF8 not ISO_8859_1. When trying to get ciphertextBlob in UTF8 - getting conversion error:
Blockquote
java.io.UncheckedIOException: Cannot encode string.
I need to save the string in UTF-8 DB and to send this encrypted string to another service that accepts UTF-8 strings\
Could you please advise how to get UTF-8 string after encryption?

Actually Base64 encrypting solves the problem:
https://github.com/amazon-archives/realworld-serverless-application/blob/master/backend/src/main/java/software/amazon/serverless/apprepo/api/impl/pagination/EncryptedTokenSerializer.java#L51

Related

How to get aws kms encrypt response as base64 string in sdk v3. Getting Uint8Array as response

I am using #aws-sdk/client-kms to encrypt the data. I was getting the base64 string as a response. Now I am getting Uint8Array.
const encryptedBlob = await kms.encrypt({
KeyId: kmsKey,
Plaintext: Buffer.from(JSON.stringify('data to encrypt')),
});
The encrypted plaintext. When you use the HTTP API or the AWS CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded. Mentioned in AWS docs
Is there any way to get base64 as response in nodeJs.
As mentioned in AWS SDK v3 docs Docs - Only HTTP API and CLI will get the base64 data. Other mediums will get Uint8Array as response.
So, we need some extra data conversion to achieve encryption and decryption using SDK.
const { KMSClient, EncryptCommand, DecryptCommand } = require('#aws-sdk/client-kms');
const client = new KMSClient({ region: AWS_REGION });
// Encrypt
// Convert Uint8Array data to base64
const input = {
KeyId: kmsKey,
Plaintext: Buffer.from(JSON.stringify(credentials)),
};
const command = new EncryptCommand(input);
const encryptedBlob = await client.send(command);
const buff = Buffer.from(encryptedBlob.CiphertextBlob);
const encryptedBase64data = buff.toString('base64');
// Decrypt
// Convert Base64 data to Uint8Array
// Uint8Array(response) convert to string.
const command = new DecryptCommand({
CiphertextBlob: Uint8Array.from(atob(item.credentials), (v) => v.charCodeAt(0)),
});
const decryptedBinaryData = await client.send(command);
const decryptedData = String.fromCharCode.apply(null, new Uint16Array(decryptedBinaryData.Plaintext));

Unable to get readable encoded string

I am trying to use google-kms nodejs library.
What I was expecting is that the end result will be encrypted text but what I get is a buffer if I don't decide base64 or if I do then I get something like $�k+��l�k��:
Does someone know what's wrong r is my expectation wrong about the encoded text.
exports.encryptKMS = async (req, res) => {
let message = req.query.message || req.body.message || 'Hello World!';
const projectId = 'xxx';
const kms = require('#google-cloud/kms');
const client = new kms.KeyManagementServiceClient();
const locationId = 'global';
const keyRingId = 'Test-Ring-01';
const cryptoKeyId = 'Test-Crypto-01';
const cryptoKeyPath = client.cryptoKeyPath(
projectId,
locationId,
keyRingId,
cryptoKeyId
);
const [result] = await client.encrypt({name: cryptoKeyPath, plaintext: message});
console.log(result);
const cryptoText = Buffer.from(result.ciphertext, 'base64').toString('utf-8');
console.log(cryptoText);
res.status(200).send(cryptoText);
}
This looks correct to me, the encrypted object will be a base64 encoded binary string which you won't be able to see any structure in. If you take that result and feed it back into Decrypt you should recover the original message.
If you wanted a readable string, please provide more info -- there's such a thing as "Format-Preserving Encryption" which attempts to encrypt data in such a fashion that it will continue to be in a compatible format to the input, for when systems aren't ready to handle encrypted data. Cloud KMS doesn't support format-preserving encryption, but I may have some suggestions depending on what you need.
Thanks for using GCP and Cloud KMS!

What encoding does blob.download_as_string() return?

I am downloading a file from Google Storage as a byte string, b64 encoding it, and using that as input into the Google Vision API.
storage_client = storage.Client(project=[PROJECT])
bucket = storage_client.get_bucket([BUCKET])
blob = bucket.blob([KEY])
content = blob.download_as_string()
b64content = base64.b64encode(content)
client = vision.ImageAnnotatorClient()
image = vision.types.Image(content=b64content)
I am getting a bad image error using the b64content. However, if I use the non base64 content, my call to the Vision API succeeds:
image = vision.types.Image(content=content)
Does blob.download_as_string() return a byte string that is already base64 encoded?
Short answer: no, it is not base64 encoded. Then why does it work with the non-encoded string?
Using the Python Client as you do, you don't need to encode the string, as seen here. You need to encode it if you post a Vision API request in JSON, like this one. This is why you get it working already without base64.b64encode().

java.security.InvalidKeyException: invalid key format

String distributionDomain = "d21geuebylb7j1.cloudfront.net";
String privateKeyFilePath = "/Users/Desktop/rsa-private-key.der";
String s3ObjectKey = "small.mp4";
String policyResourcePath = "http://" + distributionDomain + "/" + s3ObjectKey;
System.out.println(privateKeyFilePath);
byte[] derPrivateKey = null;
I am trying to make signed URL for my cloudfront distribution but I am getting invalid key error. I am getting issue with my rsa-private-key.der file. I have made this file from pem file as mentioned in Cloudfront documentation.
Below is my error logs:
Exception in thread "main" org.jets3t.service.CloudFrontServiceException: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at org.jets3t.service.CloudFrontService.signUrlCanned(CloudFrontService.java:2148)
at test.SignedURL.main(SignedURL.java:74)
Caused by: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:216)
at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:390)
at org.jets3t.service.security.EncryptionUtil.signWithRsaSha1(EncryptionUtil.java:526)
at org.jets3t.service.CloudFrontService.signUrlCanned(CloudFrontService.java:2133)
... 1 more
Caused by: java.security.InvalidKeyException: invalid key format
at java.base/sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:330)
at java.base/sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:356)
at java.base/sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(RSAPrivateCrtKeyImpl.java:91)
at java.base/sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(RSAPrivateCrtKeyImpl.java:75)
at java.base/sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:315)
at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:212)
... 4 more
I had same issue this solved my issue.
You can try this:
public enum CloudFrontUrlSigner
extends Enum<CloudFrontUrlSigner>
Utility class for generating pre-signed URLs for serving private CloudFront content. All dates must be in UTC. Use Calendar to set the timezone specifically before converting to a Date object, or else use DateUtils to turn a UTC date String into a Date object.
Protocol protocol = Protocol.http;
String distributionDomain = "d1b2c3a4g5h6.cloudfront.net";
File privateKeyFile = new File("/path/to/cfcurlCloud/rsa-private-key.pem");
String s3ObjectKey = "a/b/images.jpeg";
String keyPairId = "APKAJCEOKRHC3XIVU5NA";
Date dateLessThan = DateUtils.parseISO8601Date("2012-11-14T22:20:00.000Z");
Date dateGreaterThan = DateUtils.parseISO8601Date("2011-11-14T22:20:00.000Z");
String ipRange = "192.168.0.1/24";
String url1 = CloudFrontUrlSigner.getSignedURLWithCannedPolicy(
protocol, distributionDomain, privateKeyFile,
s3ObjectKey, keyPairId, dateLessThan);
String url2 = CloudFrontUrlSigner.getSignedURLWithCustomPolicy(
protocol, distributionDomain, privateKeyFile,
s3ObjectKey, keyPairId, dateLessThan,
dateGreaterThan, ipRange);
here is the link of AWS Documentation: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/cloudfront/CloudFrontUrlSigner.html

Binary Output from Google Script HMAC encription

I am current working with Google Apps script and am attempting to write & sign an HTTP request to AWS CloudWatch.
On the Amazon API documentation here regarding how to create a signing key, they use pseudo to explain that the HMAC algorithm is to return binary format.
HMAC(key, data) represents an HMAC-SHA256 function
that returns output in binary format.
Google apps script offers a method to do such a hash,
Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
data,
key);
but the return type is always a byte array.
Byte[]
How do I convert the Byte[] to the binary data AWS wants? Or is there a vanilla javascript function I can use in Google Apps Script to compute the hash?
Thanks
I am quite sure it is a bug that Utilities.computeHmacSignature take key as an ASCII. But there was no way to parse byte[] to ASCII correctly in GAS
And the library writer is too stupid too just provide function which take key as byte[]
So I use this instead : http://caligatio.github.com/jsSHA/
Just copy SHA.js and SHA-256.js then it work fine
PS. it waste my time for whole 2 days so I'm very annoying
The conversion from byte array to the binary data required should be simple:
kDate = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
'20130618', 'AWS4' + kSecret);
kDate = Utilities.newBlob(kDate).getDataAsString();
kRegion = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
'eu-west-1', kDate);
BUT you have to look onto this open issue in the bugtracker - there could be some issues in conversion.
maybe you could try to make a String.fromCharCode() loop and avoid negative numers:
kDateB = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
'20130618', 'AWS4' + kSecret);
kDate = '';
for (var i=0; i<kDateB.length; i++)
kDate += String.fromCharCode(kDateB[i]<0?256+kDateB[i]:0+kDateB[i]);
kRegion = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
'eu-west-1', kDate);