1. Background and needs
In cloud storage scenarios, data security is one of the core requirements. As a high-performance object storage service, MinIO supports it throughClient Encryption (CSE)Encryption is completed before data is uploaded to ensure that even if the storage server is compromised, the attacker cannot obtain plaintext data. This article will explain in detail how to implement encryption, upload and decryption and download of MinIO files through Java, combining AES symmetric encryption algorithm and BouncyCastle encryption library to provide complete code examples and security practice suggestions.
2. Technical selection and principle
1. Comparison of encryption schemes
Way | Features | Applicable scenarios |
---|---|---|
Server encryption | MinIO automatically handles encryption, and the key is managed by the server | Scenarios with low requirements for key management |
Client encryption | The data is uploaded after the client is encrypted, and the key is managed by the application (this solution is adopted in this article) | High security requirements scenarios |
2. Core algorithm selection
AES-256-CBC: Adopt 256-bit key and CBC mode, and need to be used to enhance security with random IV
BouncyCastle Library: Provides a complete implementation of the AES algorithm, and requires adding dependencies:
<dependency> <groupId></groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency>
3. Complete code implementation
1. Encrypt the upload core logic
public void minioFileEncryptionUpload(String bucketName, String folder, String objectName, String filePath) { ("Prepare to encrypt and upload files toMinIO,path:{}", filePath); try { // 1. Check and create a bucket boolean b = (bucketName); if (!b) { ("bucket:{},Does not exist!create", bucketName); (bucketName); } boolean f = (bucketName, folder); if (!f) { ("Folders:{},Does not exist!create", folder); } ("Upload file to minio start"); // 2. Make sure the folder exists (simulated by uploading empty objects) String folderKey = ("/") ? folder : folder + "/"; if (!(bucketName, folderKey)) { ("Folders:{} Does not exist,create空对象", folderKey); // Fixed: Clearly set the Content-Type of the empty object ( bucketName, new MockMultipartFile("folder", "", "application/json", "".getBytes()), // Modify point: Specify the default type folderKey, "application/json" // Modify point: explicitly pass Content-Type ); } // 3. Load the key Key secretKey = new SecretKeySpec(SECRET_KEY.getBytes(), AES_ALGORITHM); // 4. Read the file and encrypt it File file = new File(filePath); try (InputStream fileInputStream = new FileInputStream(file); CipherInputStream encryptedStream = new CipherInputStream(fileInputStream, getCipher(secretKey, Cipher.ENCRYPT_MODE))) { // 5. Build the encrypted MultipartFile (fix point: dynamic inference Content-Type) String detectedContentType = (()); // Infer type using system API if (detectedContentType == null) { detectedContentType = "application/octet-stream"; // Default type } MultipartFile encryptedFile = new MockMultipartFile( (), (), detectedContentType, // Modify point: Dynamic setting type (encryptedStream) ); // 6. Upload the encrypted file to MinIO (repair point: Force verification Content-Type) ("Start encrypted upload files to MinIO"); ( bucketName, encryptedFile, folder + objectName, () // Make sure it is not empty ); ("Encryption upload completed,文件path:{}", folder + objectName); } } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) { ("Key or encryption algorithm error", e); throw new RuntimeException("Encryption failed: key or algorithm configuration error", e); } catch (IOException | GeneralSecurityException e) { ("File processing or encryption exception", e); throw new RuntimeException("Encryption failed: file processing error", e); } catch (MinioException e) { ("MinIO operation exception", e); throw new RuntimeException("Upload failed: MinIO service exception", e); } } private Cipher getCipher(Key key, int mode) throws GeneralSecurityException { Cipher cipher = (AES_TRANSFORMATION, "BC"); // Use BouncyCastle Provider (mode, key); return cipher; }
2. Decrypt and download implementation
/** * Download the encrypted file from MinIO and decrypt it, returning to the decrypted input stream * * @param fileSaveName Encrypted file object name * @return Decrypted InputStream * @throws Exception Decryption exception */ public InputStream decryptFileFromMinio(String fileSaveName) throws Exception { String bucketName = (); // The stream is not automatically closed, it is handled by the caller InputStream encryptedStream = (bucketName, fileSaveName); Cipher cipher = (AES_TRANSFORMATION); (Cipher.DECRYPT_MODE, new SecretKeySpec(SECRET_KEY.getBytes(), AES_ALGORITHM)); return new CipherInputStream(encryptedStream, cipher); } /** * Download the encrypted file and decrypt it into a byte array * * @param fileSaveName Encrypted file object name * @return Decrypted byte array * @throws Exception Decryption exception */ public byte[] decryptFileToBytes(String fileSaveName) throws Exception { ("Start reading the encrypted stream"); InputStream encryptedStream = null; ByteArrayOutputStream outputStream = null; try { encryptedStream = decryptFileFromMinio(fileSaveName); outputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024 * 10]; int bytesRead; while ((bytesRead = (buffer)) != -1) { (buffer, 0, bytesRead); } ("Encrypted stream reading is completed"); return (); } finally { // Make sure the stream is finally closed if (encryptedStream != null) { try { (); } catch (IOException e) { // Logging ("Exception occurred while closing the input stream", e); } } if (outputStream != null) { try { (); } catch (IOException e) { // Logging ("Exception occurred while closing the output stream", e); } } } }
4. Analysis of key implementation details
1. Folder creation optimization
Simulate folders by uploading empty objects:
String folderKey = ("/") ? folder : folder + "/"; if (!(bucketName, folderKey)) { (bucketName, new MockMultipartFile("folder", "", "application/json", new byte[0]), folderKey, "application/json" ); }
2. Encrypted stream processing
IV Management: CBC mode requires random generation of IV, it is recommended to store IV with ciphertext
Cipher cipher = (AES_TRANSFORMATION); byte[] iv = new byte[()]; SecureRandom random = new SecureRandom(); (iv); (Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
Exception handling: Capture and distinguish algorithm exceptions, IO exceptions, etc.
5. Safety enhancement suggestions
1. Key management
Use key management systems such as Vault
Avoid hardcoded keys (in the exampleSECRET_KEY
Demo only)
// It is recommended to read from environment variables in the production environmentString secretKey = ("ENCRYPTION_KEY");
2. Crypto mode optimization
Recommended useAES-256-GCMMode (Java 11+ required)
Cipher cipher = ("AES/GCM/NoPadding");
3. Integrity verification
Add HMAC signature verification
Mac mac = ("HmacSHA256"); (secretKey); byte[] hmac = (encryptedData);
6. Complete project dependency
<dependencies> <!-- MinIOClient --> <dependency> <groupId></groupId> <artifactId>minio</artifactId> <version>8.5.2</version> </dependency> <!-- BouncyCastleEncryption library --> <dependency> <groupId></groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency> <!-- Apache Commons IO --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> </dependencies>
7. Expand application scenarios
- Large file shard encryption: Combined with MinIO shard upload API to implement streaming processing
- Key rotation mechanism: Regularly update the encryption key and reencrypt historical data
- Audit log: Record the timestamp and operator information of the encryption operation
The above is the detailed content of Java implements the encryption and decryption operation of MinIO file upload. For more information about Java MinIO file upload, please pay attention to my other related articles!