본문으로 바로가기

JAVA에서 개인키와 공개키를 발급하고 C# 에서 공개키를 이용해 암호화 / JAVA에서 복호화 하는 과정을 소개 합니다.

C# 코드
using System;
using System.Net;
using System.Text;
using System.IO;
using System.Web.Script.Serialization;
using System.Security.Cryptography;

public partial class NcoreEPWorkReport : System.Web.UI.Page
{
private String ResponseString = string.Empty;
private Dictionary<String, String> ResponseJson = null;

   protected void Page_Load(object sender, EventArgs e)
{
        ResponseJson = new Dictionary<string, string>();
        String service = "http://localhost:8000//rest/login/rasKeyData.json";
        this.GetGet(service, null, null);

        Dictionary<String, Object> _postData = new Dictionary<string, object>();
        string publicKeyText = string.Empty;
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
        {
            RSAParameters publicKey = new RSAParameters();
            publicKey.Modulus = Convert.FromBase64String(this.ResponseJson["modulusString"]);    //JAVA에서 생성한 값
            publicKey.Exponent = Convert.FromBase64String(this.ResponseJson["exponentString"]); //JAVA에서 생성한 값
            rsa.ImportParameters(publicKey);
            publicKeyText = rsa.ToXmlString(false);
        }



        _postData.Add("securedUser", RSAEncrypt("아이디", publicKeyText));
        _postData.Add("securedUserPwd", RSAEncrypt("패스워드", publicKeyText));
        //_postData.Add("securedUser", 
        byte[] data = Encoding.UTF8.GetBytes(String.Join("&", _postData.Select(item => item.Key + "=" + item.Value)));

        service = "http://localhost:8000//rest/login/LoginService.do";
        this.GetPost(service, data, null);
 }

 private void GetGet(string service, byte[] data, CookieCollection cookie = null)
 {
     HttpWebRequest HttpWReq = (HttpWebRequest)WebRequest.Create(service);
     HttpWReq.Method = "GET";
     HttpWReq.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";

     using (HttpWebResponse httpResponse = (HttpWebResponse)HttpWReq.GetResponse())
     {
         if (httpResponse.StatusCode == HttpStatusCode.OK)
         {
             this.Cookies = httpResponse.Cookies;
             Encoding encode;
             if (httpResponse.CharacterSet.ToLower() == "utf-8") { encode = Encoding.UTF8; }
             else { encode = Encoding.Default; }
             using (var streamReader = new StreamReader(httpResponse.GetResponseStream(), encode))
             {
                 this.ResponseString = streamReader.ReadToEnd();    //JAVA에서 전달한 값
                 this.ResponseJson = js.Deserialize<Dictionary<String, String>>(ResponseString);
             }
         }
     }
 }

 private void GetPost(string service, byte[] data, CookieCollection cookie = null)
 {
     HttpWebRequest HttpWReq = (HttpWebRequest)WebRequest.Create(service);
     HttpWReq.Method = "POST";
     HttpWReq.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
     HttpWReq.ContentLength = data.Length;
     if (cookie != null)
     {
         if (HttpWReq.CookieContainer == null)
         {
             HttpWReq.CookieContainer = new CookieContainer();
         }
         HttpWReq.CookieContainer.Add(cookie);
     }
     deviceId = this.ResponseJson["deviceId"];
     HttpWReq.Headers["deviceId"] = deviceId;
     using (var stream = HttpWReq.GetRequestStream())
     {
         stream.Write(data, 0, data.Length);
     }
     using (HttpWebResponse httpResponse = (HttpWebResponse)HttpWReq.GetResponse())
     {
         if (httpResponse.StatusCode == HttpStatusCode.OK)
         {
             this.Cookies = httpResponse.Cookies;
             Encoding encode;
             if (httpResponse.CharacterSet.ToLower() == "utf-8") { encode = Encoding.UTF8; }
             else { encode = Encoding.Default; }
             using (var streamReader = new StreamReader(httpResponse.GetResponseStream(), encode))
             {
                 this.ResponseString1 = streamReader.ReadToEnd();
             }
         }
     }
 }

