Use server-side encryption with customer keys (SSE-C)
How to implement SSE-C with CoreWeave AI Object Storage
This guide demonstrates how to implement server-side encryption with customer keys (SSE-C) with CoreWeave AI Object Storage. SSE-C allows you to provide your own encryption keys for objects stored in Object Storage, giving you complete control over data encryption.
Prerequisites
Before implementing SSE-C, ensure you have:
- Access to CoreWeave AI Object Storage with appropriate permissions
- An S3-compatible client or library that supports SSE-C
- A method to generate and store encryption keys securely
- Understanding of basic S3 operations (upload, download, copy)
If you lose your encryption key, you cannot recover your encrypted data. CoreWeave does not store your encryption keys, and cannot decrypt your data without them. See the Key management section for best practices on storing and managing your encryption keys securely.
Understanding key verification
CoreWeave AI Object Storage uses your provided key to encrypt or decrypt your data as required, but does not permanently store that key itself. Instead, CoreWeave stores only a base64-encoded MD5 digest of your encryption key.
This hash is stored solely for verification, meaning, when you later access the object, you must supply the same key and hash you used originally. CoreWeave checks the hash of the supplied key against the stored hash to verify that the correct key is provided before attempting decryption.
The hash of the key serves as a checksum: it can verify that the key is correct, but it cannot be used to reconstruct the actual key. By storing only the hash and not the key itself, CoreWeave ensures that only someone who possesses the original encryption key can access the corresponding encrypted data. If you lose your key, neither you nor CoreWeave can recover your data. The hash makes it computationally infeasible to recover the original key due to the one-way nature of cryptographic hashes.
Basic operations
In these examples, $BASE64_KEY
represents your base64-encoded encryption key. Replace this with your actual generated key in all examples.
Generate encryption keys
SSE-C requires 256-bit (32-byte) encryption keys. Generate these keys using cryptographically secure methods and encode them in base64 format:
- OpenSSL
- Python
- AWS CLI
- curl
# Generate a random 256-bit key and encode in base64openssl rand -base64 32
import secretsimport base64# Generate a random 256-bit key and encode in base64key_bytes = secrets.token_bytes(32)key = base64.b64encode(key_bytes).decode('utf-8')print(key)
# Generate a random 256-bit key using AWS CLI and encode in base64aws secretsmanager get-random-password --password-length 32 --exclude-characters --require-each-included-type | base64# Alternative: Generate key and save to variableBASE64_KEY=$(aws secretsmanager get-random-password --password-length 32 --exclude-characters --require-each-included-type | base64)echo $BASE64_KEY
# Generate a random 256-bit key using /dev/urandom and encode in base64head -c 32 /dev/urandom | base64# Alternative: Generate key and save to variableBASE64_KEY=$(head -c 32 /dev/urandom | base64)echo $BASE64_KEY
Upload objects with SSE-C
When uploading objects with SSE-C, include the encryption key in your request headers:
- AWS CLI
- Python (boto3)
- curl
# Upload an object with SSE-Caws s3 cp myfile.txt s3://my-bucket/ --sse-customer-algorithm AES256 \--sse-customer-key $BASE64_KEY \--sse-customer-key-md5 $(echo -n "$BASE64_KEY" | base64 --decode | openssl dgst -md5 -binary | base64)
import boto3import hashlibimport base64# Configure your S3 clients3_client = boto3.client('s3',endpoint_url='https://object-storage.coreweave.com',aws_access_key_id='your-access-key',aws_secret_access_key='your-secret-key')# Your encryption key (base64-encoded)encryption_key = '$BASE64_KEY'# Calculate MD5 hash of the keykey_md5 = hashlib.md5(base64.b64decode(encryption_key)).digest()key_md5_b64 = base64.b64encode(key_md5).decode('utf-8')# Upload with SSE-Cs3_client.upload_file('myfile.txt','my-bucket','myfile.txt',ExtraArgs={'SSECustomerAlgorithm': 'AES256','SSECustomerKey': encryption_key,'SSECustomerKeyMD5': key_md5_b64})
# Upload with SSE-C using curlcurl -X PUT \-H "x-amz-server-side-encryption-customer-algorithm: AES256" \-H "x-amz-server-side-encryption-customer-key: $BASE64_KEY" \-H "x-amz-server-side-encryption-customer-key-md5: $(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)" \-T myfile.txt \"https://object-storage.coreweave.com/my-bucket/myfile.txt"
Download objects with SSE-C
When downloading objects that were encrypted with SSE-C, provide the same encryption key:
- AWS CLI
- Python (boto3)
- curl
# Download an object with SSE-Caws s3 cp s3://my-bucket/myfile.txt ./downloaded-file.txt \--sse-customer-algorithm AES256 \--sse-customer-key $BASE64_KEY \--sse-customer-key-md5 $(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)
# Download with SSE-Cs3_client.download_file('my-bucket','myfile.txt','downloaded-file.txt',ExtraArgs={'SSECustomerAlgorithm': 'AES256','SSECustomerKey': encryption_key,'SSECustomerKeyMD5': key_md5_b64})
# Download with SSE-C using curlcurl -X GET \-H "x-amz-server-side-encryption-customer-algorithm: AES256" \-H "x-amz-server-side-encryption-customer-key: $BASE64_KEY" \-H "x-amz-server-side-encryption-customer-key-md5: $(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)" \"https://object-storage.coreweave.com/my-bucket/myfile.txt" \-o downloaded-file.txt
Copy objects with SSE-C
When copying objects that use SSE-C, specify the encryption parameters for both source and destination:
- AWS CLI
- Python (boto3)
- curl
# Copy an object with SSE-Caws s3 cp s3://my-bucket/source-file.txt s3://my-bucket/dest-file.txt \--sse-customer-algorithm AES256 \--sse-customer-key $BASE64_KEY \--sse-customer-key-md5 $(echo -n "$BASE64_KEY" | base64 --decode | openssl dgst -md5 -binary | base64) \--copy-source-sse-customer-algorithm AES256 \--copy-source-sse-customer-key $BASE64_KEY \--copy-source-sse-customer-key-md5 $(echo -n "$BASE64_KEY" | base64 --decode | openssl dgst -md5 -binary | base64)
# Copy with SSE-Cs3_client.copy_object(Bucket='my-bucket',Key='dest-file.txt',CopySource={'Bucket': 'my-bucket','Key': 'source-file.txt','SSECustomerAlgorithm': 'AES256','SSECustomerKey': encryption_key,'SSECustomerKeyMD5': key_md5_b64},SSECustomerAlgorithm='AES256',SSECustomerKey=encryption_key,SSECustomerKeyMD5=key_md5_b64)
# Copy with SSE-C using curlcurl -X PUT \-H "x-amz-copy-source: /my-bucket/source-file.txt" \-H "x-amz-copy-source-server-side-encryption-customer-algorithm: AES256" \-H "x-amz-copy-source-server-side-encryption-customer-key: $BASE64_KEY" \-H "x-amz-copy-source-server-side-encryption-customer-key-md5: $(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)" \-H "x-amz-server-side-encryption-customer-algorithm: AES256" \-H "x-amz-server-side-encryption-customer-key: $BASE64_KEY" \-H "x-amz-server-side-encryption-customer-key-md5: $(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)" \"https://object-storage.coreweave.com/my-bucket/dest-file.txt"
Verify encryption
Verify that your objects are encrypted by checking the response headers or object metadata:
Check upload response
- Python (boto3)
- AWS CLI
- curl
# Upload and check responseresponse = s3_client.upload_file('myfile.txt','my-bucket','myfile.txt',ExtraArgs={'SSECustomerAlgorithm': 'AES256','SSECustomerKey': encryption_key,'SSECustomerKeyMD5': key_md5_b64})# The response will include encryption headersprint(f"Encryption algorithm: {response['ResponseMetadata']['HTTPHeaders']['x-amz-server-side-encryption-customer-algorithm']}")
# Upload and check response headersaws s3 cp myfile.txt s3://my-bucket/ --sse-customer-algorithm AES256 \--sse-customer-key $BASE64_KEY \--sse-customer-key-md5 $(echo -n "$BASE64_KEY" | base64 --decode | openssl dgst -md5 -binary | base64) \--debug# Look for encryption headers in the debug output# x-amz-server-side-encryption-customer-algorithm: AES256# x-amz-server-side-encryption-customer-key-md5: [hash]
# Upload and check response headerscurl -X PUT \-H "x-amz-server-side-encryption-customer-algorithm: AES256" \-H "x-amz-server-side-encryption-customer-key: $BASE64_KEY" \-H "x-amz-server-side-encryption-customer-key-md5: $(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)" \-T myfile.txt \-D response_headers.txt \"https://object-storage.coreweave.com/my-bucket/myfile.txt"# Check the response headerscat response_headers.txt | grep -i encryption
List objects with encryption info
- Python (boto3)
- AWS CLI
- curl
# List objects and check encryptionresponse = s3_client.list_objects_v2(Bucket='my-bucket',Prefix='myfile.txt')for obj in response['Contents']:print(f"Object: {obj['Key']}")# Check if object uses SSE-Cif 'SSECustomerAlgorithm' in obj:print(f"Encryption: {obj['SSECustomerAlgorithm']}")
# List objects and check encryption infoaws s3api list-objects-v2 \--bucket my-bucket \--prefix myfile.txt \--query 'Contents[].{Key: Key, Size: Size, LastModified: LastModified}'# For detailed object info including encryption headersaws s3api head-object \--bucket my-bucket \--key myfile.txt \--query 'ServerSideEncryptionCustomerAlgorithm'
# List objects using HEAD request to check encryptioncurl -I \"https://object-storage.coreweave.com/my-bucket/myfile.txt" \| grep -i encryption# For a more detailed list with encryption infocurl -X GET \"https://object-storage.coreweave.com/my-bucket/?list-type=2&prefix=myfile.txt" \| grep -E "(Key|ServerSideEncryption)"
Error handling
When using SSE-C, you may encounter specific errors:
Error | Meaning |
---|---|
InvalidArgument | The encryption key is not 256 bits (32 bytes) |
InvalidRequest | The encryption key MD5 hash doesn't match |
AccessDenied | The encryption key provided doesn't match the one used for upload |
NoSuchKey | The object doesn't exist or the encryption key is incorrect |
It is strongly recommended to implement proper error handling in your applications. For example:
try:s3_client.download_file('my-bucket','myfile.txt','downloaded-file.txt',ExtraArgs={'SSECustomerAlgorithm': 'AES256','SSECustomerKey': encryption_key,'SSECustomerKeyMD5': key_md5_b64})except s3_client.exceptions.ClientError as e:error_code = e.response['Error']['Code']if error_code == 'InvalidArgument':print("Invalid encryption key format")elif error_code == 'AccessDenied':print("Incorrect encryption key")else:print(f"Error: {error_code}")
Key management
Store keys securely
Never store encryption keys in plain text or in your application code. Use secure key management solutions:
- Environment variables: Store keys in environment variables (not in code)
- Secret management systems: Use tools like HashiCorp Vault, AWS Secrets Manager, or Kubernetes Secrets
- Hardware Security Modules (HSMs): For high-security requirements, use HSMs to generate and store keys
Centralize key management
Create a centralized key management system in your application:
import hashlibimport base64class SSEKeyManager:def __init__(self, key_id):self.key_id = key_idself.key = self.load_key(key_id)def get_encryption_headers(self):"""Generate SSE-C headers for S3 requests"""key_md5 = hashlib.md5(base64.b64decode(self.key)).digest()key_md5_b64 = base64.b64encode(key_md5).decode('utf-8')return {'SSECustomerAlgorithm': 'AES256','SSECustomerKey': self.key,'SSECustomerKeyMD5': key_md5_b64}def load_key(self, key_id):"""Load key from secure storage"""# Implement secure key loading logicpass
Validate encryption keys
Always validate encryption keys before use:
def validate_encryption_key(key):"""Validate that the encryption key meets requirements"""if not isinstance(key, str):raise ValueError("Encryption key must be a string")try:# Decode base64 to check if it's validkey_bytes = base64.b64decode(key)if len(key_bytes) != 32: # 32 bytes = 256 bitsraise ValueError("Encryption key must be 32 bytes (256 bits)")except Exception:raise ValueError("Encryption key must be valid base64-encoded 32-byte key")return True
Advanced topics
SSE-C with presigned URLs
When using SSE-C with presigned URLs, you must include the encryption parameters when generating the URL and when accessing it. The encryption key is required both during URL generation and when the URL is used.
Generate presigned URLs with SSE-C
- Python (boto3)
- AWS CLI
- curl
import boto3import hashlibimport base64# Configure your S3 clients3_client = boto3.client('s3',endpoint_url='https://object-storage.coreweave.com',aws_access_key_id='your-access-key',aws_secret_access_key='your-secret-key')# Your encryption key (base64-encoded)encryption_key = '$BASE64_KEY'# Calculate MD5 hash of the keykey_md5 = hashlib.md5(base64.b64decode(encryption_key)).digest()key_md5_b64 = base64.b64encode(key_md5).decode('utf-8')# Generate presigned URL for download with SSE-Cpresigned_url = s3_client.generate_presigned_url('get_object',Params={'Bucket': 'my-bucket','Key': 'myfile.txt','SSECustomerAlgorithm': 'AES256','SSECustomerKey': encryption_key,'SSECustomerKeyMD5': key_md5_b64},ExpiresIn=3600 # URL expires in 1 hour)print(f"Presigned URL: {presigned_url}")
# Generate presigned URL with SSE-Caws s3 presign s3://my-bucket/myfile.txt \--expires-in 3600 \--sse-customer-algorithm AES256 \--sse-customer-key $BASE64_KEY \--sse-customer-key-md5 $(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)
# Generate presigned URL with SSE-C using curl# Note: curl doesn't directly generate presigned URLs, but you can use the URL# generated by the AWS CLI or Python with curl for access# First generate the URL using AWS CLI or Python, then use it with curl:curl -H "x-amz-server-side-encryption-customer-algorithm: AES256" \-H "x-amz-server-side-encryption-customer-key: $BASE64_KEY" \-H "x-amz-server-side-encryption-customer-key-md5: $(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)" \"https://object-storage.coreweave.com/my-bucket/myfile.txt?X-Amz-Algorithm=...&X-Amz-Credential=...&X-Amz-Date=...&X-Amz-Expires=...&X-Amz-SignedHeaders=...&X-Amz-Signature=..."
Use presigned URLs with SSE-C
When accessing a presigned URL that was generated with SSE-C, you must include the same encryption parameters:
# Download using presigned URL with SSE-Ccurl -H "x-amz-server-side-encryption-customer-algorithm: AES256" \-H "x-amz-server-side-encryption-customer-key: $BASE64_KEY" \-H "x-amz-server-side-encryption-customer-key-md5: $(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)" \"https://object-storage.coreweave.com/my-bucket/myfile.txt?X-Amz-Algorithm=...&X-Amz-Credential=...&X-Amz-Date=...&X-Amz-Expires=...&X-Amz-SignedHeaders=...&X-Amz-Signature=..."
SSE-C with bucket policies
Bucket policies can be configured to enforce SSE-C usage for specific operations. This ensures that objects are always encrypted with customer-provided keys.
Enforce SSE-C for uploads
Create a bucket policy that requires SSE-C for all upload operations:
- AWS CLI
- Python (boto3)
- curl
# Save the policy to a filecat > ssec-policy.json << 'EOF'{"Version": "2012-10-17","Statement": [{"Sid": "EnforceSSECForUploads","Effect": "Deny","Principal": "*","Action": ["s3:PutObject"],"Resource": "arn:aws:s3:::my-bucket/*","Condition": {"StringNotEquals": {"s3:x-amz-server-side-encryption-customer-algorithm": "AES256"}}},{"Sid": "EnforceSSECKeyForUploads","Effect": "Deny","Principal": "*","Action": ["s3:PutObject"],"Resource": "arn:aws:s3:::my-bucket/*","Condition": {"Null": {"s3:x-amz-server-side-encryption-customer-key": "true"}}}]}EOF# Apply the policyaws s3api put-bucket-policy --bucket my-bucket --policy file://ssec-policy.json
import boto3import json# Configure your S3 clients3_client = boto3.client('s3',endpoint_url='https://object-storage.coreweave.com',aws_access_key_id='your-access-key',aws_secret_access_key='your-secret-key')# Define the bucket policybucket_policy = {"Version": "2012-10-17","Statement": [{"Sid": "EnforceSSECForUploads","Effect": "Deny","Principal": "*","Action": ["s3:PutObject"],"Resource": "arn:aws:s3:::my-bucket/*","Condition": {"StringNotEquals": {"s3:x-amz-server-side-encryption-customer-algorithm": "AES256"}}},{"Sid": "EnforceSSECKeyForUploads","Effect": "Deny","Principal": "*","Action": ["s3:PutObject"],"Resource": "arn:aws:s3:::my-bucket/*","Condition": {"Null": {"s3:x-amz-server-side-encryption-customer-key": "true"}}}]}# Apply the bucket policys3_client.put_bucket_policy(Bucket='my-bucket',Policy=json.dumps(bucket_policy))
# Create the policy JSONcat > ssec-policy.json << 'EOF'{"Version": "2012-10-17","Statement": [{"Sid": "EnforceSSECForUploads","Effect": "Deny","Principal": "*","Action": ["s3:PutObject"],"Resource": "arn:aws:s3:::my-bucket/*","Condition": {"StringNotEquals": {"s3:x-amz-server-side-encryption-customer-algorithm": "AES256"}}},{"Sid": "EnforceSSECKeyForUploads","Effect": "Deny","Principal": "*","Action": ["s3:PutObject"],"Resource": "arn:aws:s3:::my-bucket/*","Condition": {"Null": {"s3:x-amz-server-side-encryption-customer-key": "true"}}}]}EOF# Apply the bucket policy using curlcurl -X PUT \-H "Content-Type: application/json" \-d @ssec-policy.json \"https://object-storage.coreweave.com/my-bucket?policy"
Enforce SSE-C for specific prefixes
You can also enforce SSE-C only for specific object prefixes. For example, the following policy denies uploads to any object under the sensitive/
prefix unless the request uses SSE-C with the AES256
algorithm. This ensures that only objects stored under my-bucket/sensitive/
require customer-provided encryption keys, while other objects in the bucket are not affected by this policy.
{"Version": "2012-10-17","Statement": [{"Sid": "EnforceSSECForSensitiveData","Effect": "Deny","Principal": "*","Action": ["s3:PutObject"],"Resource": "arn:aws:s3:::my-bucket/sensitive/*","Condition": {"StringNotEquals": {"s3:x-amz-server-side-encryption-customer-algorithm": "AES256"}}}]}
Allow specific encryption keys
For enhanced security, you can restrict uploads to specific encryption keys by checking the key hash:
{"Version": "2012-10-17","Statement": [{"Sid": "AllowSpecificEncryptionKey","Effect": "Allow","Principal": "*","Action": ["s3:PutObject"],"Resource": "arn:aws:s3:::my-bucket/*","Condition": {"StringEquals": {"s3:x-amz-server-side-encryption-customer-algorithm": "AES256","s3:x-amz-server-side-encryption-customer-key-md5": "your-expected-key-md5-hash"}}}]}
Key rotation
Regularly rotate your encryption keys to maintain security:
- Python (boto3)
- AWS CLI
- curl
import datetimeimport secretsimport base64class KeyManager:def __init__(self):self.current_key = self.load_current_key()self.key_expiry = datetime.datetime.now() + datetime.timedelta(days=90)def should_rotate_key(self):return datetime.datetime.now() >= self.key_expirydef rotate_key(self):# Generate new keynew_key_bytes = secrets.token_bytes(32)new_key = base64.b64encode(new_key_bytes).decode('utf-8')# Re-encrypt data with new key (if needed)self.re_encrypt_data(new_key)# Update current keyself.current_key = new_keyself.key_expiry = datetime.datetime.now() + datetime.timedelta(days=90)
# Check if key rotation is needed (check expiry date)current_date=$(date +%s)key_expiry=$(date -d "2024-04-01" +%s) # Example expiry dateif [ $current_date -ge $key_expiry ]; thenecho "Key rotation needed"# Generate new keyNEW_BASE64_KEY=$(head -c 32 /dev/urandom | base64)# Store new key securelyaws secretsmanager put-secret-value \--secret-id "my-sse-c-key" \--secret-string "{\"key\":\"$NEW_BASE64_KEY\",\"created\":\"$(date -Iseconds)\"}"echo "New key generated and stored"elseecho "Key rotation not needed yet"fi
# Check if key rotation is neededcurrent_date=$(date +%s)key_expiry=$(date -d "2024-04-01" +%s) # Example expiry dateif [ $current_date -ge $key_expiry ]; thenecho "Key rotation needed"# Generate new keyNEW_BASE64_KEY=$(head -c 32 /dev/urandom | base64)# Store new key securely (example using a secure endpoint)curl -X POST \-H "Content-Type: application/json" \-d "{\"key\":\"$NEW_BASE64_KEY\",\"created\":\"$(date -Iseconds)\"}" \"https://your-key-management-endpoint.com/keys"echo "New key generated and stored"elseecho "Key rotation not needed yet"fi
Performance optimization
Cache encryption headers to avoid recalculating them for every request:
- Python (boto3)
- AWS CLI
- curl
import timeclass CachedSSEHeaders:def __init__(self, key_manager):self.key_manager = key_managerself._cached_headers = Noneself._cache_timestamp = Noneself._cache_duration = 3600 # 1 hourdef get_headers(self):now = time.time()if (self._cached_headers is None orself._cache_timestamp is None ornow - self._cache_timestamp > self._cache_duration):self._cached_headers = self.key_manager.get_encryption_headers()self._cache_timestamp = nowreturn self._cached_headers
# Cache encryption headers in environment variables# Calculate headers once and reuse them# Generate and cache headersBASE64_KEY="your-encryption-key"KEY_MD5=$(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)# Export for reuse in subsequent commandsexport SSE_CUSTOMER_ALGORITHM="AES256"export SSE_CUSTOMER_KEY="$BASE64_KEY"export SSE_CUSTOMER_KEY_MD5="$KEY_MD5"# Use cached headers in AWS CLI commandsaws s3 cp myfile.txt s3://my-bucket/ \--sse-customer-algorithm "$SSE_CUSTOMER_ALGORITHM" \--sse-customer-key "$SSE_CUSTOMER_KEY" \--sse-customer-key-md5 "$SSE_CUSTOMER_KEY_MD5"
# Cache encryption headers in shell variables# Calculate headers once and reuse them# Generate and cache headersBASE64_KEY="your-encryption-key"KEY_MD5=$(echo -n "$BASE64_KEY" | base64 -d | openssl dgst -md5 -binary | base64)# Store headers in variables for reuseSSE_HEADERS="-H \"x-amz-server-side-encryption-customer-algorithm: AES256\" \-H \"x-amz-server-side-encryption-customer-key: $BASE64_KEY\" \-H \"x-amz-server-side-encryption-customer-key-md5: $KEY_MD5\""# Use cached headers in curl commandscurl -X PUT $SSE_HEADERS \-T myfile.txt \"https://object-storage.coreweave.com/my-bucket/myfile.txt"
Audit logging
Maintain comprehensive audit logs for key operations:
- Python (boto3)
- AWS CLI
- curl
import loggingimport timelogger = logging.getLogger(__name__)def upload_with_monitoring(s3_client, bucket, key, file_path, key_manager):start_time = time.time()try:headers = key_manager.get_encryption_headers()s3_client.upload_file(file_path, bucket, key, ExtraArgs=headers)duration = time.time() - start_timelogger.info(f"Successfully uploaded {key} with SSE-C in {duration:.2f}s")except Exception as e:duration = time.time() - start_timelogger.error(f"Failed to upload {key} with SSE-C after {duration:.2f}s: {e}")raise
# Enable AWS CLI logging for audit purposesexport AWS_CLI_LOG_LEVEL=debug# Create a wrapper script for audit loggingupload_with_audit() {local file_path=$1local bucket=$2local key=$3local start_time=$(date +%s.%N)echo "$(date): Starting upload of $key to $bucket" >> audit.logif aws s3 cp "$file_path" "s3://$bucket/$key" \--sse-customer-algorithm AES256 \--sse-customer-key "$BASE64_KEY" \--sse-customer-key-md5 "$KEY_MD5"; thenlocal end_time=$(date +%s.%N)local duration=$(echo "$end_time - $start_time" | bc)echo "$(date): Successfully uploaded $key in ${duration}s" >> audit.logelselocal end_time=$(date +%s.%N)local duration=$(echo "$end_time - $start_time" | bc)echo "$(date): Failed to upload $key after ${duration}s" >> audit.logreturn 1fi}# Use the wrapper functionupload_with_audit myfile.txt my-bucket myfile.txt
# Create a wrapper script for audit logging with curlupload_with_audit() {local file_path=$1local bucket=$2local key=$3local start_time=$(date +%s.%N)echo "$(date): Starting upload of $key to $bucket" >> audit.logif curl -X PUT \-H "x-amz-server-side-encryption-customer-algorithm: AES256" \-H "x-amz-server-side-encryption-customer-key: $BASE64_KEY" \-H "x-amz-server-side-encryption-customer-key-md5: $KEY_MD5" \-T "$file_path" \"https://object-storage.coreweave.com/$bucket/$key" \-s -o /dev/null -w "%{http_code}"; thenlocal end_time=$(date +%s.%N)local duration=$(echo "$end_time - $start_time" | bc)echo "$(date): Successfully uploaded $key in ${duration}s" >> audit.logelselocal end_time=$(date +%s.%N)local duration=$(echo "$end_time - $start_time" | bc)echo "$(date): Failed to upload $key after ${duration}s" >> audit.logreturn 1fi}# Use the wrapper functionupload_with_audit myfile.txt my-bucket myfile.txt
Testing your implementation
Test key validation
Implement comprehensive tests for key validation:
- Python (boto3)
- AWS CLI
- curl
def test_key_validation():# Test valid keyvalid_key_bytes = secrets.token_bytes(32)valid_key = base64.b64encode(valid_key_bytes).decode('utf-8')assert validate_encryption_key(valid_key) == True# Test invalid key lengthtry:validate_encryption_key("short")assert False, "Should raise ValueError"except ValueError:pass# Test invalid base64try:validate_encryption_key("invalid-base64-key-here")assert False, "Should raise ValueError"except ValueError:pass
# Test key validation with AWS CLItest_key_validation() {local key=$1# Test if key is base64 encoded and 32 bytesif echo "$key" | base64 -d | wc -c | grep -q "^33$"; thenecho "Valid key: Key is 32 bytes when decoded"elseecho "Invalid key: Key must be 32 bytes when decoded"return 1fi# Test if key is valid base64if echo "$key" | base64 -d > /dev/null 2>&1; thenecho "Valid key: Key is valid base64"elseecho "Invalid key: Key must be valid base64"return 1fi}# Test with valid keyVALID_KEY=$(head -c 32 /dev/urandom | base64)test_key_validation "$VALID_KEY"# Test with invalid keytest_key_validation "invalid-key"
# Test key validation with curltest_key_validation() {local key=$1# Test if key is base64 encoded and 32 bytesif echo "$key" | base64 -d | wc -c | grep -q "^33$"; thenecho "Valid key: Key is 32 bytes when decoded"elseecho "Invalid key: Key must be 32 bytes when decoded"return 1fi# Test if key is valid base64if echo "$key" | base64 -d > /dev/null 2>&1; thenecho "Valid key: Key is valid base64"elseecho "Invalid key: Key must be valid base64"return 1fi}# Test with valid keyVALID_KEY=$(head -c 32 /dev/urandom | base64)test_key_validation "$VALID_KEY"# Test with invalid keytest_key_validation "invalid-key"
Test error handling
Test for key-related errors:
- Python (boto3)
- AWS CLI
- curl
def test_key_error_handling():# Test with incorrect keywrong_key_bytes = secrets.token_bytes(32)wrong_key = base64.b64encode(wrong_key_bytes).decode('utf-8')try:upload_with_sse_c(s3_client, bucket, key, file_path, wrong_key)assert False, "Should raise KeyError"except KeyError:pass
# Test error handling with AWS CLItest_error_handling() {local wrong_key=$(head -c 32 /dev/urandom | base64)# Test upload with wrong keyif aws s3 cp testfile.txt s3://my-bucket/ \--sse-customer-algorithm AES256 \--sse-customer-key "$wrong_key" \--sse-customer-key-md5 $(echo -n "$wrong_key" | base64 -d | openssl dgst -md5 -binary | base64) 2>&1 | grep -q "AccessDenied"; thenecho "Correctly detected wrong key"elseecho "Failed to detect wrong key"return 1fi}# Run error handling testtest_error_handling
# Test error handling with curltest_error_handling() {local wrong_key=$(head -c 32 /dev/urandom | base64)# Test upload with wrong keyresponse=$(curl -X PUT \-H "x-amz-server-side-encryption-customer-algorithm: AES256" \-H "x-amz-server-side-encryption-customer-key: $wrong_key" \-H "x-amz-server-side-encryption-customer-key-md5: $(echo -n "$wrong_key" | base64 -d | openssl dgst -md5 -binary | base64)" \-T testfile.txt \"https://object-storage.coreweave.com/my-bucket/testfile.txt" \-s -w "%{http_code}")if [ "$response" = "403" ]; thenecho "Correctly detected wrong key"elseecho "Failed to detect wrong key"return 1fi}# Run error handling testtest_error_handling
Additional resources
- Review the SSE-C concept guide to learn more about how SSE-C works
- Check the S3 API reference for detailed SSE-C headers and parameters
- Explore key management best practices for production deployments
- For more information about SSE-C implementation, see the AWS documentation on using server-side encryption with customer-provided keys