">

Spring에서 RSA 암호화를 사용하려는 도중 에러가 발생 했다. 


'nested exception is java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider'


필요한 jar도 추가해주고 build path도 잡아줬는데도 왜!!!?


검색해 보니 Java 폴더를 찾아서 jar 파일을 복사하면 된다고 한다. 


파일 경로는 jdk 폴더 안에 /Contents/Home/jre/ext 이다. (Mac 기준)


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

이 글은 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

다른 사람보다 내가 글을 더 잘 정리했다면 내 블로그를 통해 정보를 가져가는 사람이 많을 것이고 그렇다면 검색 순위를 관리하는 것도 좋지 않을까 생각에 다다르게 되었습니다. 그러다보니 파워 블로거라면 기본으로 한다는 몇 가지들을 나도 적용해보며 정리해보았어요. 

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


1. 구글 서치 콘솔에 블로그 등록 하기

우선 Google 웹 마스터 페이지를 열어 보자. https://www.google.com/webmasters/#?modal_active=none

여기서 Search Console를 클릭한다. 



2. 구글 계정으로 로그인을 하고, 속성 추가 버튼을 눌러 자신의 블로그 주소를 입력한다. 



3. 실제 서버가 아닌 블로그에 사용할거기에 대체방법을 택하고 html 태그를 넣는 것으로 합니다. 

4. 자신의 블로그에 위의 태그를 붙여 넣기 합니다. 블로그의 '관리페이지 - HTML/CSS편집'을 클릭하면 아래 그림과 같은 화면을 볼 수 있습니다. 

그리고 위에서 복사한 HTML 태그를 아래와 같이 넣어주시면 됩니다. 



5. 등록이 완료 되면 아래와 같은 화면이 나옵니다. 



오른쪽을 보면 Sitemaps라고 있는데 이것에 관해서는 다음 글에서 등록하는 방법 과 설명을 하도록 하겠습니다.


글을 읽고 도움이 되셨다면 '공감' 클릭 부탁드려요!

+ Recent posts