Add secure file upload and download with per-user AES encryption

- FileController encrypts uploads using AES-GCM with salt and IV.
- Downloads are decrypted on-the-fly using user-supplied password.
- File metadata (salt, IV, username, path) stored in DB.
This commit is contained in:
K
2025-07-03 02:32:42 +05:30
parent c133617990
commit c88cb5ac0e
3 changed files with 112 additions and 0 deletions
@@ -0,0 +1,79 @@
package com.skycrate.backend.skycrateBackend.controller;
import com.skycrate.backend.skycrateBackend.entity.FileMetadata;
import com.skycrate.backend.skycrateBackend.repository.FileMetadataRepository;
import com.skycrate.backend.skycrateBackend.security.EncryptionService;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.crypto.SecretKey;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.Optional;
@RestController
@RequestMapping("/api/files")
public class FileController {
@Autowired
private FileSystem hdfs;
@Autowired
private FileMetadataRepository metadataRepo;
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file,
@RequestParam("password") String password,
Authentication auth) throws Exception {
byte[] fileBytes = file.getBytes();
byte[] salt = EncryptionService.generateSalt();
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
SecretKey key = EncryptionService.deriveKey(password, salt);
byte[] encrypted = EncryptionService.encrypt(fileBytes, key, iv);
String pathStr = "/user/" + auth.getName() + "/" + file.getOriginalFilename();
Path hdfsPath = new Path(pathStr);
try (FSDataOutputStream out = hdfs.create(hdfsPath, true)) {
out.write(encrypted);
}
FileMetadata metadata = new FileMetadata();
metadata.setUsername(auth.getName());
metadata.setFilePath(pathStr);
metadata.setSalt(salt);
metadata.setIv(iv);
metadataRepo.save(metadata);
return "File uploaded and encrypted successfully!";
}
@GetMapping("/download")
public void download(@RequestParam("path") String path,
@RequestParam("password") String password,
Authentication auth,
OutputStream responseStream) throws Exception {
Optional<FileMetadata> optional = metadataRepo.findByFilePathAndUsername(path, auth.getName());
if (optional.isEmpty()) {
throw new SecurityException("You are not authorized to access this file.");
}
FileMetadata metadata = optional.get();
SecretKey key = EncryptionService.deriveKey(password, metadata.getSalt());
try (FSDataInputStream input = hdfs.open(new Path(path))) {
byte[] encrypted = input.readAllBytes();
byte[] decrypted = EncryptionService.decrypt(encrypted, key, metadata.getIv());
responseStream.write(decrypted);
}
}
}
@@ -0,0 +1,23 @@
package com.skycrate.backend.skycrateBackend.entity;
import jakarta.persistence.*;
@Entity
public class FileMetadata {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String filePath;
private String username;
@Lob
private byte[] salt;
@Lob
private byte[] iv;
// Getters and Setters
}
@@ -0,0 +1,10 @@
package com.skycrate.backend.skycrateBackend.repository;
import com.skycrate.backend.skycrateBackend.entity.FileMetadata;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface FileMetadataRepository extends JpaRepository<FileMetadata, Long> {
Optional<FileMetadata> findByFilePathAndUsername(String filePath, String username);
}