PKI 암호화

This commit is contained in:
jaewohong
2025-12-17 09:31:45 +09:00
parent 69804f7a70
commit 48f657e04c
3 changed files with 259 additions and 34 deletions
@@ -7,14 +7,32 @@ import com.nimbusds.jose.jwk.gen.*;
import java.io.FileInputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.cert.Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.KeyStore;
import java.security.PrivateKey;
//공개키 관련 (RSA)
import java.security.interfaces.RSAPublicKey;
import java.security.PublicKey;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.KeyUse;
import java.io.FileNotFoundException;
import java.security.cert.CertificateException;
import java.io.IOException;
import java.util.Collection;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class ECDHESA256Cipher {
private ECKey myECKey;
private ECKey receiverPublicJWK;
private RSAKey receiverPublicRSAKey;
private PublicKey pubKey;
private JWEObject jweObject;
// 생성자: 수신자 키 쌍을 준비
public ECDHESA256Cipher() throws Exception {
@@ -28,69 +46,162 @@ public class ECDHESA256Cipher {
//생성자: 내 키쌍 로드용 생성자 (복호화할 때 필요)
public ECDHESA256Cipher(String myKeystorePath, String storePass, String keyAlias, String keyPass) throws Exception {
// 생성자: 키스토어에서 RSA 개인키/인증서 로드 (복호화용)
public ECDHESA256Cipher(String keystorePath, String storePass, String keyAlias, String keyPass) throws Exception {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(myKeystorePath), storePass.toCharArray());
try (FileInputStream fis = new FileInputStream(keystorePath)) {
ks.load(fis, storePass.toCharArray());
PrivateKey myPrivateKey = (PrivateKey) ks.getKey(keyAlias, keyPass.toCharArray());
X509Certificate myCert = (X509Certificate) ks.getCertificate(keyAlias);
ECPublicKey myPublicKey = (ECPublicKey) myCert.getPublicKey();
PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias, keyPass.toCharArray());
X509Certificate cert = (X509Certificate) ks.getCertificate(keyAlias);
this.myECKey = new ECKey.Builder(Curve.P_256, myPublicKey)
.privateKey(myPrivateKey)
.keyUse(KeyUse.ENCRYPTION)
//.keyID("my-key")
.keyID(myCert.getSerialNumber().toString()) // 인증서 시리얼번호를 keyID로 사용
.build();
if (privateKey == null || cert == null) {
throw new IllegalStateException("키스토어에서 개인키 또는 인증서를 로드하지 못했습니다.");
}
this.pubKey = cert.getPublicKey();
System.out.println(pubKey.getAlgorithm()); // "RSA" 또는 "EC"
if ("RSA".equals(pubKey.getAlgorithm())) {
RSAPublicKey rsaPub = (RSAPublicKey) cert.getPublicKey();
this.receiverPublicRSAKey = new RSAKey.Builder(rsaPub)
.privateKey(privateKey) // ★ 개인키 포함
.keyUse(KeyUse.ENCRYPTION)
.keyID(cert.getSerialNumber().toString())
.build();
} else if ("EC".equals(pubKey.getAlgorithm())) {
ECPublicKey ecPub = (ECPublicKey) cert.getPublicKey();
// EC 복호화를 위해서는 EC 개인키도 필요합니다.
this.myECKey = new ECKey.Builder(Curve.P_256, ecPub)
.privateKey(privateKey)
.keyUse(KeyUse.ENCRYPTION)
.keyID(cert.getSerialNumber().toString())
.build();
} else {
throw new IllegalStateException("지원하지 않는 공개키 알고리즘: " + pubKey.getAlgorithm());
}
}
}
// 생성자: 상대방 인증서에서 공개키 로드
public ECDHESA256Cipher(String certPath) throws Exception {
//0. 개인키 새성
//String certPath = "C:/imsi/mytestcert/signCert.der";
String priPath = "C:/imsi/mytestcert/signPri.key";
char[] password = "looking100!".toCharArray();
Security.addProvider(new BouncyCastleProvider());
PrivateKey myPrivateKey1 = NPKILoader.loadPrivateKey(priPath, password, pubKey.getAlgorithm());
// 1. 인증서 로드
CertificateFactory cf = CertificateFactory.getInstance("X.509");
try (FileInputStream fis = new FileInputStream(certPath)) {
X509Certificate cert = (X509Certificate) cf.generateCertificate(fis);
X509Certificate cert = null;
//인증서 묶음인 경우(PKCS#7 경우)
Collection<? extends Certificate> certs = cf.generateCertificates(fis);
for (Certificate c : certs) {
if (c instanceof X509Certificate) {
cert = (X509Certificate) c;
System.out.println("Subject: " + cert.getSubjectDN());
}
}
// 2. 공개키 추출
ECPublicKey publicKey = (ECPublicKey) cert.getPublicKey();
if (cert == null) {
throw new IllegalStateException("인증서를 찾을 수 없습니다.");
}
// 3. Nimbus ECKey로 변환
this.receiverPublicJWK = new ECKey.Builder(Curve.P_256, publicKey)
// 공개키 저장 및 알고리즘 확인
this.pubKey = cert.getPublicKey();
System.out.println(pubKey.getAlgorithm()); // "RSA" 또는 "EC"
KeyStore ks = KeyStore.getInstance("PKCS12");
if ("RSA".equals(pubKey.getAlgorithm())) {
//PrivateKey myPrivateKey = (PrivateKey) ks.getKey(keyAlias, keyPass.toCharArray());
//PrivateKey myPrivateKey = (PrivateKey) ks.getKey("mykey", "key1234".toCharArray());
PrivateKey myPrivateKey = NPKILoader.loadPrivateKey(priPath, password, pubKey.getAlgorithm());
System.out.println("PrivateKey Alg: " + myPrivateKey.getAlgorithm());
RSAPublicKey publicKey = (RSAPublicKey) cert.getPublicKey();
this.receiverPublicRSAKey = new RSAKey.Builder(publicKey)
.privateKey(myPrivateKey) //개인키 포함
.keyUse(KeyUse.ENCRYPTION)
//.keyID("receiver-key")
.keyID(cert.getSerialNumber().toString()) // 인증서 시리얼번호를 keyID로 사용
.keyID(cert.getSerialNumber().toString())
.build();
} else if ("EC".equals(pubKey.getAlgorithm())) {
ECPublicKey ecPub = (ECPublicKey) cert.getPublicKey();
this.receiverPublicJWK = new ECKey.Builder(Curve.P_256, ecPub)
.keyUse(KeyUse.ENCRYPTION)
.keyID(cert.getSerialNumber().toString())
.build();
} else {
throw new IllegalStateException("지원하지 않는 공개키 알고리즘: " + pubKey.getAlgorithm());
}
} catch (FileNotFoundException e) {
System.err.println("인증서 파일을 찾을 수 없습니다: " + e.getMessage());
} catch (CertificateException e) {
System.err.println("인증서 파싱 오류: " + e.getMessage());
} catch (IOException e) {
System.err.println("파일 읽기 오류: " + e.getMessage());
} catch (ClassCastException e) {
System.err.println("X509Certificate 형변환 오류: " + e.getMessage());
}
}
// 암호화 메서드
public String encrypt(String jsonPayload) throws Exception {
if (receiverPublicJWK == null) {
if (receiverPublicJWK == null && receiverPublicRSAKey == null ) {
throw new IllegalStateException("상대방 공개키가 초기화되지 않았습니다.");
}
if (receiverPublicJWK != null && KeyType.EC.equals(receiverPublicJWK.getKeyType())) {
JWEHeader header = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES_A256KW, EncryptionMethod.A256GCM)
.contentType("application/json")
.keyID(receiverPublicJWK.getKeyID())
.build();
JWEObject jweObject = new JWEObject(header, new Payload(jsonPayload));
this.jweObject = new JWEObject(header, new Payload(jsonPayload));
JWEEncrypter encrypter = new ECDHEncrypter(receiverPublicJWK);
jweObject.encrypt(encrypter);
} else if (receiverPublicRSAKey != null && "RSA".equals(receiverPublicRSAKey.getKeyType().getValue())) {
JWEHeader header = new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM)
.contentType("application/json")
.keyID(receiverPublicRSAKey.getKeyID())
.build();
this.jweObject = new JWEObject(header, new Payload(jsonPayload));
JWEEncrypter encrypter = new RSAEncrypter(receiverPublicRSAKey);
jweObject.encrypt(encrypter);
}
return jweObject.serialize();
}
// 복호화 메서드(상대방이 나한테 보낸것)
public String decrypt(String jweString) throws Exception {
if (myECKey == null) {
throw new IllegalStateException("내 개인키가 초기화되지 않았습니다.");
if (myECKey == null && receiverPublicRSAKey == null) {
throw new IllegalStateException("내 개인키 또는 RSA 키가 초기화되지 않았습니다.");
}
JWEObject decryptedJWE = JWEObject.parse(jweString);
if (myECKey != null) {
// EC 키로 복호화
JWEDecrypter decrypter = new ECDHDecrypter(myECKey);
decryptedJWE.decrypt(decrypter);
} else if (receiverPublicRSAKey != null && receiverPublicRSAKey.isPrivate()) {
// RSA 키로 복호화
JWEDecrypter decrypter = new RSADecrypter(receiverPublicRSAKey.toPrivateKey());
decryptedJWE.decrypt(decrypter);
} else {
throw new IllegalStateException("복호화에 필요한 키가 준비되지 않았습니다. (EC 개인키 또는 RSA 개인키)");
}
return decryptedJWE.getPayload().toString();
}
@@ -0,0 +1,60 @@
package com.kjbank.encrypt.exchange.crypto;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.*;
import javax.crypto.spec.PBEKeySpec;
public class NPKILoader {
// 1) Provider 등록 (예: KISA SEED JCE, KSign 등)
static {
// 예시: Security.addProvider(new com.kisa.seed.KISASeedJCE());
// 또는 Security.addProvider(new com.ksign.security.provider.KsignProvider());
// 실제 사용하는 라이브러리 명으로 교체
}
public static X509Certificate loadCert(String certDerPath) throws Exception {
try (FileInputStream fis = new FileInputStream(certDerPath)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(fis);
}
}
public static PrivateKey loadPrivateKey(String priDerPath, char[] password, String keyAlg) throws Exception {
// 암호화된 PKCS#8 (DER) 읽기
byte[] enc = Files.readAllBytes(Paths.get(priDerPath));
EncryptedPrivateKeyInfo encInfo = new EncryptedPrivateKeyInfo(enc);
// 알고리즘 확인 (OID → 알고리즘 이름 매핑 필요)
String algName = encInfo.getAlgName(); // 일부 Provider는 빈 문자열을 반환할 수 있음
AlgorithmParameters params = encInfo.getAlgParameters();
// 국내 OID 매핑: 1.2.410.200004.1.15 → PBEWithSHA1AndSEED (Provider에 따라 이름 다를 수 있음)
if (algName == null || algName.isEmpty()) {
String oid = encInfo.getAlgParameters().getAlgorithm(); // 일부 Provider는 여기서도 OID를 줍니다
// 필요 시 강제 설정
algName = "PBEWithSHA1AndSEED"; // 사용 중인 Provider 문서의 이름으로 맞추세요
}
// PBE 키 생성
SecretKeyFactory skf = SecretKeyFactory.getInstance(algName);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKey pbeKey = skf.generateSecret(pbeKeySpec);
// 복호화
Cipher cipher = Cipher.getInstance(algName);
cipher.init(Cipher.DECRYPT_MODE, pbeKey, params);
byte[] pkcs8 = cipher.doFinal(encInfo.getEncryptedData());
// PKCS#8 → PrivateKey
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8);
KeyFactory kf = KeyFactory.getInstance(keyAlg); // "RSA" 또는 "EC"
return kf.generatePrivate(keySpec);
}
}
@@ -0,0 +1,54 @@
package com.kjbank.encrypt.exchange.crypto;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jose.util.StandardCharset;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import com.nimbusds.jose.crypto.RSAEncrypter;
import java.security.interfaces.RSAPublicKey;
public class RSAOAEP256Cipher {
// 암호화: 상대방 공개 인증서(RSA)로 암호화
public static String encrypt(byte[] plaintext, X509Certificate recipientCert, String kidOptional) throws Exception {
// 인증서에서 RSA 공개키 추출
if (!"RSA".equalsIgnoreCase(recipientCert.getPublicKey().getAlgorithm())) {
throw new IllegalArgumentException("Recipient certificate public key is not RSA");
}
RSAPublicKey recipientPublicKey = (RSAPublicKey) recipientCert.getPublicKey();
JWEHeader header = new JWEHeader.Builder(
JWEAlgorithm.RSA_OAEP_256,
EncryptionMethod.A256GCM
)
.contentType("application/octet-stream")
.keyID(kidOptional)
.build();
JWEObject jweObject = new JWEObject(header, new Payload(plaintext));
JWEEncrypter encrypter = new RSAEncrypter(recipientPublicKey);
jweObject.encrypt(encrypter);
return jweObject.serialize();
}
// 복호화: 내 RSA 개인키로 복호화
public static byte[] decrypt(String jweCompact, PrivateKey myPrivateKey) throws Exception {
JWEObject jweObject = JWEObject.parse(jweCompact);
JWEDecrypter decrypter = new RSADecrypter(myPrivateKey);
jweObject.decrypt(decrypter);
return jweObject.getPayload().toBytes();
}
// 편의 함수
public static String encryptText(String text, X509Certificate cert, String kidOptional) throws Exception {
return encrypt(text.getBytes(StandardCharset.UTF_8), cert, kidOptional);
}
public static String decryptToText(String jweCompact, PrivateKey myPrivateKey) throws Exception {
return new String(decrypt(jweCompact, myPrivateKey), StandardCharset.UTF_8);
}
}