RSA 암호화
공개키 암호시스템 중 한개, 암호화 뿐만 아니라 전자서명도 가능한 최초의 알고리즘으로 알려진다.
RSA는 두개의 키 를 사용한다. 공개키 와 개인키 를 사용하는데 공개키 는 평문을 암호화 하는데 사용 하고
개인키 는 암호화한 데이터를 복호화할때 사용 한다. 이를 비대칭키라고 하며 누구나 어떤 메시지를 암호화 할수 있지만
암호화한 데이터는 개인키를 가지고 있는 사람만 복호화 할 수 있다.
일반적으로 공개키는 n 과 e 값으로 구성되어 있습니다.
n 값은 Modulus 이며 e 값은 Public exponent 값입니다.
개인키는 기본적으로 n , e , d, p, q, exponent1, exponent2, coefficient 값을 갖습니다. 여기서 가장 필요한 값은 d 입니다.
개인키의 각 값은 공개키의 데이터 값을 포함합니다.
여기서는 암호화만 진행할 예정이다.
RsaUtils.java
package com.angular.web.utils; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; import javax.crypto.Cipher; import org.springframework.stereotype.Component; import com.angular.web.utils.Funcs; @Component public class RsaUtils { //공개키 private PublicKey publicKey; //개인키 private PrivateKey privateKey; //공개키 및 개인키의 N 값 private String publicKeyModulus; //공개키 및 개인키의 E 값 private String publicKeyExponent; KeyPairGenerator gen; public RsaUtils() { try { //1024 RSA 키쌍을 생성 gen = KeyPairGenerator.getInstance("RSA"); gen.initialize(1024); KeyPair keys = gen.genKeyPair(); publicKey = keys.getPublic(); privateKey = keys.getPrivate(); //만들어진 공개키의 N(Modulus) 값과 E(Exponent)값을 문자열로 가져온다. KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKeySpec publicSpec = keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class); publicKeyModulus = publicSpec.getModulus().toString(16); publicKeyExponent = publicSpec.getPublicExponent().toString(16); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { e.printStackTrace(); publicKey = null; privateKey = null; publicKeyModulus = null; publicKeyExponent = null; } } public PublicKey getPublicKey() { return publicKey; } public void setPublicKey(PublicKey publicKey) { this.publicKey = publicKey; } public PrivateKey getPrivateKey() { return privateKey; } public void setPrivateKey(PrivateKey privateKey) { this.privateKey = privateKey; } public String getPublicKeyModulus() { return publicKeyModulus; } public void setPublicKeyModulus(String publicKeyModulus) { this.publicKeyModulus = publicKeyModulus; } public String getPublicKeyExponent() { return publicKeyExponent; } public void setPublicKeyExponent(String publicKeyExponent) { this.publicKeyExponent = publicKeyExponent; } public KeyPairGenerator getGen() { return gen; } public void setGen(KeyPairGenerator gen) { this.gen = gen; } //암호화 public String decrypt(String secureInfo) { String secureDecryptInfo = ""; try{ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); /** * 암호화 된 값은 byte 배열이다. * 이를 문자열 폼으로 전송하기 위해 16진 문자열(hex)로 변경한다. * 16진 문자열로 변경하기 위해 funcs이라는 utils 이용 * 서버에서도 값을 받을 때 hex 문자열을 받아서 다시 byte 배열로 바꾼 뒤에 복호화 과정을 수행한다. * 아래의 Funcs는 16진수 변환을 위한 utils 파일, 아래에서 첨부할꺼다 */ byte[] encryptedBytes = Funcs .hexToByteArray(secureInfo); if( encryptedBytes == null) return null; cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); secureDecryptInfo = new String(decryptedBytes, "utf-8"); // 문자 인코딩 주의. return secureDecryptInfo; }catch(Exception e) { return null; } } }
Funcs.java 에서 사용할 pom.xml에 dependency 추가
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.3</version> </dependency>
Funcs.java
package com.angular.web.utils; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; public class Funcs { private static final char[] HEXCHAR = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'd', 'd', 'e', 'f' }; public static String getkey(HashMap<String, String> m, String v){ for(String o: m.keySet()) { if(m.get(o).equals(v)) { return o; } } return null; } public static String encodingURL(String url){ try { return URLEncoder.encode(url, "UTF-8").replaceAll("\\+", "%20"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block //에러시 주요 문자만 인코딩 return url.replaceAll(" ", "%20").replaceAll("//", "%2F").replaceAll("+", "%2B"); } } public static String getMD5Str(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bf = md.digest(input.getBytes("UTF-8")); StringBuilder stbf = new StringBuilder(); for( int i = 0; i < bf.length ; i++) { stbf.append(HEXCHAR[((bf[i]>>4) & 0x0f)]); stbf.append(HEXCHAR[(bf[i] & 0x0f)]); } return stbf.toString(); } catch (NoSuchAlgorithmException e) { } catch (UnsupportedEncodingException e) { } return null; } public static String getSha512Str( String input) { try { MessageDigest md = MessageDigest.getInstance("SHA-512"); byte[] bf = md.digest(input.getBytes()); StringBuilder stbf = new StringBuilder(); for( int i = 0; i < bf.length ; i++) { stbf.append(HEXCHAR[((bf[i]>>4) & 0x0f)]); stbf.append(HEXCHAR[(bf[i] & 0x0f)]); } return stbf.toString(); } catch (NoSuchAlgorithmException e) { } return null; } public static byte[] hexToByteArray(String input) { if (input == null || input.length() % 2 != 0) { return null; } byte[] bytes = new byte[input.length() >> 1]; for (int i = 0; i < input.length(); i += 2) { byte value = (byte)Integer.parseInt(input.substring(i, i + 2), 16); bytes[(i>>1)] = value; } return bytes; } public static final void convtCommaStr( String str , List<String> lststr) { if( str == null || str.length() == 0) return; char[] buf = str.toCharArray(); int pt, spt, cnt, stat,len; pt = spt = cnt = 0; len = buf.length; char nextfind; try { while( pt < len) { if( buf[pt] == '"') { nextfind = '"'; stat = 2; pt++; spt = pt; } else { if( buf[pt] == ',') { lststr.add(""); pt++; if( pt == len) { lststr.add(""); } spt = pt; continue; } else { nextfind = ','; stat = 1; spt = pt; cnt++; pt++; } } while (pt < len ) { if( buf[pt] != nextfind) { pt++; cnt++; continue; } if( stat == 1) { lststr.add( new String(buf, spt, cnt)); pt++; if(pt == len) { lststr.add(""); } cnt = 0; break; } if(stat == 2) { pt++; if( buf[pt] != nextfind) { lststr.add( new String(buf, spt, cnt)); cnt = 0; pt++; if( pt == len) { lststr.add(""); } break; } else { int i = pt; while( i < len) { buf[i-1] = buf[i]; i++; } len--; cnt++; } } } } if( cnt > 0) lststr.add( new String( buf, spt, cnt)); } catch( Exception e) { if( cnt > 0) lststr.add( new String(buf, spt, cnt)); } } public static final List<String> convtCommaStr( String str ) { if( str == null || str.length() == 0) return null; char[] buf = str.toCharArray(); int pt, spt, cnt, stat,len; pt = spt = cnt = 0; len = buf.length; char nextfind; List<String> lststr = new ArrayList<String>(); try { while( pt < len) { if( buf[pt] == '"') { nextfind = '"'; stat = 2; pt++; spt = pt; } else { if( buf[pt] == ',') { lststr.add(""); pt++; if( pt == len) { lststr.add(""); } spt = pt; continue; } else { nextfind = ','; stat = 1; spt = pt; cnt++; pt++; } } while (pt < len ) { if( buf[pt] != nextfind) { pt++; cnt++; continue; } if( stat == 1) { lststr.add( new String(buf, spt, cnt)); pt++; if(pt == len) { lststr.add(""); } cnt = 0; break; } if(stat == 2) { pt++; if( buf[pt] != nextfind) { lststr.add( new String(buf, spt, cnt)); cnt = 0; pt++; if( pt == len) { lststr.add(""); } break; } else { int i = pt; while( i < len) { buf[i-1] = buf[i]; i++; } len--; cnt++; } } } } if( cnt > 0) lststr.add( new String( buf, spt, cnt)); } catch( Exception e) { if( cnt > 0) lststr.add( new String(buf, spt, cnt)); } return lststr; } public static final String[] commaStrToStrs( String str , int count) { if( str == null || str.length() == 0) return null; String[] arstrs = new String[count]; int numidx = 0; char[] buf = str.toCharArray(); int pt, spt, cnt, stat,len; pt = spt = cnt = 0; len = buf.length; char nextfind; try { while( pt < len) { if( buf[pt] == '"') { nextfind = '"'; stat = 2; pt++; spt = pt; } else { if( buf[pt] == ',') { if( numidx == count) return null; arstrs[numidx] = ""; numidx++; pt++; if( pt == len) { if( numidx == count) return null; arstrs[numidx] = ""; numidx++; } spt = pt; continue; } else { nextfind = ','; stat = 1; spt = pt; cnt++; pt++; } } while (pt < len ) { if( buf[pt] != nextfind) { pt++; cnt++; continue; } if( stat == 1) { if( numidx == count) return null; arstrs[numidx] = new String(buf, spt, cnt); numidx++; pt++; if(pt == len) { if( numidx == count) return null; arstrs[numidx] = ""; numidx++; } cnt = 0; break; } if(stat == 2) { pt++; if( buf[pt] != nextfind) { if( numidx == count) return null; arstrs[numidx] = new String(buf, spt, cnt); numidx++; pt++; if( pt == len) { if( numidx == count) return null; arstrs[numidx] = ""; numidx++; } cnt = 0; break; } else { int i = pt; while( i < len) { buf[i-1] = buf[i]; i++; } len--; cnt++; } } } } if( cnt > 0) { if( numidx == count) return null; arstrs[numidx] = new String( buf, spt, cnt); numidx++; } } catch( Exception e) { if( cnt > 0) { if( numidx == count) return null; arstrs[numidx] = new String( buf, spt, cnt); numidx++; } } if( count == numidx ) return arstrs; else return null; } public static Map ConverObjectToMap(Object obj){ try { //Field[] fields = obj.getClass().getFields(); //private field는 나오지 않음. Field[] fields = obj.getClass().getDeclaredFields(); Map resultMap = new HashMap(); for(int i=0; i<=fields.length-1;i++){ fields[i].setAccessible(true); resultMap.put(fields[i].getName(), fields[i].get(obj)); } return resultMap; } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } public static String toJson(Object obj){ ObjectMapper mapper = new ObjectMapper(); String json = ""; try { json = mapper.writeValueAsString(obj); } catch (JsonProcessingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return json; } public static CloseableHttpClient createAcceptSelfSignedCertificateClient() throws Exception { SSLContext sslContext = SSLContextBuilder .create() .loadTrustMaterial(new TrustSelfSignedStrategy()) .build(); sslContext.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom()); HostnameVerifier allowAllHosts = new HostnameVerifier(){ public boolean verify(String hostname, SSLSession session) { return true; } }; SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext, allowAllHosts); return HttpClients .custom() .setSSLSocketFactory(connectionFactory) .build(); } private static class DefaultTrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} @Override public X509Certificate[] getAcceptedIssuers() { return null; } } }