">

개발을 하면서 암호화에 대해 깊이 있게 생각하지 않고 필요한 스펙 대로만 구현 했었는데, 이번에 시간이 좀 있어 블로그에 정리를 해봅니다. 

이 글은 Java 기준으로 작성하였습니다. 


여러 글을 보면서 검색을 하다 보니 타 업체와 협의 할 때 적어도 아래와 같은 스펙을 서로 교환하는게 좋다고 합니다. (http://redutan.github.io/2015/11/20/about-crypto)



암호화 스펙

    • 알고리즘: RSA-256
    • 암호화 키: 2017201820192020wow2021202220232024202520262027202820292030
    • 인코딩: UTF-8


public class RSAEncrypto {
	private static final String algorithm = "RSA/None/NoPadding";
	private static final String provider = "BC";

	public static void main(String[] args) {
		
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

		String plainText = "2017201820192020wow2021202220232024202520262027202820292030";

		try {
			Cipher serverCipher = Cipher.getInstance(algorithm, provider);
			SecureRandom random = new SecureRandom();
			KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
			generator.initialize(512, random);
			
			KeyPair pair = generator.generateKeyPair();
			Key publicKey = pair.getPublic();  
			Key privateKey = pair.getPrivate();

			// 개인키로 암호화
			serverCipher.init(Cipher.ENCRYPT_MODE, privateKey);
			byte[] cipherText = serverCipher.doFinal(plainText.getBytes());
			System.out.println("cipher: ("+ cipherText.length +")"+ new String(cipherText));

			// 공개키로 복호화
			serverCipher.init(Cipher.DECRYPT_MODE, publicKey);
			byte[] plainTextB = serverCipher.doFinal(cipherText);

			System.out.println("plain: " + new String(plainTextB));
			
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		}
	}
}


이렇게 하면 암호화 하고, 복호화 되는 것을 확인할 수 있습니다. 하지만 실무에서는 이렇게 사용하는 경우는 거의 없습니다. 그래서 추가로 필요한 부분이 있습니다. 바로 Base64로 인코딩을 해야 되는 것입니다. 이상한 문자로 나온 암호문을 통신 할 때나, DB에 저장해서 사용할 수 없기에 Base64로 인코딩 합니다. 그리고 복호화 할 때는 다시 디코딩을 해서 복호화를 진행합니다. 


참고로 Base64는 8비트 바이너리 데이터를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 스트링으로 바꾸는 인코딩 방식을 말합니다. 아래 그림을 보면 Base64로 인코딩 된 것은 우리가 알아볼 수 있는 문자로 출력 됩니다. 




인코딩을 사용하여 적용한 소스는 다음과 같습니다. 

public class RSAEncrypto {
	private static final String algorithm = "RSA/None/NoPadding";
	private static final String provider = "BC";

	public static void main(String[] args) {
		
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

		String plainText = "2017201820192020wow2021202220232024202520262027202820292030";

		try {
			Cipher serverCipher = Cipher.getInstance(algorithm, provider);
			SecureRandom random = new SecureRandom();
			KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
			generator.initialize(512, random);
			
			KeyPair pair = generator.generateKeyPair();
			Key publicKey = pair.getPublic();  
			Key privateKey = pair.getPrivate();

			// 개인키를 전달하여 암호화
			serverCipher.init(Cipher.ENCRYPT_MODE, privateKey);
			byte[] cipherText = serverCipher.doFinal(plainText.getBytes("UTF-8"));
			System.out.println("cipher: ("+ cipherText.length +")"+ new String(cipherText));
			
			// Base64로 인코딩
			byte[] encodedBase64 = Base64.encode(cipherText);
			System.out.println("Base64Encoded: " + new String(encodedBase64));
			
			// Base64로 디코딩
			byte[] decodedBase64 = Base64.decode(encodedBase64);
			System.out.println("Base64Decoded: " + new String(decodedBase64));
			
			// 공개키를 가지고있는쪽에서 복호화
			serverCipher.init(Cipher.DECRYPT_MODE, publicKey);
			byte[] plainTextB = serverCipher.doFinal(decodedBase64);
			
			System.out.println("plain: " + new String((plainTextB), "UTF-8"));
			
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


패딩을 사용 하는 이유에 대해서는 다음 링크를 참조하시면 됩니다. 
여러 이유에 대한 설명이 있으니 꼭 읽어보세요. 
https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Padding_schemes

마지막으로 RSA 키를 저장하고 복구 하는 것에 대한 설명은 링크로 대체 하겠습니다. 


도움 되셨다면 아래 '공감' 클릭 한번 해주세요~ 감사합니다. 


'프로그래밍 > JAVA' 카테고리의 다른 글

CLOSE_WAIT 해결 방법  (1) 2017.01.13
자바로 크롤링 구현  (0) 2016.09.13
문자열을 나누거나 합치거나  (0) 2016.09.13

+ Recent posts