어떤 주제로 잡을지 고민을 많이 했는데.. 그래도 나한테 의미있는 주제로 하고 싶어서 침해사고와 관련된 시나리오로 만들었다.
주제는 거창해보이지만 많은 기능이 있지는 않다.
근데 하다보니 뭔가 생각보다 사이즈가 좀 커지고 있는 느낌이긴 하지만 일단 킵고잉.
개요는 대충 아래와 같다.
.log 파일 생성 및 보관.envpkg 생성 : ZIP 파일로 구조화encrypted_log.enclog.hashsignature.sigaes_key.enc.envpkg 생성aes_key.enc를 자신의 개인키로 복호화 -> AES 키 획득encrypted_log.enc를 AES 키로 복호화 -> 원본 로그 획득signature.sig 서명 검증log.hash 비교 -> 일치 여부 확인




package service;
import java.nio.file.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.spec.*;
public class CryptoUtil {
public static SecretKey generateAESKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
return keyGen.generateKey();
}
public static byte[] encryptAES(byte[] data, SecretKey key) throws Exception{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
public static byte[] decryptAES(byte[] encrypted, SecretKey key) throws Exception{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(encrypted);
}
public static byte[] generateHash(byte[] data) throws Exception{
MessageDigest md = MessageDigest.getInstance("SHA-512");
return md.digest(data);
}
public static byte[] sign(byte[] hash, PrivateKey privateKey) throws Exception {
Signature sig = Signature.getInstance("SHA512withRSA");
sig.initSign(privateKey);
sig.update(hash);
return sig.sign();
}
public static boolean verifySignature(byte[] hash, byte[] signature, PublicKey publicKey) throws Exception {
Signature sig = Signature.getInstance("SHA512withRSA");
sig.initVerify(publicKey);
sig.update(hash);
return sig.verify(signature);
}
public static void saveAESKey(SecretKey key, Path path) throws Exception {
byte[] encoded = key.getEncoded();
Files.write(path, encoded);
}
public static SecretKey loadAESKey(Path path) throws Exception {
byte[] encoded = Files.readAllBytes(path);
return new SecretKeySpec(encoded, "AES");
}
public static PrivateKey loadPrivateKey(Path path) throws Exception {
byte[] keyBytes = Files.readAllBytes(path);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
return KeyFactory.getInstance("RSA").generatePrivate(spec);
}
public static PublicKey loadPublicKey(Path path) throws Exception {
byte[] keyBytes = Files.readAllBytes(path);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
return KeyFactory.getInstance("RSA").generatePublic(spec);
}
}
package service;
import java.io.IOException;
import java.nio.file.*;
import java.security.*;
import javax.crypto.*;
public class KeyManager {
public static KeyPair generateRSAKeyPair(int keySize) throws NoSuchAlgorithmException{
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(keySize);
return generator.generateKeyPair();
}
public static void savePrivateKey(PrivateKey key, Path path) throws IOException {
Files.write(path, key.getEncoded());
}
public static void savePublicKey(PublicKey key, Path path) throws IOException {
Files.write(path,key.getEncoded());
}
public static SecretKey generateAESKey() throws Exception {
return CryptoUtil.generateAESKey();
}
public static void saveAESKey(SecretKey key, Path path) throws Exception{
CryptoUtil.saveAESKey(key, path);
}
}
package service;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.crypto.*;
public class EnvelopeService {
public static Path createEnvelope(Path logFile, Path privateKeyFile, Path outputDir) throws Exception {
byte[] logData = Files.readAllBytes(logFile);
PrivateKey privateKey = CryptoUtil.loadPrivateKey(privateKeyFile);
SecretKey key = CryptoUtil.generateAESKey();
byte[] encrypted = CryptoUtil.encryptAES(logData, key);
byte[] hash = CryptoUtil.generateHash(logData);
byte[] signature = CryptoUtil.sign(hash, privateKey);
Path keyPath = outputDir.resolve("aes.key");
CryptoUtil.saveAESKey(key, keyPath);
String baseName = logFile.getFileName().toString().replace(".log", "");
Path envelopePath = outputDir.resolve(baseName+".envpkg");
try(FileOutputStream fos = new FileOutputStream(envelopePath.toFile());
ZipOutputStream zos = new ZipOutputStream(fos)){
zos.putNextEntry(new ZipEntry("encrypted_log.bin"));
zos.write(encrypted);
zos.closeEntry();
zos.putNextEntry(new ZipEntry("signature.sig"));
zos.write(signature);
zos.closeEntry();
zos.putNextEntry(new ZipEntry("hash.txt"));
zos.write(hash);
zos.closeEntry();
}
return envelopePath;
}
}
package service;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.PublicKey;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.crypto.SecretKey;
public class EnvelopeVerifier {
public static boolean verifyEnvelope(Path envelopePath, Path publicKeyPath, Path aesKeyPath, Path outputLogPath) throws Exception {
byte[] encrypted = null;
byte[] signature = null;
byte[] hash = null;
try(ZipInputStream zis = new ZipInputStream(new FileInputStream(envelopePath.toFile()))){
ZipEntry entry;
while((entry = zis.getNextEntry()) != null ) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len=zis.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
switch(entry.getName()) {
case "encrypted_log.bin":
encrypted = baos.toByteArray();
break;
case "signature.sig":
signature = baos.toByteArray();
break;
case "hash.txt":
hash = baos.toByteArray();
break;
}
zis.closeEntry();
}
}
if(encrypted == null || signature == null || hash == null) {
throw new IOException("봉투 내 필수 파일이 누락되었습니다.");
}
PublicKey publicKey = CryptoUtil.loadPublicKey(publicKeyPath);
SecretKey aesKey = CryptoUtil.loadAESKey(aesKeyPath);
// 검증
boolean verified = CryptoUtil.verifySignature(hash, signature, publicKey);
if (!verified) {
return false;
}
// 복호화
byte[] decrypted = CryptoUtil.decryptAES(encrypted, aesKey);
Files.write(outputLogPath, decrypted);
return true;
}
}
꼭 DB가 필요할지 내 욕심인지
다들 DB를 써서 만들지 어떨진 모르겠지만 . . . 점점 일이 커지는 느낌 엉엉
Spring으로 처음부터 다시 만들기로 했습니당,,