gpki 분리
parent
6e9f9a32fa
commit
f609575860
@ -1,421 +0,0 @@
|
|||||||
package cokr.xit.interfaces.disabledparking.service.bean;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.IntSupplier;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
|
|
||||||
import com.gpki.gpkiapi_jni;
|
|
||||||
import com.gpki.gpkiapi.GpkiApi;
|
|
||||||
import com.gpki.gpkiapi.cert.X509Certificate;
|
|
||||||
import com.gpki.gpkiapi.exception.GpkiApiException;
|
|
||||||
import com.gpki.gpkiapi.storage.Disk;
|
|
||||||
|
|
||||||
import cokr.xit.foundation.AbstractComponent;
|
|
||||||
import cokr.xit.foundation.data.JSON;
|
|
||||||
|
|
||||||
/**GPKI(행정전자서명) API 유틸리티. 올바로 동작하려면
|
|
||||||
* <ul><li>{@link Config intf-conf/gpki.conf}를 설정해야 한다.</li>
|
|
||||||
* <li>libgpkiapi_jni_1.5.jar를 lib 디렉토리에 두어야 한다.</li>
|
|
||||||
* </ul>
|
|
||||||
* @author mjkhan
|
|
||||||
*/
|
|
||||||
public class GPKI extends AbstractComponent {
|
|
||||||
private static final Config conf = Config.get();
|
|
||||||
private static final HashMap<String, X509Certificate> serverCerts = new HashMap<>();
|
|
||||||
private static byte[]
|
|
||||||
envCert,
|
|
||||||
envKey,
|
|
||||||
sigCert,
|
|
||||||
sigKey;
|
|
||||||
|
|
||||||
private gpkiapi_jni gpki;
|
|
||||||
|
|
||||||
/**주어진 문자열을 지정한 아이디의 서버인증서로 암호화하여 반환한다.
|
|
||||||
* @param serverID 서버인증서 아이디
|
|
||||||
* @param data 문자열
|
|
||||||
* @return 지정한 아이디의 서버인증서로 암호화한 문자열
|
|
||||||
*/
|
|
||||||
public String encrypt(String serverID, String data) {
|
|
||||||
if (isEmpty(data)) return "";
|
|
||||||
|
|
||||||
try {
|
|
||||||
start();
|
|
||||||
|
|
||||||
byte[] encrypted = encrypt(serverID, data.getBytes(conf.getCharset()));
|
|
||||||
return encode(sign(encrypted));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw runtimeException(e);
|
|
||||||
} finally {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**주어진 암호화된 문자열을 복호화하여 반환한다.
|
|
||||||
* @param encrypted 암호화된 문자열
|
|
||||||
* @return 복호화한 문자열
|
|
||||||
*/
|
|
||||||
public String decrypt(String encrypted) {
|
|
||||||
if (isEmpty(encrypted)) return "";
|
|
||||||
|
|
||||||
try {
|
|
||||||
start();
|
|
||||||
|
|
||||||
byte[] validated = validate(decode(encrypted));
|
|
||||||
return new String(decrypt(validated), conf.getCharset());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw runtimeException(e);
|
|
||||||
} finally {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void call(gpkiapi_jni gpki, IntSupplier task, String msg) throws Exception {
|
|
||||||
int result = task.getAsInt();
|
|
||||||
if (result == 0) return;
|
|
||||||
|
|
||||||
String message = isEmpty(msg) ? "" : msg + ": ";
|
|
||||||
message += gpki != null ? gpki.sDetailErrorString : "errorCode = " + result;
|
|
||||||
|
|
||||||
throw new Exception(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void init() {
|
|
||||||
if (!serverCerts.isEmpty()) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
GpkiApi.init(conf.license);
|
|
||||||
gpkiapi_jni gpki = getGpki();
|
|
||||||
call(gpki, () -> gpki.API_GetInfo(), null);
|
|
||||||
|
|
||||||
for (String serverID: conf.getTargetServers())
|
|
||||||
load(gpki, serverID);
|
|
||||||
|
|
||||||
envCert = Disk.readCert(conf.getCertFile("env")).getCert();
|
|
||||||
envKey = Disk.readPriKey(conf.getPrivateKeyFile("env"), conf.getPrivateKeyPassword("env")).getKey();
|
|
||||||
|
|
||||||
sigCert = Disk.readCert(conf.getCertFile("sig")).getCert();
|
|
||||||
sigKey = Disk.readPriKey(conf.getPrivateKeyFile("sig"), conf.getPrivateKeyPassword("sig")).getKey();
|
|
||||||
|
|
||||||
finish(gpki);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw runtimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static gpkiapi_jni getGpki() throws Exception{
|
|
||||||
gpkiapi_jni gpki = new gpkiapi_jni();
|
|
||||||
call(gpki, () -> gpki.API_Init(conf.license), null);
|
|
||||||
return gpki;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void finish(gpkiapi_jni gpki) {
|
|
||||||
if (gpki == null) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
call(gpki, () -> gpki.API_Finish(), null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw runtimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void load(gpkiapi_jni gpki, String serverID) throws Exception {
|
|
||||||
X509Certificate cert = null;
|
|
||||||
String certFile = conf.certDir + File.separator + serverID + ".cer";
|
|
||||||
try {
|
|
||||||
cert = Disk.readCert(certFile);
|
|
||||||
} catch (GpkiApiException e) {
|
|
||||||
cert = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cert == null && !isEmpty(conf.ldapUrl)) {
|
|
||||||
String uri = serverID.charAt(3) > '9' ?
|
|
||||||
",ou=Group of Server,o=Public of Korea,c=KR" :
|
|
||||||
",ou=Group of Server,o=Government of Korea,c=KR";
|
|
||||||
|
|
||||||
call(gpki,
|
|
||||||
() -> gpki.LDAP_GetAnyDataByURL("userCertificate;binary", conf.ldapUrl + serverID + uri),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
cert = new X509Certificate(gpki.baReturnArray);
|
|
||||||
Disk.writeCert(certFile, cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cert != null)
|
|
||||||
serverCerts.put(serverID, cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static X509Certificate getCert(String serverID) {
|
|
||||||
X509Certificate cert = serverCerts.get(serverID);
|
|
||||||
if (cert == null)
|
|
||||||
throw new RuntimeException(String.format("%s not found for %s", X509Certificate.class.getSimpleName(), serverID));
|
|
||||||
return cert;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T call(IntSupplier work, Supplier<T> result, String message) {
|
|
||||||
boolean finish = start();
|
|
||||||
|
|
||||||
try {
|
|
||||||
call(gpki, work, message);
|
|
||||||
return result.get();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw runtimeException(e);
|
|
||||||
} finally {
|
|
||||||
if (finish)
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] encrypt(String serverID, byte[] data) {
|
|
||||||
return call(
|
|
||||||
() -> {
|
|
||||||
X509Certificate cert = getCert(serverID);
|
|
||||||
return gpki.CMS_MakeEnvelopedData(cert.getCert(), data, gpkiapi_jni.SYM_ALG_NEAT_CBC);
|
|
||||||
},
|
|
||||||
() -> gpki.baReturnArray,
|
|
||||||
"Encryption failed"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] decrypt(byte[] encrypted) {
|
|
||||||
return call(
|
|
||||||
() -> gpki.CMS_ProcessEnvelopedData(envCert, envKey, encrypted),
|
|
||||||
() -> gpki.baReturnArray,
|
|
||||||
"Decryption failed"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] sign(byte[] data) {
|
|
||||||
return call(
|
|
||||||
() -> gpki.CMS_MakeSignedData(sigCert, sigKey, data, null),
|
|
||||||
() -> gpki.baReturnArray,
|
|
||||||
"Fail to sign message"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] validate(byte[] signed) {
|
|
||||||
return call(
|
|
||||||
() -> gpki.CMS_ProcessSignedData(signed),
|
|
||||||
() -> gpki.baData,
|
|
||||||
"Validation failed"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String encode(byte[] data) {
|
|
||||||
return call(
|
|
||||||
() -> gpki.BASE64_Encode(data),
|
|
||||||
() -> gpki.sReturnString,
|
|
||||||
"Encoding failed"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] decode(String encoded) {
|
|
||||||
return call(
|
|
||||||
() -> gpki.BASE64_Decode(encoded),
|
|
||||||
() -> gpki.baReturnArray,
|
|
||||||
"Decoding failed"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean start() {
|
|
||||||
if (gpki != null) return false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
init();
|
|
||||||
gpki = getGpki();
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw runtimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finish() {
|
|
||||||
if (gpki == null) return;
|
|
||||||
|
|
||||||
finish(gpki);
|
|
||||||
gpki = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**GPKI 설정파일(intf-conf/gpki.conf)을 로드한다.
|
|
||||||
* 설정 항목은 JSON 포맷으로 된 다음 내용을 적재한다.
|
|
||||||
* <pre><code> {
|
|
||||||
* "license": "이용기관 GPKI API 라이센스 디렉토리",
|
|
||||||
* "charset": "문자셋",
|
|
||||||
* "server": {
|
|
||||||
* "local": "이용기관 서버 CN",
|
|
||||||
* "targets": "대상기관 서버인증서 아이디, 여러 개일 경우 컴마(,)로 구분"
|
|
||||||
* },
|
|
||||||
* "ldapUrl": "대상기관 인증서 다운로드를 위한 LDAP URL",
|
|
||||||
* "certDir": "서버 인증서, 키 저장 디렉토리",
|
|
||||||
* "env": {
|
|
||||||
* "certFile": "이용기관 서버 인증서 파일",
|
|
||||||
* "privateKeyFile": "이용기관 서버 인증서 키 파일",
|
|
||||||
* "privateKeyPassword": "이용기관 서버 인증서 비밀번호"
|
|
||||||
* },
|
|
||||||
* "sig": {
|
|
||||||
* "certFile": "이용기관 서버 전자서명 파일",
|
|
||||||
* "privateKeyFile": "이용기관 서버 전자서명 키 파일",
|
|
||||||
* "privateKeyPassword": "이용기관 서버 전자서명 비밀번호"
|
|
||||||
* }
|
|
||||||
* }</code></pre>
|
|
||||||
* @author mjkhan
|
|
||||||
*/
|
|
||||||
public static class Config extends AbstractComponent {
|
|
||||||
private static Config conf;
|
|
||||||
|
|
||||||
public static Config get() {
|
|
||||||
if (conf == null)
|
|
||||||
try {
|
|
||||||
ClassPathResource res = new ClassPathResource("intf-conf/gpki.conf");
|
|
||||||
conf = new JSON().parse(res.getInputStream(), Config.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw runtimeException(e);
|
|
||||||
}
|
|
||||||
return conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String
|
|
||||||
charset,
|
|
||||||
license,
|
|
||||||
ldapUrl,
|
|
||||||
certDir;
|
|
||||||
private Map<String, String>
|
|
||||||
server,
|
|
||||||
env,
|
|
||||||
sig;
|
|
||||||
|
|
||||||
/**charset을(를) 반환한다.
|
|
||||||
* @return charset
|
|
||||||
*/
|
|
||||||
public String getCharset() {
|
|
||||||
return charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**charset을(를) 설정한다.
|
|
||||||
* @param charset charset
|
|
||||||
*/
|
|
||||||
public void setCharset(String charset) {
|
|
||||||
this.charset = charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**license을(를) 반환한다.
|
|
||||||
* @return license
|
|
||||||
*/
|
|
||||||
public String getLicense() {
|
|
||||||
return license;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**license을(를) 설정한다.
|
|
||||||
* @param license license
|
|
||||||
*/
|
|
||||||
public void setLicense(String license) {
|
|
||||||
this.license = license;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**ldapUrl을(를) 반환한다.
|
|
||||||
* @return ldapUrl
|
|
||||||
*/
|
|
||||||
public String getLdapUrl() {
|
|
||||||
return ldapUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**ldapUrl을(를) 설정한다.
|
|
||||||
* @param ldapUrl ldapUrl
|
|
||||||
*/
|
|
||||||
public void setLdapUrl(String ldapUrl) {
|
|
||||||
this.ldapUrl = ldapUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**certDir을(를) 반환한다.
|
|
||||||
* @return certDir
|
|
||||||
*/
|
|
||||||
public String getCertDir() {
|
|
||||||
return certDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**certDir을(를) 설정한다.
|
|
||||||
* @param certDir certDir
|
|
||||||
*/
|
|
||||||
public void setCertDir(String certDir) {
|
|
||||||
this.certDir = certDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**server을(를) 반환한다.
|
|
||||||
* @return server
|
|
||||||
*/
|
|
||||||
public Map<String, String> getServer() {
|
|
||||||
return ifEmpty(server, Collections::emptyMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLocalServer() {
|
|
||||||
return getServer().get("local");
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getTargetServers() {
|
|
||||||
String targets = ifEmpty(getServer().get("targets"), "");
|
|
||||||
return Stream.of(targets.split(",")).map(String::trim).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**server을(를) 설정한다.
|
|
||||||
* @param server server
|
|
||||||
*/
|
|
||||||
public void setServer(Map<String, String> server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**env을(를) 반환한다.
|
|
||||||
* @return env
|
|
||||||
*/
|
|
||||||
public Map<String, String> getEnv() {
|
|
||||||
return ifEmpty(env, Collections::emptyMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**env을(를) 설정한다.
|
|
||||||
* @param env env
|
|
||||||
*/
|
|
||||||
public void setEnv(Map<String, String> env) {
|
|
||||||
this.env = env;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**sig을(를) 반환한다.
|
|
||||||
* @return sig
|
|
||||||
*/
|
|
||||||
public Map<String, String> getSig() {
|
|
||||||
return ifEmpty(sig, Collections::emptyMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**sig을(를) 설정한다.
|
|
||||||
* @param sig sig
|
|
||||||
*/
|
|
||||||
public void setSig(Map<String, String> sig) {
|
|
||||||
this.sig = sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, String> getCredentials(String type) {
|
|
||||||
switch (type) {
|
|
||||||
case "env": return getEnv();
|
|
||||||
case "sig": return getSig();
|
|
||||||
default: throw new IllegalArgumentException(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCertFile(String type) {
|
|
||||||
return certDir + File.separator + getCredentials(type).get("certFile");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPrivateKeyFile(String type) {
|
|
||||||
return certDir + File.separator + getCredentials(type).get("privateKeyFile");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPrivateKeyPassword(String type) {
|
|
||||||
return getCredentials(type).get("privateKeyPassword");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue