I am trying to encrypt/decrypt local files with AWS KMS DataKey but I don't know what to use to do it.
I have already generated the DataKey from AWS KMS (receiving the Plaintext Key and the Encrypted Key). Now it's supposed I have to use the Plaintext key to encrypt the file, store the Encrypted key with the Final Encrypted file and delete the Plaintext key.
How do I encrypt a file in Java 7 using that Plaintext Key? I mean, there are several ways to do it but which is the most effective and secure with AES_256 cipher I request to AWS KMS to get the keys?
//AWS KMS requesting data key
GenerateDataKeyRequest dataKeyRequest = new GenerateDataKeyRequest()
dataKeyRequest.setKeyId(keyId)
dataKeyRequest.setKeySpec("AES_256")
GenerateDataKeyResult dataKeyResult = awskmsClient.generateDataKey(dataKeyRequest)
ByteBuffer plaintextKey = dataKeyResult.getPlaintext()
ByteBuffer encryptedKey = dataKeyResult.getCiphertextBlob()
I cannot use AWS Encryption to do that (even it would be easier) because o Java 7 version
Related
As i am new to Android, i am working on hiding my API keys and found Android keystore the way to go. But when i see examples of how to use Android Keystore, one thing i am not understanding is how to supply the unencrypted original key for encryption? if i am storing in the code, wouldn't that beat the purpose of using Android Keystore?
from an article on storing secrets:
https://medium.com/#ericfu/securely-storing-secrets-in-an-android-application-501f030ae5a3
Generate a random key when the app runs the first time;
When you want to store a secret, retrieve the key from KeyStore, encrypt the data with it, and then store the encrypted data in
Preferences.
When you want to read a secret, read the encrypted data from Preferences, get the key from KeyStore and then use the key to decrypt
the data
In second point, it says encrypt the data with it. How to supply the data without exposing to the code/application?
I apologize if this has been answered.
Thanks
private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
private static final String AES_MODE = "AES/GCM/NoPadding";
private static final String KEY_ALIAS = "MyNiceKey";
Load the default AndroidKeyStore:
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
keyStore.load(null);
Generate AES key inside the KeyStore which in the latest verision of android, it is hardware-backed keystore; it means that it is very hard to extract the bytes of the key from it:
if (!keyStore.containsAlias(KEY_ALIAS)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_PROVIDER);
keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build());
keyGenerator.generateKey();
}
Anyway you should use .setRandomizedEncryptionRequired(true). There is no point to set up a faulty protocol. Otherwise, if you have to encrypt only few bytes(your API key) you could create an asymmetric public/private key and encrypt it with RSA so that you don't even need to provide the IV.
Haing said that, when you get the secret key from the KeyStore:
public static SecretKey getKeyStoreSecretKeyEntry(final String entryAlias)
throws GeneralSecurityException, IOException {
return ((KeyStore.SecretKeyEntry) getKeyStore().getEntry(entryAlias, null)).getSecretKey();
}
the returned SecretKey does not containt the Key Material (the real bytes of the key) but only its reference. So you can use it freely iside the Cipher to encrypt and decrypt what you want. In any case, you API key will be exposed enyway if you use it to make http request directly to you service. The best way to go in your case is to use a server like Google Firebase
P.s. there is very a simple library from google that will save you time and headache:
https://developer.android.com/jetpack/androidx/releases/security
https://developer.android.com/topic/security/data
Conclusion: The key you generate within the android Key Store is property of the user and it should be used to protect the user's private data. So it is not a good practice to encrypt an API Key, wich is the developer's private data, with the user key. Use a server to protect the API key.
I'm trying to test the most basic use cases around encryption/decryption with AWS S3 and AWS java SDK (trying both v1 and v2).
So this is what I'm doing :
I upload a small json file using aws console, and then I check that in Properties > crypt, "AWS-KMS" is selected and my key alias is selected. I assume this tells me the file is encrypted with my key, but I have no way to check this, since if I try to open the file using aws console, it's in clear text.
I try to download the file using various methods, and I expect to get an encrypted file when I use the most basic method.
So by using this client (sdk v2) :
#Bean
public S3Client s3Clientv2(AppProperties appProperties, CustomAwsCredentialsProvider customAwsCredentialsProvider) {
return S3Client.builder()
.httpClientBuilder(httpClientBuilder)
.credentialsProvider(customAwsCredentialsProvider)
.region(Region.EU_WEST_3)
.build();
}
and this download method :
public void downloadFile(String bucket, String key) {
s3Client.getObject(GetObjectRequest.builder().bucket(bucket).key(key).build(), ResponseTransformer.toFile(Paths.get("test_aws.json")));
}
I expected to get an encrypted file, but it was not.
Then I tried to use a client able to "encrypt/decrypt" by itself "Any objects you get from Amazon S3 using this client are automatically decrypted" source : https://docs.aws.amazon.com/en_pv/sdk-for-java/v1/developer-guide/examples-crypto-kms.html
AmazonS3Encryption s3Encryption = AmazonS3EncryptionClientBuilder
.standard()
.withRegion(Regions.US_WEST_2)
.withCryptoConfiguration(new CryptoConfiguration(CryptoMode.EncryptionOnly).withAwsKmsRegion(Region.getRegion(Regions.US_WEST_2)))
// Can either be Key ID or alias (prefixed with 'alias/')
.withEncryptionMaterials(new KMSEncryptionMaterialsProvider("alias/s3-kms-key"))
.build();
But using :
S3Object file = s3Encryption.getObject(new GetObjectRequest(bucket, key));
with this client call gets me a warning : "Unable to detect encryption information for object '%s' in bucket '%s'. Returning object without decryption."
So what am I doing wrong here ?
How can I check my file is really encrypted ?
What would be the right config to download it and decrypt it without a "file not encrypted" warning ?
Update : of course I've checked the object metadata, which do contain the KMS information and the KMS key id, but the encryption client is expecting some other informations about this :
/** Initialization vector (IV) header that is used in the symmetric and envelope encryption mechanisms */
public static final String CRYPTO_IV = "x-amz-iv";
and this :
/**
* Encrypted symmetric key header that is used in the Encryption Only (EO) envelope
* encryption mechanism.
*/
public static final String CRYPTO_KEY = "x-amz-key";
Ok, I think I get it : I got confused between server-side encryption and client-side encryption.
So AmazonS3Encryption client is only about client-side encryption. When you are using server-side encryption, any access to the file will decrypt it.
So the answer to my question would be : disable server-side encryption and use client-side encryption if you need fine-grained control about encryption, and to be able to download still encrypted content.
I have Android application which using WebRTC. All works perfect. But now, main problem, with encryption.
For making call and transfer data, WebRTC creates and uses a single KeyPair for every call. But I want to use custom KeyPair from AndroidKeyStore. For this problem I need to send own KeyPair to OpenSSL shared object to work.
The fix will be in NATIVE OpenSSL code, where WebRTC is getting OpenSSL context for encryption data using this function (opensslidnetity.cc):
bool OpenSSLIdentity::ConfigureIdentity
{
...
}
How transfer PK from AndroidKeyStore to WebRTC native code? Another case, how set custom PK for WebRTC encryption work?
AndroidKeyStore
In Java I can open the KeyStore (AndroidKeyStore) and get the public key - which ready to transfer (has bytes of key with method - getEncoded()). Also I can get private Key for encryption data, but I can't send this key in bytes, because getEncoded() return null. In this case, I thought, I can get PublicKey and PrivateKey and save them in bytes array. And after, call prepared methods in native code.
UPDATE: There is something similar located in google.source.chromium. Where they get key from Android KeyStore and creating OpenSSL context in native code. Native class for getting and using AndroidKeyStore for TLS - Link 1 and Link 2.
Android Keystore does not expose the key material of private or secret keys, by design (see https://developer.android.com/training/articles/keystore.html). You options are:
Present Android Keystore PrivateKey + Signature or Cipher as OpenSSL EVP_PKEY.
Don't use Android Keystore. Perhaps you don't need the additional protections it offered compared to storing private keys inside your process?
I try to design system that encrypts files(audio) and store them in disc in order that only my clients(applet) decrypt and play them. So I decided to use AES cipher for bulk encryption and store keys in database. My problem is transferring secret key securely.
In modern systems use SSL for transferring keys and data which are not stored cryptically. In SSL/TLS design, session key is generated two ways;
First way client creates key and encrypt it server's public key(certificate).
Second option is more secure and becomes more important after detecting heartbleed security bug. In this option key is created by client and server with (EC)DHE key agreement for every sessions.
When it comes to my case, there are two options as well.
First case;
Client(applet) can create ephemeral RSA key pair and sends public key to server.
Server encrypts secret key with client's public key and sends to client.
Client(applet) decrypt secret key with private key.
Client(applet) can decrypt audio files streamly and play.
Second case;
Client(applet) and server agrees on session key using (EC)DHE.
Server encrypts secret key symmetrically with session key and sends to client.
Client(applet) decrypt secret key with session key.
Client(applet) can decrypt audio files streamlly and play.
Which option is suits my scenario? What is pros and cons each case?
Thanks for answers.
Both solutions are vulnerable to an active attacker, in position of man-in-the-middle, because the key exchange is not authenticated. This issue is solved in SSL/TLS by the use of the X.509 PKI. It could also be solved by giving your client prior knowledge of the server's public key, for example.
That being said, between your two options, go with the (EC)DHE one, because generating a new DH key from pre-computed DH group (also known as DH parameters) is fast, whereas generating a new RSA key is very slow.
Authenticated encryption requires that we use some accepted standard for encrypting and authenticating a message. So we both encrypt the message and compute a MAC on the message to verify it has not been tampered with.
This question outlines a way to perform password based key strengthening and encryption:
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
But as far as I can tell, this does not compute any MAC on the ciphertext and so would be insecure. What is the accepted standard for performing authenticated encryption in Java?
I would recommend using GCM mode encryption. It is included in the latest JDK (1.7) by default. It uses a counter mode encryption (a stream cipher, no padding required) and adds an authentication tag. One big advantage is that it requires only a single key, whereas HMAC adds another key to the mix. Bouncy Castle has an implementation as well, which is moslty compatible with one provided by Oracle.
GCM mode encryption is also features in a TLS RFC, and in XML encrypt 1.1 (both not final). GCM mode provides all three security features: confidentiality, integrity and authenticity of the data send. The String would be "AES/GCM/NoPadding" instead of the CBC one you are now deploying. As said, make sure you have the latest JDK from Oracle, or have Bouncy Castle provider installed.
Also check out my answer here, which is mostly about String encoding, but I've succesfully tried GCM mode too - see the comment.
When transferring files from one server to another through secure ftp, I use private/public key pairs with the private key residing on the "from" server and the public key residing on the "to" server.
Using private/public key pairs is a secure standard when transferring files.
I believe it would also be a secure means in the context of a Java application.
Check out Generating and Verifying Signatures and Generate Public and Private Keys
for more details on using a private/public key pair setup for digital signatures in Java.