| « PreviousNext » | |
![]() ![]() ![]() | Did this page help you? Yes | No | Tell us about it... |
To encrypt and decrypt objects client-side for upload to Amazon S3, you can use the
AmazonS3EncryptionClient class in the AWS SDK for Java. This
class provides similar functionality as the non-encrypted
AmazonS3Client class so upgrading existing code to use the
encryption client is straightforward.
The AmazonS3EncryptionClient class requires that you initialize it with an
EncryptionMaterials instance which describes your encryption keys
(either asymmetric or symmetric) to use.
Note
If you get a cipher encryption error message when you use the encryption API for the first
time, then your version of the JDK may have a Java Cryptography Extension (JCE)
jurisdiction policy file that limits the maximum key length for encryption and
decryption transformations to 128 bits. The AWS SDK requires a maximum key length of
256 bits. To check your maximum key length, use the
getMaxAllowedKeyLength method of the
javax.crypto.Cipher class. To remove the key length restriction,
install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy
Files at the Java SE
download page.
Uploading and Retrieving a Client-Side Encrypted Object
|
1 |
Construct a new |
| 2 | Construct a new EncryptionMaterials object by providing asymmetric key
pair. |
| 3 | Use the AWSCredentials object and the EncryptionMaterials
object to create a new AmazonS3EncryptionClient
instance. |
| 4 | Upload the object using the putObject method and retrieve the object using
the getObject method. The client performs the necessary
encryption and decryption. |
The following Java code sample demonstrates the preceding tasks.
// Specify the asymmetric key pair to use.
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
keyGenerator.initialize(1024, new SecureRandom());
KeyPair myKeyPair = keyGenerator.generateKeyPair();
// Construct an instance of AmazonS3EncryptionClient.
AWSCredentials credentials = new BasicAWSCredentials(myAccessKeyId, mySecretKey);
EncryptionMaterials encryptionMaterials = new EncryptionMaterials(myKeyPair);
AmazonS3EncryptionClient encryptedS3client =
new AmazonS3EncryptionClient(credentials, encryptionMaterials);
// Upload the object.
encryptedS3client.putObject(new PutObjectRequest(bucketName, key, createSampleFile()));
// Retrieve the object.
S3Object downloadedObject = encryptedS3client.getObject(bucketName, key);
This section contains three examples that demonstrate using the AWS SDK to implement client-side encryption. The first example generates a 256-bit Advanced Encryption Standard (AES) secret key. The key is saved locally and then read from file. The second example demonstrates how to upload a directory to Amazon S3 by using a 256-bit AES key as a symmetric key in client-side encryption. (You can use the key from the first example.) The third example demonstrates how to create a key pair (a public key and a private key) and use it for client-side encrypted uploads to Amazon S3. The key pair is an example of using an asymmetric key pair as your private encryption key.
This example shows how to create a secret key as your private symmetric key for encrypted uploads to Amazon S3. The example reads and writes a key to a local file. In a typical use case, you would need to run this program only once to generate the key, and then you would save the key in a secure location.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class SymAES256KeyGenerate {
private static String keyDir = "***specify local directory ***";
private static String keyName = "symmetric.key";
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
//Generate symmetric 256 AES key.
KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES");
symKeyGenerator.init(256);
SecretKey symKey = symKeyGenerator.generateKey();
System.out.println("Symmetric key saved (base 64): " + new String(Base64.encodeBase64(symKey.getEncoded())));
//Save key.
saveSymmetricKey(keyDir, symKey);
//Load key.
SecretKey symKeyLoaded = loadSymmetricAESKey(keyDir, "AES");
//Compare with what we saved.
System.out.println("Symmetric key loaded (base 64): " + new String(Base64.encodeBase64(symKeyLoaded.getEncoded())));
}
public static void saveSymmetricKey(String path, SecretKey secretKey)
throws IOException {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
secretKey.getEncoded());
FileOutputStream keyfos = new FileOutputStream(path + "/" + keyName);
keyfos.write(x509EncodedKeySpec.getEncoded());
keyfos.close();
}
public static SecretKey loadSymmetricAESKey(String path, String algorithm)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException{
//Read private key from file.
FileInputStream keyfis = new FileInputStream(path + "/" + keyName);
byte[] encodedPrivateKey = new byte[keyfis.available()];
keyfis.read(encodedPrivateKey);
keyfis.close();
//Generate secret key.
return new SecretKeySpec(encodedPrivateKey, "AES");
}
}This example demonstrates how to upload a directory and all its files to Amazon S3 by using client-side encryption with a 256-bit AES symmetric key as your private encryption key. The example code uses a properties file (.properties) to specify the configuration information for the encryption and upload. To incorporate this example into your own source code, you will need to change the values in the properties file to reflect your situation.
The properties file takes five configuration parameters as shown below. For an
example of generating a key that you can use for as the
master_symmetric_key parameter in this properties file, see
Example: Creating a 256-bit AES
Secret.
# Base64 encoded AES 256 bit symmetric master key. master_symmetric_key=***specify your key*** # Endpoint. Use s3-external-1.amazonaws.com for buckets in us-east s3_endpoint=s3.amazonaws.com # Bucket to upload data. s3_bucket=***specify your bucket name*** # S3 prefix to add to uploaded data files. All files loaded will have this prefix. # Leave blank if no prefix is desired. s3_prefix= # Source files src_dir = ***specify local directory***
The code the uses the properties file and performs the client-side encryption is shown below.
import java.io.File;
import java.util.Properties;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3EncryptionClient;
import com.amazonaws.services.s3.model.EncryptionMaterials;
import com.amazonaws.services.s3.model.PutObjectRequest;
public class SymKeyEncryptAndUploadDirectoryToS3 {
private static final int MAX_RETRY_COUNT = 10;
private static AmazonS3EncryptionClient encryptedS3Client;
public static void main(String[] args) throws Exception {
AWSCredentials credentials = new PropertiesCredentials(
SymKeyEncryptAndUploadDirectoryToS3.class
.getResourceAsStream("AwsCredentials.properties"));
// Specify values in SymKeyEncryptAndUploadDirectoryToS3.properties file.
Properties config = new Properties();
config.load(SymKeyEncryptAndUploadDirectoryToS3.class
.getResourceAsStream("SymKeyEncryptAndUploadDirectoryToS3.properties"));
// Get property values.
String masterSymmetricKeyBase64 = getProperty(config,
"master_symmetric_key");
String bucketName = getProperty(config, "s3_bucket");
String s3Prefix = getProperty(config, "s3_prefix");
String s3Endpoint = getProperty(config, "s3_endpoint");
String sourceDir = getProperty(config, "src_dir");
// Get secret key in correct format and create encryption materials.
SecretKey mySymmetricKey = new SecretKeySpec(
Base64.decodeBase64(masterSymmetricKeyBase64.getBytes()), "AES");
EncryptionMaterials materials = new EncryptionMaterials(mySymmetricKey);
encryptedS3Client = new AmazonS3EncryptionClient(credentials, materials);
encryptedS3Client.setEndpoint(s3Endpoint);
// Upload all files.
uploadAllFilesToS3(encryptedS3Client, bucketName, s3Prefix, new File(
sourceDir));
}
private static void uploadAllFilesToS3(AmazonS3 s3, String bucketName,
String s3Prefix, final File folder) {
System.out.println("Reading files from directory " + folder);
for (final File fileEntry : folder.listFiles()) {
if (!fileEntry.isDirectory()) { // Skip sub directories.
int retryCount = 0;
boolean done = false;
while (!done) {
try {
uploadToS3(s3, bucketName, s3Prefix, fileEntry);
done = true;
} catch (Exception e) {
retryCount++;
if (retryCount > MAX_RETRY_COUNT) {
System.out
.println("Retry count exceeded max retry count "
+ MAX_RETRY_COUNT + ". Giving up");
throw new RuntimeException(e);
}
// Do retry after 10 seconds.
System.out.println("Failed to upload file " + fileEntry
+ ". Retrying...");
try {
Thread.sleep(10 * 1000);
} catch (Exception te) {
}
}
}// while
}// for
}
}
private static void uploadToS3(AmazonS3 s3, String bucketName,
String s3Prefix, File file) {
try {
System.out.println("Uploading a new object to S3 object '"
+ s3Prefix + "' from file " + file);
String key = s3Prefix + "/" + file.getName();
s3.putObject(new PutObjectRequest(bucketName, key, file));
} catch (AmazonServiceException ase) {
System.out
.println("Caught an AmazonServiceException, which means your request made it "
+ "to Amazon S3, but was rejected with an error response for some reason.");
System.out.println("Error Message: " + ase.getMessage());
System.out.println("HTTP Status Code: " + ase.getStatusCode());
System.out.println("AWS Error Code: " + ase.getErrorCode());
System.out.println("Error Type: " + ase.getErrorType());
System.out.println("Request ID: " + ase.getRequestId());
throw ase;
} catch (AmazonClientException ace) {
System.out
.println("Caught an AmazonClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with S3, "
+ "such as not being able to access the network.");
System.out.println("Error Message: " + ace.getMessage());
throw ace;
}
}
private static String getProperty(Properties config, String name) {
if (config.containsKey(name)) {
return config.getProperty(name);
}
throw new RuntimeException(name + " property not configured");
}
}
This example demonstrates how to create a key pair (a public key and a private key) and use it as your asymmetric key for a client-side encrypted upload to Amazon S3. This code generates a key pair, saves the key pair locally, loads the key pair, and uses the key pair to upload a test file. In a typical use case, you would need to generate your key pair only once and then save it in a secure location.
This example creates a key pair that uses the RSA algorithm and a key size of 1024 bits. You can change the code to use a different algorithm and key size.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Properties;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.s3.AmazonS3EncryptionClient;
import com.amazonaws.services.s3.model.EncryptionMaterials;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
public class KeyPairSampleEncryptAndUploadDataToS3 {
private static AmazonS3EncryptionClient encryptedS3Client;
private static String encryptionAlgorithm = "RSA";
private static String bucketName = "***provide bucket name***";
private static String downloadDir = "***provide local directory***";
private static String key = "keypairtest.txt";
public static void main(String[] args) throws Exception {
// Specify values in KeyPairSampleEncryptAndUploadDataToS3.properties file.
AWSCredentials credentials = new PropertiesCredentials(
KeyPairSampleEncryptAndUploadDataToS3.class
.getResourceAsStream("AwsCredentials.properties"));
// Generate a key pair. In a typical scenario, you would just generate this once.
KeyPairGenerator keyGenerator = KeyPairGenerator
.getInstance(encryptionAlgorithm);
keyGenerator.initialize(1024, new SecureRandom());
KeyPair myKeyPair = keyGenerator.generateKeyPair();
// Save key pair so we can use key later.
saveKeyPair(downloadDir, myKeyPair);
// Create encryption materials and instantiate client.
EncryptionMaterials encryptionMaterials = new EncryptionMaterials(
myKeyPair);
encryptedS3Client = new AmazonS3EncryptionClient(credentials,
encryptionMaterials);
// Upload a test file.
encryptedS3Client.putObject(new PutObjectRequest(bucketName, key,
createSampleFile()));
// Load the test key from file to show that key was saved correctly.
KeyPair myKeyPairRetrieved = loadKeyPair(downloadDir,
encryptionAlgorithm);
// Create new encryption materials using the retrieved key pair and a
// new client based on the new materials.
encryptionMaterials = new EncryptionMaterials(myKeyPairRetrieved);
encryptedS3Client = new AmazonS3EncryptionClient(credentials,
encryptionMaterials);
// Download the object.
// When you use the getObject method with the encrypted client,
// the data retrieved from Amazon S3 is automatically decrypted on the fly.
S3Object downloadedObject = encryptedS3Client
.getObject(bucketName, key);
SaveS3ObjectContent(downloadedObject, downloadDir, key);
}
private static File createSampleFile() throws IOException {
File file = File.createTempFile("encryptiontest", ".txt");
file.deleteOnExit();
Writer writer = new OutputStreamWriter(new FileOutputStream(file));
writer.write(new java.util.Date().toString() + "\n");
writer.write("abcdefghijklmnopqrstuvwxyz\n");
writer.write("01234567890112345678901234\n");
writer.write("!@#$%^&*()-=[]{};':',.<>/?\n");
writer.write("01234567890112345678901234\n");
writer.write("abcdefghijklmnopqrstuvwxyz\n");
writer.close();
return file;
}
public static void saveKeyPair(String path, KeyPair keyPair)
throws IOException {
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// Save public key to file.
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
publicKey.getEncoded());
FileOutputStream keyfos = new FileOutputStream(path + "/public.key");
keyfos.write(x509EncodedKeySpec.getEncoded());
keyfos.close();
// Save private key to file.
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
privateKey.getEncoded());
keyfos = new FileOutputStream(path + "/private.key");
keyfos.write(pkcs8EncodedKeySpec.getEncoded());
keyfos.close();
}
public static KeyPair loadKeyPair(String path, String algorithm)
throws IOException, NoSuchAlgorithmException,
InvalidKeySpecException {
// Read public key from file.
FileInputStream keyfis = new FileInputStream(path + "/public.key");
byte[] encodedPublicKey = new byte[keyfis.available()];
keyfis.read(encodedPublicKey);
keyfis.close();
// Read private key from file.
keyfis = new FileInputStream(path + "/private.key");
byte[] encodedPrivateKey = new byte[keyfis.available()];
keyfis.read(encodedPrivateKey);
keyfis.close();
// Generate KeyPair from public and private keys.
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(
encodedPublicKey);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
encodedPrivateKey);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
return new KeyPair(publicKey, privateKey);
}
public static void SaveS3ObjectContent(S3Object obj, String path,
String filename) throws IOException {
InputStream stream = obj.getObjectContent();
FileOutputStream fos = new FileOutputStream(path + "/" + filename);
byte[] buffer = new byte[1024];
int len;
while ((len = stream.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.close();
}
}