 // RSA 암호화
 public string RSAEncrypt(string getValue, string pubKey)
 {
     using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
     {
         rsa.FromXmlString(pubKey);
         //암호화할 문자열을 UFT8인코딩
         byte[] inbuf = (new UTF8Encoding()).GetBytes(getValue);

         byte[] bytText = new byte[getValue.Length];

         for (int i = 0; i < getValue.Length; i++)
     {
         bytText[i] = Convert.ToByte(getValue[i]);
     }
         byte[] bytEncText = rsa.Encrypt(bytText, false);
         return BitConverter.ToString(bytEncText).Replace("-", string.Empty); ;
     }            
 }

}

JAVA 코드
public class CryptoUtil {
private static class Singleton {
private static final CryptoUtil instance = new CryptoUtil();
}

private CryptoUtil() {}

public static CryptoUtil getInstance(){
    return Singleton.instance;
}

/*

  • RSA 공개키와 개인키를 생성한다.

  • @param keyLength

  • /
    @SuppressWarnings("finally")
    public RSAKEYINFODTO generationRsaKey(int keyLength, boolean saveKeybit){
    KeyPairGenerator kpgenerator = null;
    RSAPublicKeySpec publicSpec = null;
    KeyPair keyPair = null;
    KeyFactory keyFactory = null;

    String publicKeyModulus = "";
    String publicKeyExponent = "";

    RSAKEYINFODTO rsakeyDTO =null;
    try {

      //RSA키 생성을 위한 초기 셋팅
      //RSA키 생성기 인스턴스 생성
      kpgenerator = KeyPairGenerator.getInstance("RSA");
    
      //SecureRandom 랜덤값에 의한 키 초기화
      SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
    
      kpgenerator.initialize(keyLength, random);
    
      //RSA 키쌍(공개키, 개인키) 생성
      keyPair = kpgenerator.genKeyPair();
      keyFactory = KeyFactory.getInstance("RSA");
    
      /*
       * RSA에서 PublicKey의  byte[]은
       * modulus와 exponent의
       * 좋바으로 이루어진 ASN.1
       * 포맷(publickey.getEncoded())
       */
    
      //공개키(사용자에게 발급), 개인키(서버에 저장)
      PublicKey publicKey = keyPair.getPublic();
      PrivateKey privateKey = keyPair.getPrivate();
    
      publicSpec = (RSAPublicKeySpec)keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);
      publicKeyModulus = publicSpec.getModulus().toString(16);
    
      //공개 지수, 16진수
      publicKeyExponent = publicSpec.getPublicExponent().toString(16);
    
      //공개키와 개인키정보를 저장하는 DTO객체를 생성
      rsakeyDTO = new RSAKEYINFODTO(keyLength, publicKeyModulus, publicKeyExponent);
    
      //옵션값에 따라 키값을 바이너리 파일로 젖아하거나
      //DTO객체에 셋 하거나 할 수 있다.
      if(saveKeybit)
          saveRsaPrivateKey(publicKeyModulus, privateKey);
      else
          rsakeyDTO.setPrivateKey(privateKey);
      rsakeyDTO.setPublicKey(publicKey);

    } catch (NoSuchAlgorithmException e) {

      e.printStackTrace();

    } catch (InvalidKeySpecException e){

      e.printStackTrace();

    } catch (FileNotFoundException e) {

      e.printStackTrace();

    } catch (IOException e){

      e.printStackTrace();

    }
    finally{

      return rsakeyDTO;

    }
    }

