Microsoft Azure client library for Blob Storage - Azure Blob Storage is Microsoft's object storage solution for the cloud, optimized for storing massive amounts of unstructured data such as text or binary data.
—
This documentation covers comprehensive security features in the Azure Storage Blob Java SDK, including authentication methods, SAS (Shared Access Signature) tokens, encryption, and access control.
Modern authentication using Azure Active Directory and managed identities.
import com.azure.identity.*;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
// Default Azure Credential (tries multiple methods automatically)
DefaultAzureCredential defaultCredential = new DefaultAzureCredentialBuilder()
.build();
BlobServiceClient serviceClient = new BlobServiceClientBuilder()
.endpoint("https://myaccount.blob.core.windows.net")
.credential(defaultCredential)
.buildClient();
// Managed Identity (for Azure-hosted applications)
ManagedIdentityCredential managedIdentityCredential = new ManagedIdentityCredentialBuilder()
.clientId("user-assigned-identity-client-id") // Optional for user-assigned identity
.build();
BlobServiceClient managedIdentityClient = new BlobServiceClientBuilder()
.endpoint("https://myaccount.blob.core.windows.net")
.credential(managedIdentityCredential)
.buildClient();
// Service Principal (for applications)
ClientSecretCredential servicePrincipalCredential = new ClientSecretCredentialBuilder()
.clientId("application-client-id")
.clientSecret("application-client-secret")
.tenantId("azure-tenant-id")
.build();
BlobServiceClient spClient = new BlobServiceClientBuilder()
.endpoint("https://myaccount.blob.core.windows.net")
.credential(servicePrincipalCredential)
.buildClient();
// Interactive authentication (for user applications)
InteractiveBrowserCredential interactiveCredential = new InteractiveBrowserCredentialBuilder()
.clientId("application-client-id")
.redirectUrl("http://localhost:8080/auth/callback")
.build();
BlobServiceClient interactiveClient = new BlobServiceClientBuilder()
.endpoint("https://myaccount.blob.core.windows.net")
.credential(interactiveCredential)
.buildClient();
// Azure CLI credential (for development)
AzureCliCredential cliCredential = new AzureCliCredentialBuilder()
.build();
BlobServiceClient cliClient = new BlobServiceClientBuilder()
.endpoint("https://myaccount.blob.core.windows.net")
.credential(cliCredential)
.buildClient();Traditional authentication using storage account access keys.
import com.azure.storage.common.StorageSharedKeyCredential;
// Create shared key credential
StorageSharedKeyCredential sharedKeyCredential = new StorageSharedKeyCredential(
"mystorageaccount",
"base64-encoded-account-key-here"
);
BlobServiceClient sharedKeyClient = new BlobServiceClientBuilder()
.endpoint("https://mystorageaccount.blob.core.windows.net")
.credential(sharedKeyCredential)
.buildClient();
// Using connection string (contains account name and key)
String connectionString = "DefaultEndpointsProtocol=https;" +
"AccountName=mystorageaccount;" +
"AccountKey=base64-encoded-key;" +
"EndpointSuffix=core.windows.net";
BlobServiceClient connectionStringClient = new BlobServiceClientBuilder()
.connectionString(connectionString)
.buildClient();
// Key rotation example
public class StorageAccountKeyManager {
private volatile StorageSharedKeyCredential currentCredential;
private final String accountName;
public StorageAccountKeyManager(String accountName, String initialKey) {
this.accountName = accountName;
this.currentCredential = new StorageSharedKeyCredential(accountName, initialKey);
}
public void rotateKey(String newKey) {
StorageSharedKeyCredential newCredential = new StorageSharedKeyCredential(accountName, newKey);
// Test new credential
BlobServiceClient testClient = new BlobServiceClientBuilder()
.endpoint("https://" + accountName + ".blob.core.windows.net")
.credential(newCredential)
.buildClient();
try {
// Verify new credential works
testClient.getAccountInfo();
// Switch to new credential
this.currentCredential = newCredential;
System.out.println("Successfully rotated to new storage key");
} catch (Exception ex) {
System.err.println("Key rotation failed, keeping current key: " + ex.getMessage());
throw ex;
}
}
public StorageSharedKeyCredential getCurrentCredential() {
return currentCredential;
}
}Generate SAS tokens for service-level access.
import com.azure.storage.blob.sas.*;
import com.azure.storage.common.sas.*;
import java.time.OffsetDateTime;
// Account-level SAS (access to multiple services)
OffsetDateTime expiryTime = OffsetDateTime.now().plusDays(1);
OffsetDateTime startTime = OffsetDateTime.now();
AccountSasSignatureValues accountSasValues = new AccountSasSignatureValues(
expiryTime,
AccountSasPermission.parse("rwdlacup"), // read, write, delete, list, add, create, update, process
AccountSasService.parse("bfqt"), // blob, file, queue, table services
AccountSasResourceType.parse("sco") // service, container, object
)
.setStartTime(startTime)
.setProtocol(SasProtocol.HTTPS_ONLY)
.setSasIpRange(SasIpRange.parse("192.168.1.0/24"));
// Generate account SAS
String accountSas = serviceClient.generateAccountSas(accountSasValues);
System.out.println("Account SAS token: " + accountSas);
// Use account SAS to create new client
String accountSasUrl = serviceClient.getAccountUrl() + "?" + accountSas;
BlobServiceClient sasServiceClient = new BlobServiceClientBuilder()
.endpoint(accountSasUrl)
.buildClient();
// Test account SAS permissions
try {
PagedIterable<BlobContainerItem> containers = sasServiceClient.listBlobContainers();
System.out.println("Account SAS is valid - can list containers");
} catch (Exception ex) {
System.err.println("Account SAS validation failed: " + ex.getMessage());
}Generate SAS tokens for container access.
// Container SAS with specific permissions
BlobContainerSasPermission containerPermission = new BlobContainerSasPermission()
.setReadPermission(true)
.setListPermission(true)
.setAddPermission(true)
.setCreatePermission(true)
.setWritePermission(false) // No write permission
.setDeletePermission(false); // No delete permission
BlobServiceSasSignatureValues containerSasValues = new BlobServiceSasSignatureValues(
OffsetDateTime.now().plusHours(6), // 6 hour expiry
containerPermission
)
.setStartTime(OffsetDateTime.now())
.setProtocol(SasProtocol.HTTPS_ONLY)
.setCacheControl("no-cache")
.setContentDisposition("attachment")
.setContentType("application/octet-stream")
.setContentLanguage("en-US")
.setContentEncoding("gzip");
// Generate container SAS
String containerSas = containerClient.generateSas(containerSasValues);
System.out.println("Container SAS token: " + containerSas);
// Create container URL with SAS
String containerSasUrl = containerClient.getBlobContainerUrl() + "?" + containerSas;
BlobContainerClient sasContainerClient = new BlobContainerClientBuilder()
.endpoint(containerSasUrl)
.buildClient();
// Test container SAS
try {
PagedIterable<BlobItem> blobs = sasContainerClient.listBlobs();
System.out.println("Container SAS is valid - can list blobs");
// Try to create a new blob
BlobClient sasBlob = sasContainerClient.getBlobClient("test-sas-blob.txt");
sasBlob.upload(BinaryData.fromString("Content created with SAS"), true);
System.out.println("Successfully created blob with container SAS");
} catch (Exception ex) {
System.err.println("Container SAS operation failed: " + ex.getMessage());
}Generate SAS tokens for individual blob access.
// Blob SAS with read-only permission
BlobSasPermission blobPermission = new BlobSasPermission()
.setReadPermission(true)
.setAddPermission(false)
.setCreatePermission(false)
.setWritePermission(false)
.setDeletePermission(false);
BlobServiceSasSignatureValues blobSasValues = new BlobServiceSasSignatureValues(
OffsetDateTime.now().plusMinutes(30), // Short-lived token
blobPermission
)
.setStartTime(OffsetDateTime.now())
.setProtocol(SasProtocol.HTTPS_ONLY)
.setSasIpRange(SasIpRange.parse("203.0.113.0/24"));
// Generate blob SAS
String blobSas = blobClient.generateSas(blobSasValues);
System.out.println("Blob SAS token: " + blobSas);
// Create blob URL with SAS for sharing
String blobSasUrl = blobClient.getBlobUrl() + "?" + blobSas;
System.out.println("Shareable blob URL: " + blobSasUrl);
// Create client using blob SAS
BlobClient sasBlobClient = new BlobClientBuilder()
.endpoint(blobSasUrl)
.buildClient();
// Test blob SAS
try {
BinaryData content = sasBlobClient.downloadContent();
System.out.println("Successfully downloaded blob with SAS: " + content.getLength() + " bytes");
} catch (Exception ex) {
System.err.println("Blob SAS download failed: " + ex.getMessage());
}
// Advanced blob SAS with custom headers
BlobServiceSasSignatureValues advancedBlobSas = new BlobServiceSasSignatureValues(
OffsetDateTime.now().plusDays(7),
new BlobSasPermission().setReadPermission(true)
)
.setContentType("image/jpeg") // Force specific content type
.setContentDisposition("attachment; filename=photo.jpg") // Force download
.setCacheControl("public, max-age=86400") // Cache for 1 day
.setContentEncoding("identity")
.setContentLanguage("en-US");
String advancedBlobSasToken = blobClient.generateSas(advancedBlobSas);Generate SAS using Azure AD credentials (more secure than account key SAS).
// Get user delegation key (requires Azure AD authentication)
OffsetDateTime keyStart = OffsetDateTime.now();
OffsetDateTime keyExpiry = keyStart.plusDays(7);
try {
UserDelegationKey userDelegationKey = serviceClient.getUserDelegationKey(keyStart, keyExpiry);
// Create user delegation SAS
BlobServiceSasSignatureValues userDelegationSasValues = new BlobServiceSasSignatureValues(
OffsetDateTime.now().plusHours(2),
new BlobSasPermission().setReadPermission(true).setListPermission(true)
)
.setStartTime(OffsetDateTime.now())
.setProtocol(SasProtocol.HTTPS_ONLY);
// Generate user delegation SAS (more secure)
String userDelegationSas = serviceClient.generateUserDelegationSas(
userDelegationSasValues,
userDelegationKey
);
System.out.println("User delegation SAS: " + userDelegationSas);
// User delegation SAS for specific blob
BlobServiceSasSignatureValues blobUserDelegationSas = new BlobServiceSasSignatureValues(
OffsetDateTime.now().plusMinutes(30),
new BlobSasPermission().setReadPermission(true)
)
.setStartTime(OffsetDateTime.now())
.setProtocol(SasProtocol.HTTPS_ONLY);
String blobUserDelegationSasToken = blobClient.generateUserDelegationSas(
blobUserDelegationSas,
userDelegationKey
);
System.out.println("Blob user delegation SAS: " + blobUserDelegationSasToken);
} catch (Exception ex) {
System.err.println("User delegation key operation failed: " + ex.getMessage());
System.err.println("Ensure client is authenticated with Azure AD, not account key");
}Use stored access policies for easier SAS management and revocation.
import java.util.List;
import java.util.ArrayList;
// Create stored access policy
BlobSignedIdentifier readOnlyPolicy = new BlobSignedIdentifier()
.setId("ReadOnlyPolicy")
.setAccessPolicy(new BlobAccessPolicy()
.setPermissions("r") // read only
.setStartsOn(OffsetDateTime.now())
.setExpiresOn(OffsetDateTime.now().plusDays(30)));
BlobSignedIdentifier fullAccessPolicy = new BlobSignedIdentifier()
.setId("FullAccessPolicy")
.setAccessPolicy(new BlobAccessPolicy()
.setPermissions("racwdl") // read, add, create, write, delete, list
.setStartsOn(OffsetDateTime.now())
.setExpiresOn(OffsetDateTime.now().plusDays(7)));
List<BlobSignedIdentifier> policies = List.of(readOnlyPolicy, fullAccessPolicy);
// Set access policies on container
containerClient.setAccessPolicy(null, policies); // null = no public access
System.out.println("Stored access policies created");
// Generate SAS using stored policy (no expiry needed in SAS)
BlobServiceSasSignatureValues storedPolicySas = new BlobServiceSasSignatureValues()
.setIdentifier("ReadOnlyPolicy"); // Reference stored policy
String storedPolicySasToken = containerClient.generateSas(storedPolicySas);
System.out.println("SAS with stored policy: " + storedPolicySasToken);
// Update stored policy (affects all existing SAS tokens using this policy)
readOnlyPolicy.getAccessPolicy().setExpiresOn(OffsetDateTime.now().plusDays(15)); // Extend expiry
containerClient.setAccessPolicy(null, List.of(readOnlyPolicy, fullAccessPolicy));
System.out.println("Stored policy updated - existing SAS tokens now expire in 15 days");
// Revoke access by removing policy
containerClient.setAccessPolicy(null, List.of(fullAccessPolicy)); // Remove ReadOnlyPolicy
System.out.println("ReadOnlyPolicy revoked - existing SAS tokens using it are now invalid");Use customer-managed encryption keys for blob operations.
import com.azure.storage.blob.models.CustomerProvidedKey;
import com.azure.storage.blob.models.EncryptionAlgorithmType;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.util.Base64;
// Generate or use existing encryption key
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // 256-bit key
SecretKey secretKey = keyGen.generateKey();
String base64Key = Base64.getEncoder().encodeToString(secretKey.getEncoded());
// Create customer-provided key
CustomerProvidedKey cpk = new CustomerProvidedKey(base64Key)
.setEncryptionAlgorithm(EncryptionAlgorithmType.AES256);
// Create service client with CPK
BlobServiceClient encryptedServiceClient = new BlobServiceClientBuilder()
.connectionString(connectionString)
.customerProvidedKey(cpk)
.buildClient();
BlobClient encryptedBlobClient = encryptedServiceClient
.getBlobContainerClient("encrypted-data")
.getBlobClient("sensitive-document.pdf");
// Upload encrypted content
String sensitiveContent = "This is highly sensitive information that must be encrypted";
encryptedBlobClient.upload(BinaryData.fromString(sensitiveContent), true);
System.out.println("Content uploaded with customer-provided encryption");
// Download requires the same key
BinaryData decryptedContent = encryptedBlobClient.downloadContent();
System.out.println("Decrypted content: " + decryptedContent.toString());
// Key management example
public class EncryptionKeyManager {
private final Map<String, CustomerProvidedKey> keys = new ConcurrentHashMap<>();
public CustomerProvidedKey generateKey(String keyId) {
try {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey secretKey = keyGen.generateKey();
String base64Key = Base64.getEncoder().encodeToString(secretKey.getEncoded());
CustomerProvidedKey cpk = new CustomerProvidedKey(base64Key)
.setEncryptionAlgorithm(EncryptionAlgorithmType.AES256);
keys.put(keyId, cpk);
// In production, securely store the key mapping
storeKeySecurely(keyId, base64Key);
return cpk;
} catch (Exception ex) {
throw new RuntimeException("Failed to generate encryption key", ex);
}
}
public CustomerProvidedKey getKey(String keyId) {
CustomerProvidedKey key = keys.get(keyId);
if (key == null) {
// In production, retrieve from secure store
String base64Key = retrieveKeySecurely(keyId);
if (base64Key != null) {
key = new CustomerProvidedKey(base64Key)
.setEncryptionAlgorithm(EncryptionAlgorithmType.AES256);
keys.put(keyId, key);
}
}
return key;
}
private void storeKeySecurely(String keyId, String key) {
// Implement secure key storage (Azure Key Vault, etc.)
System.out.println("Storing key " + keyId + " securely");
}
private String retrieveKeySecurely(String keyId) {
// Implement secure key retrieval
System.out.println("Retrieving key " + keyId + " securely");
return null; // Return actual key from secure storage
}
}Use server-side encryption scopes for automatic encryption management.
// Create service client with default encryption scope
BlobServiceClient scopedServiceClient = new BlobServiceClientBuilder()
.connectionString(connectionString)
.encryptionScope("production-encryption-scope")
.buildClient();
// All blobs created through this client will use the encryption scope
BlobClient scopedBlob = scopedServiceClient
.getBlobContainerClient("scoped-container")
.getBlobClient("auto-encrypted-file.txt");
scopedBlob.upload(BinaryData.fromString("This content is automatically encrypted"), true);
// Container-level encryption scope enforcement
BlobContainerEncryptionScope containerScope = new BlobContainerEncryptionScope()
.setDefaultEncryptionScope("container-specific-scope")
.setPreventEncryptionScopeOverride(true); // Enforce scope for all blobs
BlobContainerCreateOptions scopedContainerOptions = new BlobContainerCreateOptions()
.setEncryptionScope(containerScope)
.setMetadata(Map.of("encryption", "enforced"));
containerClient.createWithResponse(scopedContainerOptions, Duration.ofMinutes(1), Context.NONE);
// Check encryption scope in blob properties
BlobProperties properties = scopedBlob.getProperties();
System.out.println("Encryption scope: " + properties.getEncryptionScope());
System.out.println("Server encrypted: " + properties.isServerEncrypted());Use request conditions for secure operations.
// Conditional upload (only if blob doesn't exist)
BlobRequestConditions createOnlyConditions = new BlobRequestConditions()
.setIfNoneMatch("*"); // Only if no ETag exists (blob doesn't exist)
try {
blobClient.uploadWithResponse(
new BlobParallelUploadOptions(BinaryData.fromString("New content"))
.setRequestConditions(createOnlyConditions),
Duration.ofMinutes(5),
Context.NONE
);
System.out.println("Blob created successfully");
} catch (BlobStorageException ex) {
if (ex.getStatusCode() == 412) { // Precondition Failed
System.out.println("Blob already exists - upload skipped");
} else {
throw ex;
}
}
// Conditional update (only if blob hasn't changed)
BlobProperties currentProps = blobClient.getProperties();
String currentETag = currentProps.getETag();
BlobRequestConditions updateConditions = new BlobRequestConditions()
.setIfMatch(currentETag); // Only if ETag matches (no concurrent modifications)
try {
blobClient.uploadWithResponse(
new BlobParallelUploadOptions(BinaryData.fromString("Updated content"))
.setRequestConditions(updateConditions),
Duration.ofMinutes(5),
Context.NONE
);
System.out.println("Blob updated successfully");
} catch (BlobStorageException ex) {
if (ex.getStatusCode() == 412) {
System.out.println("Blob was modified by another process - update skipped");
} else {
throw ex;
}
}
// Time-based conditions
BlobRequestConditions timeConditions = new BlobRequestConditions()
.setIfModifiedSince(OffsetDateTime.now().minusHours(1)) // Only if modified in last hour
.setIfUnmodifiedSince(OffsetDateTime.now()); // Only if not modified since now
// Tag-based conditions
BlobRequestConditions tagConditions = new BlobRequestConditions()
.setTagsConditions("\"environment\" = 'staging' AND \"ready\" = 'true'");Use blob leases for exclusive access control.
import com.azure.storage.blob.specialized.BlobLeaseClient;
import com.azure.storage.blob.specialized.BlobLeaseClientBuilder;
// Create lease client
BlobLeaseClient leaseClient = new BlobLeaseClientBuilder()
.blobClient(blobClient)
.buildClient();
try {
// Acquire exclusive lease
String leaseId = leaseClient.acquireLease(60); // 60 second lease
System.out.println("Acquired lease: " + leaseId);
// Perform exclusive operations with lease
BlobRequestConditions leaseConditions = new BlobRequestConditions()
.setLeaseId(leaseId);
// Update blob metadata exclusively
Map<String, String> exclusiveMetadata = Map.of(
"locked-by", "process-12345",
"lock-time", OffsetDateTime.now().toString(),
"operation", "exclusive-update"
);
blobClient.setMetadataWithResponse(
exclusiveMetadata,
leaseConditions,
Duration.ofSeconds(30),
Context.NONE
);
// Upload new content exclusively
blobClient.uploadWithResponse(
new BlobParallelUploadOptions(BinaryData.fromString("Exclusively updated content"))
.setRequestConditions(leaseConditions),
Duration.ofMinutes(2),
Context.NONE
);
System.out.println("Exclusive operations completed");
// Renew lease if more time needed
leaseClient.renewLease();
System.out.println("Lease renewed");
} finally {
try {
// Always release lease when done
leaseClient.releaseLease();
System.out.println("Lease released");
} catch (Exception ex) {
System.err.println("Failed to release lease: " + ex.getMessage());
}
}
// Automatic lease management
public class AutoLease implements AutoCloseable {
private final BlobLeaseClient leaseClient;
private final String leaseId;
public AutoLease(BlobClient blobClient, int durationSeconds) {
this.leaseClient = new BlobLeaseClientBuilder()
.blobClient(blobClient)
.buildClient();
this.leaseId = leaseClient.acquireLease(durationSeconds);
}
public String getLeaseId() {
return leaseId;
}
public void renewLease() {
leaseClient.renewLease();
}
@Override
public void close() {
try {
leaseClient.releaseLease();
} catch (Exception ex) {
System.err.println("Failed to release lease in auto-close: " + ex.getMessage());
}
}
}
// Usage with try-with-resources
try (AutoLease autoLease = new AutoLease(blobClient, 300)) { // 5 minute lease
BlobRequestConditions leaseConditions = new BlobRequestConditions()
.setLeaseId(autoLease.getLeaseId());
// Perform operations with automatic lease cleanup
blobClient.setMetadata(
Map.of("locked", "true"),
leaseConditions
);
// Lease automatically released when leaving try block
}Configure network-level security for blob access.
// SAS with IP restrictions
SasIpRange ipRange = SasIpRange.parse("192.168.1.0/24"); // Allow only from specific subnet
BlobServiceSasSignatureValues restrictedSas = new BlobServiceSasSignatureValues(
OffsetDateTime.now().plusHours(1),
new BlobSasPermission().setReadPermission(true)
)
.setProtocol(SasProtocol.HTTPS_ONLY) // Force HTTPS
.setSasIpRange(ipRange);
String restrictedSasToken = blobClient.generateSas(restrictedSas);
// Multiple IP ranges
SasIpRange multipleRanges = SasIpRange.parse("192.168.1.0/24,10.0.0.0/8");
// Single IP address
SasIpRange singleIp = SasIpRange.parse("203.0.113.42");
// HTTP client configuration with security settings
HttpClient secureHttpClient = new NettyAsyncHttpClientBuilder()
.connectionTimeout(Duration.ofSeconds(30))
.responseTimeout(Duration.ofMinutes(2))
.build();
BlobServiceClient secureClient = new BlobServiceClientBuilder()
.endpoint("https://myaccount.blob.core.windows.net") // Always use HTTPS
.credential(new DefaultAzureCredentialBuilder().build())
.httpClient(secureHttpClient)
.buildClient();Implement comprehensive security logging and monitoring.
import com.azure.core.util.Context;
// Security context for request tracking
public class SecurityContext {
private final String userId;
private final String sessionId;
private final String operationId;
private final OffsetDateTime timestamp;
private final String clientIp;
public SecurityContext(String userId, String sessionId, String clientIp) {
this.userId = userId;
this.sessionId = sessionId;
this.operationId = UUID.randomUUID().toString();
this.timestamp = OffsetDateTime.now();
this.clientIp = clientIp;
}
public Context toAzureContext() {
return Context.NONE
.addData("user-id", userId)
.addData("session-id", sessionId)
.addData("operation-id", operationId)
.addData("client-ip", clientIp)
.addData("timestamp", timestamp);
}
public void logSecurityEvent(String operation, String resource, boolean success, String details) {
String logEntry = String.format(
"[SECURITY] %s | User: %s | Session: %s | Operation: %s | Resource: %s | Success: %s | IP: %s | Details: %s",
timestamp,
userId,
sessionId,
operation,
resource,
success,
clientIp,
details
);
// In production, use proper logging framework
System.out.println(logEntry);
// Send to security monitoring system
sendToSecurityMonitoring(logEntry);
}
private void sendToSecurityMonitoring(String logEntry) {
// Implement security monitoring integration
// e.g., Azure Monitor, Splunk, etc.
}
}
// Secure blob operations with auditing
public class SecureBlobOperations {
private final BlobServiceClient serviceClient;
public SecureBlobOperations(BlobServiceClient serviceClient) {
this.serviceClient = serviceClient;
}
public boolean secureUpload(String containerName, String blobName,
BinaryData content, SecurityContext securityContext) {
try {
BlobClient blobClient = serviceClient
.getBlobContainerClient(containerName)
.getBlobClient(blobName);
// Add security metadata
Map<String, String> securityMetadata = Map.of(
"uploaded-by", securityContext.userId,
"upload-session", securityContext.sessionId,
"upload-time", securityContext.timestamp.toString(),
"client-ip", securityContext.clientIp
);
// Upload with security context
Response<BlockBlobItem> response = blobClient.uploadWithResponse(
new BlobParallelUploadOptions(content)
.setMetadata(securityMetadata)
.setRequestConditions(new BlobRequestConditions()
.setIfNoneMatch("*")), // Prevent overwriting
Duration.ofMinutes(5),
securityContext.toAzureContext()
);
securityContext.logSecurityEvent(
"UPLOAD",
containerName + "/" + blobName,
true,
"File uploaded successfully, size: " + content.getLength() + " bytes"
);
return true;
} catch (Exception ex) {
securityContext.logSecurityEvent(
"UPLOAD",
containerName + "/" + blobName,
false,
"Upload failed: " + ex.getMessage()
);
throw ex;
}
}
public BinaryData secureDownload(String containerName, String blobName,
SecurityContext securityContext) {
try {
BlobClient blobClient = serviceClient
.getBlobContainerClient(containerName)
.getBlobClient(blobName);
// Download with security context
BinaryData content = blobClient.downloadContentWithResponse(
new BlobDownloadOptions(),
Duration.ofMinutes(5),
securityContext.toAzureContext()
).getValue();
securityContext.logSecurityEvent(
"DOWNLOAD",
containerName + "/" + blobName,
true,
"File downloaded successfully, size: " + content.getLength() + " bytes"
);
return content;
} catch (Exception ex) {
securityContext.logSecurityEvent(
"DOWNLOAD",
containerName + "/" + blobName,
false,
"Download failed: " + ex.getMessage()
);
throw ex;
}
}
}
// Usage with security context
SecurityContext userContext = new SecurityContext("user123", "session456", "192.168.1.100");
SecureBlobOperations secureOps = new SecureBlobOperations(serviceClient);
// Secure upload
BinaryData uploadContent = BinaryData.fromString("Sensitive document content");
secureOps.secureUpload("secure-docs", "document.txt", uploadContent, userContext);
// Secure download
BinaryData downloadedContent = secureOps.secureDownload("secure-docs", "document.txt", userContext);Implement compliance features for data retention.
import com.azure.storage.blob.models.BlobImmutabilityPolicy;
import com.azure.storage.blob.models.BlobImmutabilityPolicyMode;
// Set immutability policy (WORM - Write Once Read Many)
OffsetDateTime immutabilityExpiry = OffsetDateTime.now().plusYears(7);
BlobImmutabilityPolicy immutabilityPolicy = new BlobImmutabilityPolicy()
.setExpiryTime(immutabilityExpiry)
.setPolicyMode(BlobImmutabilityPolicyMode.UNLOCKED); // Can be modified
try {
Response<BlobImmutabilityPolicy> policyResponse = blobClient.setImmutabilityPolicyWithResponse(
immutabilityPolicy,
new BlobRequestConditions(),
Duration.ofSeconds(30),
Context.NONE
);
System.out.println("Immutability policy set, expiry: " + policyResponse.getValue().getExpiryTime());
// Lock the policy (cannot be modified once locked)
Response<BlobImmutabilityPolicy> lockedPolicy = blobClient.setImmutabilityPolicyWithResponse(
new BlobImmutabilityPolicy()
.setExpiryTime(immutabilityExpiry)
.setPolicyMode(BlobImmutabilityPolicyMode.LOCKED),
new BlobRequestConditions(),
Duration.ofSeconds(30),
Context.NONE
);
System.out.println("Immutability policy locked");
} catch (Exception ex) {
System.err.println("Failed to set immutability policy: " + ex.getMessage());
}
// Set legal hold
try {
Response<BlobLegalHoldResult> legalHoldResponse = blobClient.setLegalHoldWithResponse(
true, // Set legal hold
Duration.ofSeconds(30),
Context.NONE
);
System.out.println("Legal hold set: " + legalHoldResponse.getValue().hasLegalHold());
// Remove legal hold when appropriate
blobClient.setLegalHoldWithResponse(
false, // Remove legal hold
Duration.ofSeconds(30),
Context.NONE
);
} catch (Exception ex) {
System.err.println("Failed to manage legal hold: " + ex.getMessage());
}
// Check compliance status
BlobProperties complianceProps = blobClient.getProperties();
System.out.println("Has legal hold: " + complianceProps.hasLegalHold());
BlobImmutabilityPolicy currentPolicy = complianceProps.getImmutabilityPolicy();
if (currentPolicy != null) {
System.out.println("Immutability expiry: " + currentPolicy.getExpiryTime());
System.out.println("Policy mode: " + currentPolicy.getPolicyMode());
}public class SecureBlobStorageManager {
private final BlobServiceClient serviceClient;
private final EncryptionKeyManager keyManager;
private final SecurityAuditor auditor;
public SecureBlobStorageManager(String connectionString) {
// Use Azure Identity for authentication
this.serviceClient = new BlobServiceClientBuilder()
.endpoint(extractEndpointFromConnectionString(connectionString))
.credential(new DefaultAzureCredentialBuilder().build())
.buildClient();
this.keyManager = new EncryptionKeyManager();
this.auditor = new SecurityAuditor();
}
public void secureUploadWithComprehensiveSecurity(
String containerName,
String blobName,
byte[] content,
String userId,
Map<String, String> classifications) {
SecurityContext context = new SecurityContext(userId, UUID.randomUUID().toString(), getClientIp());
try {
// 1. Generate or retrieve encryption key
CustomerProvidedKey cpk = keyManager.getOrGenerateKey(blobName);
// 2. Create secure client with encryption
BlobClient secureClient = serviceClient
.getBlobContainerClient(containerName)
.getBlobClient(blobName)
.getCustomerProvidedKeyClient(cpk);
// 3. Set comprehensive metadata including security classifications
Map<String, String> securityMetadata = new HashMap<>();
securityMetadata.put("uploaded-by", userId);
securityMetadata.put("upload-time", OffsetDateTime.now().toString());
securityMetadata.put("encryption-key-id", keyManager.getKeyId(blobName));
securityMetadata.put("content-hash", calculateHash(content));
securityMetadata.putAll(classifications);
// 4. Upload with security conditions
BlobRequestConditions securityConditions = new BlobRequestConditions()
.setIfNoneMatch("*") // Prevent overwriting
.setTagsConditions(buildTagsCondition(classifications));
Response<BlockBlobItem> response = secureClient.uploadWithResponse(
new BlobParallelUploadOptions(BinaryData.fromBytes(content))
.setMetadata(securityMetadata)
.setTags(classifications)
.setRequestConditions(securityConditions)
.setHeaders(new BlobHttpHeaders()
.setContentType(detectContentType(blobName))
.setCacheControl("private, no-cache")),
Duration.ofMinutes(10),
context.toAzureContext()
);
// 5. Set compliance policies if required
if (requiresCompliance(classifications)) {
setCompliancePolicies(secureClient, classifications);
}
// 6. Generate secure access SAS for authorized users
String authorizedSas = generateAuthorizedSas(secureClient, userId, classifications);
// 7. Log security event
auditor.logSecureUpload(userId, containerName, blobName, content.length, classifications);
System.out.println("Secure upload completed successfully");
System.out.println("Authorized access SAS: " + authorizedSas);
} catch (Exception ex) {
auditor.logSecurityFailure(userId, "SECURE_UPLOAD", containerName + "/" + blobName, ex);
throw new SecurityException("Secure upload failed", ex);
}
}
private void setCompliancePolicies(BlobClient client, Map<String, String> classifications) {
String retentionYears = classifications.get("retention-years");
if (retentionYears != null) {
int years = Integer.parseInt(retentionYears);
OffsetDateTime expiry = OffsetDateTime.now().plusYears(years);
client.setImmutabilityPolicy(new BlobImmutabilityPolicy()
.setExpiryTime(expiry)
.setPolicyMode(BlobImmutabilityPolicyMode.UNLOCKED));
}
if ("true".equals(classifications.get("legal-hold"))) {
client.setLegalHold(true);
}
}
private String generateAuthorizedSas(BlobClient client, String userId, Map<String, String> classifications) {
BlobSasPermission permission = new BlobSasPermission().setReadPermission(true);
// Adjust permissions based on classification
if ("public".equals(classifications.get("classification"))) {
permission.setListPermission(true);
}
BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(
OffsetDateTime.now().plusHours(1), // Short-lived token
permission
)
.setStartTime(OffsetDateTime.now())
.setProtocol(SasProtocol.HTTPS_ONLY);
return client.generateSas(sasValues);
}
private String calculateHash(byte[] content) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(content);
return Base64.getEncoder().encodeToString(hash);
} catch (Exception ex) {
throw new RuntimeException("Failed to calculate content hash", ex);
}
}
// Additional helper methods...
}Install with Tessl CLI
npx tessl i tessl/maven-com-azure--azure-storage-blob