티스토리 뷰

이전에 작성한 암호화 관련 글을 쓴 적이 있는데, 어떻게 적용할 것인가에 대해서만 작성했지, 암호화를 어떻게 구현할 것인가에 대한 내용이 빠져서 추가로 정리해보려고 한다. 

 

내가 사용하는 Java 기준이다.

 

또, 안드로이드-Spring 서버 간 암복호화는 문제가 없었지만, iOS-Spring 간의 암복화에는 문제가 있었고, 아직 해결을 하지 못했다. 그 내용도 정리해보려고 한다.

1. KeyGenerator

프로젝트에서 RSA를 사용했기 때문에, RSA를 기준으로 키를 생성했다. java.security.KeyPairGenerator 클래스에서 instance를 만들어 RSA 키를 생성한다.

SecureRandom secureRandom = new SecureRandom();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048, secureRandom);
KeyPair keyPair = keyPairGenerator.genKeyPair();

PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();

RSA 외에도 DiffieHellman, DSA 알고리즘을 사용할 수 있는데, "RSA" 대신 "DiffieHellman" ,"DSA"를 넣으면 된다. DiffieHellman (1024), DSA (1024), RSA (1024, 2048) 각 key size를 사용할 수 있다.

2. Encrypt/Decrypt

  • Encrypt
//만들어진 공개키 객체를 기반으로 암호화 모드로 설정하는 과정
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);

//평문을 암호화하는 과정
byte[] byteEncryptedData = cipher.doFinal(plainData.getBytes(StandardCharsets.UTF_8));
  • Decrypt
//만들어진 개인키객체를 기반으로 암호화모드로 설정하는 과정
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] byteDecryptedData = cipher.doFinal(byteEncryptedData);
String decryptData = new String(byteDecryptedData);

암복호화 과정은 딱히 설명할게 없는게, cipher 클래스로 암호화 방식과 모드를 설정한 후 암복호화를 진행한다.

3. Base64 Encoding/Decoding

공개키는 별도의 오브젝트형태고, 암호화된 데이터는 바이트 형식으로 생성된다. 때문에 서버-클라이언트 간 데이터 전송 편의를 위해서, Base64로 인코딩해 String형태로 변환한 후 데이터를 전송한다.

  • 클라이언트 -> 서버로 공개키(public Key)를 전송할 때, request시 전송해줘야 함
String encryptedData = Base64.getEncoder().encodeToString(byteEncryptedData);
  • 받은 공개키로 데이터를 암호화하고, 클라이언트에게 response
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] bytePublicKey = Base64.getDecoder().decode(encryptPublicKey.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(bytePublicKey);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
...
암호화
...
String encryptedData = Base64.getEncoder().encodeToString(byteEncryptedData);

클라이언트 단에서 별도의 복호화 툴을 쓸 수 있지만, 자바에서는 아래와 같이 암호화된 데이터를 받아서 복호화한다.

byte[] byteEncryptedData = Base64.getDecoder().decode(encryptedData.getBytes(StandardCharsets.UTF_8));
... 복호화

4. Java-iOS 간 RSA key format 문제

현재 개발/운영 중인 앱은 네이티브 단말을 Android와 iOS를 둘 다 쓴다.

같은 자바를 쓰는 Android는 RSA 키 생성 및 데이터 암복호화에 큰 문제가 없었다.

 

문제는 iOS에서 발생했는데, iOS에서 swift에서는 RSA key Generator가 내장되어 있지 않았다.(Object-C에도 없었던 것 같다.) 그래서 외부 라이브러리를 사용했는데, 사용하는 라이브러리들이 모두 key 생성 방식을 PKCS#1으로 채택하고 있었다.

 

그러나 자바는 PKCS#8 방식을 사용하고 있어서 key 호환 문제가 발생한다.

 

공개 키(public key)를 예를 들면, 시작과 끝의 태그와 DER 구조가 다르다.

  • PKCS#1, PKCS#8 key
  • PKCS#1, PKCS#8 DER

개인키도 PKCS#1과 PKCS#8이 비슷한 차이가 있다.

 

자바에서 PKCS#1 key Generator를 제공하면 쉽게 해결할 수 있겠지만, 아쉽게도 자바에서는 PKCS#8 방식만을 제공했다. 때문에 bouncycastle이라는 외부라이브러리를 이용해 PKCS#1을 PKCS#8로 변환해서 사용했다.

byte[] decoded = Base64.getDecoder().decode(encryptPublicKey);
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(decoded);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey = kf.generatePublic(keySpec);

//만들어진 공개키객체를 기반으로 암호화모드로 설정하는 과정
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);

//평문을 암호화하는 과정
byte[] byteEncryptedData = cipher.doFinal(plainData.getBytes(StandardCharsets.UTF_8));
String encryptedData = Base64.getEncoder().encodeToString(byteEncryptedData);

외부라이브러리를 사용하지 않고 byte화 된 key를 직접 변환해 사용하는 방법도 있다.

 

문제는 이렇게 생성된 암호화된 평문은 PKCS#8 형식의 공개키로 암호화 했기 때문에, iOS에서 데이터를 가져가서 복호화하려면 개인키를 PKCS#8로 변환해 주어야한다는 문제가 있어, 변환을 요청했지만 잘 안되는지 계속 딜레이가 됐다.

 

자바에서는 포맷 변환이 됐기 때문에 swift에서도 변환해서 사용해볼 것을 요청했지만, iOS개발자 분이 불가능하다고 해 일정이 계속 딜레이되다가 일단 개발은 중단되었다. 여기서 변환 방법을 제공하는거 같은데, 써봤는지 잘 모르겠다.

5. 마치며

개인 정보를 좀더 안전한 암호화 방식인 RSA를 적용해보려 했지만, 결국에는 미뤄진 건이다. 긴급 개발 건인데 계속해서 딜레이 될 수는 없어서 기존에 사용하던 암호화를 적용해 배포했다.

 

암호화 변환 방식이 java에는 있었고, 반영 테스트까지 완료했었지만 swift엔 없거나 안된다고해서 swift를 잘 모르는 나도 이리저리 알아봤지만 해결이 되지 않았던, 좀 답답했었던 문제였다.

 

이러면 나중에, 다시 돌아와서 RSA보다 보안성이 좋다는 ECC 암호화 방식을 재검토해야할 수도 있다..


 
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함