/*

  • Base64로 인코딩된 RSA로 암호화된

  • 문자열을 복호화한다.

  • @param RSAKEYINFODTO keyDTO 객체

  • @param encStr Base64로 인코딩된 암호화된 문자열

  • @param saveKeybit PrivateKey 저장구분

  • @return

  • /
    public String rsaDecrypt(RSAKEYINFODTO rsakeyDTO, String encStr, boolean saveKeybit)
    throws NoSuchAlgorithmException
    ,NoSuchPaddingException
    ,FileNotFoundException,IOException
    ,ClassNotFoundException,InvalidKeyException
    ,IllegalBlockSizeException,BadPaddingException{
    /*

    • 알고리즘 / 모드 /패딩 값 설정

    • 알고리즘 : 인스턴스를 생성 할 알고리즘 명을 지정할 수 있다 현재는 RSA

    • 모드 : 알고리즘 모드 지정(ECB,CBC,CCM,GCM,PCBC..)

    • 패딩 : 알고리즘 패딩값지정 (PKCS1Padding..)

    • /
      Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

      PrivateKey privateKey = null;

      /*

    • 비밀키를 가져온다

    • /
      if(saveKeybit) privateKey = readRsaPrivateKey(rsakeyDTO.getModulus());
      else privateKey = (PrivateKey)rsakeyDTO.getPrivateKey();

      cipher.init(Cipher.DECRYPT_MODE, privateKey);
      byte[] encryptedBytes = hexToByteArray(encStr);
      byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
      String decStr = new String(decryptedBytes,"UTF-8");

      return decStr;
      }
      }

public class RSAKEYINFODTO implements Serializable {
/**
*
*/
private static final long serialVersionUID = -7120303539138954172L;
private int keyLength; //RSA 키 길이
private String modulus; //공개키(Public Key)
private String exponent; //공개 지수
private String createdDt; //RSA 키 생성 날짜 시간
private String reqIpAddr; //로그인 IP
private String deviceId; //로그인 디바이스 id
private String loginGate; //로그인 디바이스 GATE
private PrivateKey privateKey; //로그인 개인키(Private Key)
private PublicKey publicKey; //로그인 공개키

public RSAKEYINFODTO(int keyLength, String publicKeyModulus, String publicKeyExponent) {
    this.keyLength = keyLength;
    this.modulus = publicKeyModulus;
    this.exponent = publicKeyExponent;
    SimpleDateFormat transFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    this.setCreatedDt(transFormat.format(new Date()));
}

public PrivateKey getPrivateKey() {
    return privateKey;
}

public void setPrivateKey(PrivateKey privateKey){
    this.privateKey = privateKey;
}

public String getModulus() {
    return this.modulus;
}

public String getExponent(){
    return this.exponent;
}

public void setDeviceId(String  deviceId){
    this.deviceId = deviceId;
}

public String getDeviceId() {
    return this.deviceId;
}

public String getCreatedDt() {
    return createdDt;
}

public void setCreatedDt(String createdDt) {
    this.createdDt = createdDt;
}

public String getReqIpAddr() {
    return reqIpAddr;
}

public void setReqIpAddr(String reqIpAddr) {
    this.reqIpAddr = reqIpAddr;
}

public String getLoginGate() {
    return loginGate;
}

public void setLoginGate(String loginGate) {
    this.loginGate = loginGate;
}

public PublicKey getPublicKey() {
    return publicKey;
}

public void setPublicKey(PublicKey publicKey) {
    this.publicKey = publicKey;
}

}

사용 방법
JAVA에서
private static byte[] stripLeadingZeros(byte[] a)
{

int lastZero = -1;

for (int i = 0; i < a.length; i++)

{

if (a[i] == 0)

{

lastZero = i;

}

else

{

break;

}

}

lastZero++;

byte[] result = new byte[a.length-lastZero];

System.arraycopy(a, lastZero, result, 0, result.length);

return result;

}
RSAKEYINFODTO rsakeyInfo = encryptInstance.generationRsaKey(ConstantUtil.RSA_KEYLENGTH_512, false);
loginManger.setSession(session, rsakeyInfo);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec rsaPublicKey = (RSAPublicKeySpec)keyFactory.getKeySpec(rsakeyInfo.getPublicKey(), RSAPublicKeySpec.class); ;
byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
modulusBytes = stripLeadingZeros(modulusBytes);
byte[] exponentBytes = rsaPublicKey.getPublicExponent().toByteArray();
String modulusString = Base64.encodeBase64String(modulusBytes); //C# 으로 전달할 값
String exponentString = Base64.encodeBase64String(exponentBytes); //C# 으로 전달할 값

C#에서 던질 값을 JAVA 에서복호화 하기
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, "개인키");
byte[] encryptedBytes = hexToByteArray("C#에서 암호화한 값");
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
String decStr = new String(decryptedBytes,"UTF-8"); //복호화

몇몇 코드는 안넣었는데 이해만 해서 직접 구현...