달력

3

« 2024/3 »

  • 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
2015. 4. 29. 23:03

Java에서 SHA 기반 해시암호화 Java2015. 4. 29. 23:03

  원래부터 중요하긴 하지만 요 몇 년새에 각종 보안사고(포털/금융기관 등에서의 개인정보 유출 등)가 많이 발생하면서 보안에 대해서 많이 깐깐해지고 있습니다.
그래도 외부에 공개되는 사이트의 경우는 좀 신경쓰고 있지만 내부 시스템의 경우는 비교적 최근까지 BASE64 기반 "인코딩"을 하고 나서 암호화라고 하는 곳도 봤고 심하면 아예 패스워드를 눈에 보이게 그대로 저장하는 케이스까지 있었습니다.


  그런데 이제는 구체적으로 암호화의 방법에 대해서 최소 256bit 이상으로 해야 하네, 패스워드는 복호화가 불가능하게 해시 암호화를 해야 하네 등등 가이드가 나오고 있습니다.  좋은 방향이죠...


  원래 암호화라는게 수학, 특히 정수론 등의 상당히 복잡한 이론에 기반한 내용이라 아무나 쉽게 접근할 수 없는 내용이지만 그래도 요즘에는 효율적인 암호화 알고리즘들이 라이브러리화되어 쉽게 사용할 수 있도록 제공되고 있어 내부적인 알고리즘까지는 알지 못하더라도 사용하기는 쉽게 제공되고 있습니다.


  이번에는 그 중에서 먼저 복호화가 불가능한 방식인 해시암호화, 그 중 대표적인 SHA 기반 암호화에 대해서 적어보려고 하는데 구체적인 내용까지는 모르더라도 해시암호화가 뭔지, 복호화가 가능한 암호화와 무엇이 다른지 등등 기본적인 내용은 알아야 상황에 따라서 잘 사용할 수 있겠죠..
암호화 알고리즘을 검토할 때 상당히 도움이 되었던 사이트가 있어서 소개드리니 기본적인 내용은 아시는 분들이라도 먼저 간단하게 내용을 한 번 보고 오기 바랍니다.  해시 암호화 위주의 설명이긴 하지만


안전한 패스워드 저장


  위 사이트에서 나온 부분 중 대표적인 해시 암호화 함수인 SHA- 계열을 Java에서 구현하는 방법을 기술한다.  Oracle(구 Sun)의 1.6 이상 JVM이면 다 지원됩니다(IBM의 JVM에서도 확인).



  SHA(위키피디아)는 Secure Hash Algorithm의 약자로 기존에 많이 사용되던 MD5(Message-Digest algorithm 5, 위키피디아)가 암호화로 쓰이기는 부적합하여 해시 암호화를 위해 개발되었고 Java의 암호화 알고리즘에서는 SHA-1, SHA-256, SHA-384, SHA-512 를 지원합니다.


서론이 길었으니 일단 그 중 SHA-512 방식의 암호화 함수 예제를 한 번 보겠습니다.

 private static String getSha512(String plainText) {
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-512");
			byte[] bytes = plainText.getBytes(Charset.forName("UTF-8"));
			md.update(bytes);
			return Base64.encode(md.digest());
		} catch (Exception e) {
			System.out.println("Sha512 error.");
			e.printStackTrace();
			return null;
		}
	}



  의외로 위와 같이 간단합니다.  SHA-1, SHA-256, SHA-384 등은 3번째 라인의 "SHA-512" 부분만 해당 값으로 변경하면 됩니다.  물론 간단한 코드라 Base64 인코딩이나 NullPointerException 방지를 위한 체크 등은 더 필요하구요.  


  다만 많은 구현에서 String <=> byte[] 간의 변환시에 별도의 인코딩을 지정을 안하는 경우가 많던데 그럴 경우 환경이 달라지면(예를들어 MS949 기반의 윈도우에서 구동하는 프로그램과 UTF-8 기반의 Unix에서 구동하는 프로그램이 동시에 서로 암복호화를 할 경우) 한글 등을 처리할 때 문제가 발생할 수 있으니 인코딩은 꼭 지정하시길 바랍니다.


  담번에는 대표적인 복호화 가능한 대칭키 암호화 알고리즘인 AES와 참고 사이트에서 나왔던 bCrypt에 대해서 간단히 포스팅할 예정입니다.
(일단 테스트는 다 된 상황이라.. 정리만 하면...)

'Java' 카테고리의 다른 글

"\ub4f1" 와 같은 unicode 문자열 읽어오기  (0) 2014.01.28
JDK 1.7.21에서 Runtime.exec의 변경사항  (0) 2013.04.26
:
Posted by hanavy
2014. 1. 28. 12:56

"\ub4f1" 와 같은 unicode 문자열 읽어오기 Java2014. 1. 28. 12:56

컴퓨터가 7bit면 자신들의 모든 문자를 표현 가능한 문화권에서 개발되다 보니 아무래도 우리나라처럼 2-byte 문자열을 쓰는 나라들에서는 아직까지도 문제가 생기는 경우가 많습니다.  우리나라 같은 경우는 한글문제라고 흔히들 통칭하는데..


그나마 현대의 언어들은 상당수 다국어 지원(유니코드를 비롯)을 기본적으로 가져가기 때문에 일반적인 환경에서는 문제가 잘 생기지 않는 경우들이 많지만 그렇기 때문에 가끔 문제가 생기면 더더욱 힘든 케이스들도 많이 보입니다.
자바의 경우는 처음부터 내부적으로 유니코드만을 사용하는 등 언어 자체적으로 국제화를 고려해서 만들었지만 초기 API 설계의 miss로 지금은 deprecated된 함수들도 많이 있죠...(국제화쪽 관련해서는 주로 I/O쪽.. LineNumberInputStream 등등..)


여튼 서론은 이정도고..
이번에 하는 프로젝트에서 property 파일(key=value 형태로 텍스트로 저장하는)에서 값을 읽어와야 할 일이 있는데 Eclipse 기반의 개발환경에서 property 파일 편집을 위해 일본에서 만든 플러그인인 PropertiesEditor라는걸 사용하더군요..
그런데 문제(?)는 이 플러그인이 파일을 저장하면 한글이 다음과 같이 저장됩니다.


# \uc2dc\uc2a4\ud15c \uacf5\ud1b5


저 문자열은 원래 다음과 같은 글자입니다.


# 시스템 공통


유니코드를 저장할 때 ASCII 영역 외의 2-byte 문자열의 경우 \uXXXX 형태로 저장하는 걸로 보입니다.
그런데 문제는 저 내용을 다음과 같이 소스코드상에 넣으면 잘 나오는데

public static void main(String[] args) {
    System.out.println("# \uc2dc\uc2a4\ud15c \uacf5\ud1b5");
}

저걸 파일에서 읽어오면 저 문자열 그대로 읽어오더군요..
처음에는 다음과 같은 방법을 사용해 봤습니다.

public static void main(String[] args) throws IOException {
    LineNumberReader in = new LineNumberReader(new InputStreamReader(new FileInputStream("c:\\a.properties"), "UTF-8"));
    System.out.println(in.readLine());
    in.close();

음.. UTF-8이긴 하지만 헥사값의 내용이 아닌 헥사값의 표현인 영문값이 저장된 형태이기 때문에 효용이 없었습니다.
그래서 이런때를 위한 구글신에게 검색이 들어갔습니다.


찾아보다 보니 다음과 같은 내용이 보이더군요..
Unicode Escape Formats 라는 내용인데 유니코드를 Escape Sequence를 이용해서 표기하는 여러가지 방법을 나열했는데 그 중에 위의 내용이 보입니다.
Java나 Ruby에서 쓰이는 포맷이라고 합니다.  언어별로 표기하는 법들이 다들 천차만별이라..


여튼 검색하다 보니 반대의 경우, 즉 한글을 넣으면 저렇게 unicode sequence를 기반으로 만들어 주는 프로그램은 JDK에 기본적으로 같이 제공이 되고 있습니다.
native2ascii 라는 프로그램인데 실행 후 console에서 입력하면 다음과 같이 만들어 줍니다.


실행하면 그냥 커서만 깜빡이는데 여기에 문자열을 아무거나 입력하면 "\u" 를 이용한 Escape Sequence 문자열로 만들어줍니다.(종료시에는 그냥 Ctrl-C 사용)
그런데 일단 다른 별도의 프로그램을 쓰는 방식은 프로그래머들에게는 별 도움 안되고 일단 지금 필요한 건 반대의 케이스라..
무언가가 있을거 같긴 한데 결국 찾지는 못했고 그냥 만들었습니다.(몇 개 찾는 솔루션들도 다들 직접 구현했더군요..)


원리는 간단합니다.\uXXXXX 형태로 저장된 문자열을 찾으면 \u를 제거하고 XXXX 로 표현된 32bit 문자열을 읽어서 해당 char 형태로 바꿔주면 됩니다.(자바는 내부적으로 유니코드를 사용하므로 char는 2 byte 입니다.)
일단 최적화는 크게 고민은 안했고 기능상으로 다음과 같이 만들었습니다.

	private String unicodeConvert(String str) {
		StringBuilder sb = new StringBuilder();
		char ch;
		int len = str.length();
		for (int i = 0; i < len; i++) {
			ch = str.charAt(i);
			if (ch == '\\' && str.charAt(i+1) == 'u') {
				sb.append((char) Integer.parseInt(str.substring(i+2, i+6), 16));
				i+=5;
				continue;
			}
			sb.append(ch);
		}
		return sb.toString();
	}

일단 개념 정도고 제대로 사용하려면 예외상황(\u 뒤에 4byte의 16진수 데이터가 올바로 나오지 않을때는? 등등)에 대한 처리가 좀 더 필요합니다.

'Java' 카테고리의 다른 글

Java에서 SHA 기반 해시암호화  (1) 2015.04.29
JDK 1.7.21에서 Runtime.exec의 변경사항  (0) 2013.04.26
:
Posted by hanavy
2013. 4. 26. 00:38

JDK 1.7.21에서 Runtime.exec의 변경사항 Java2013. 4. 26. 00:38

정말.. 오랜만에 포스팅이네요.. 1년 하고도 몇 달만에...
일상 포스팅이야 몰라도 개발쪽 포스팅은 아무래도 준비해야 할 것도 많고 해서 좀 부담스러워 자주 못 하게 됩니다.


여튼.. 이번 건은 최근에 자주 버전업이 되고 있는 자바와 관련한 건인데요..


최근 오라클로 넘어간 다음에 자바에 대해서 이러쿵 저러쿵 말이 많은데...
많이들 사용하다 보니 해커도 꼬이고 보안 구멍은 자꾸 늘어나는데다가 오라클이 하는 짓꺼리 때문에 별로 호응도 못 받고...
(James Gosling 아저씨도 떠났더랬죠..)


그때문이기도 하겠지만 예전처럼 버전이(물론 소숫점 아래 둘째 자리 혹은 _ 아래의 fix 버전 얘기지만) 하나씩 올라가는게 아니라 한번에 몇 개씩 올라가기도 합니다.(정확히 올라가는 기준은 잘 모르겠습니다만)
그러다가 이번에는 최신버전인 1.7.x 버전이 1.7.0_17에서 1.7.0_21로 네 단계올라갔는데요..
지금까지는 그냥 그러려니 했는데 이번 버전업을 한 뒤에 기존 프로그램에 오류가 발생한 사건이 생겨서 혹시나 저처럼 삽질하실 분들이 생길까봐 정리합니다.


기본적으로 업데이트 상세내역은 Update Release Notes 페이지에 잘 나와있습니다.
버전의 특성상 대부분 버그 또는 보안 패치인데요..


그 중에서 이번에 저한테 영향을 줬던 부분은 다음 부분입니다.
Changes to Runtime.exec


링크 가보셔도 됩니다만 아래와 같은 내용이 적혀있습니다.


On Windows platform, the decoding of command strings specified to Runtime.exec(String),Runtime.exec(String,String[]) and Runtime.exec(String,String[],File) methods, has been improved to follow the specification more closely. This may cause problems for applications that are using one or more of these methods with commands that contain spaces in the program name, or are invoking these methods with commands that are not quoted correctly.

For example, Runtime.getRuntime().exec("C:\\My Programs\\foo.exe bar") is an attempt to launch the program "C:\\My" with the arguments "Programs\\foo.exe" and "bar". This command is likely to fail with an exception to indicate "C:\My" cannot be found.

The example Runtime.getRuntime().exec("\"C:\\My Programs\\foo.exe\" bar") is an attempt to launch the program "\"C:\\My". This command will fail with an exception to indicate the program has an embedded quote.

Applications that need to launch programs with spaces in the program name should consider using the variants of Runtime.exec that allow the command and arguments to be specified in an array.

Alternatively, the preferred way to create operating systems processes since JDK 5.0 is usingjava.lang.ProcessBuilder. The ProcessBuilder class has a much more complete API for setting the environment, working directory and redirecting streams for the process.


울렁증 있으신 분들을 위해 간략하게 적어보면 Windows 플랫폼의 경우 자바의 Runtime.exec를 통해 다른 프로세스를 호출하여 실행할 경우 규격에 맞게 좀 더 까탈스러워졌다는 말입니다.
주로 경로(또는 파일명)에 공백이 있을 경우(대표적으로 Program Files와 같은 곳이 되겠죠) 발생할 수 있는 보안 이슈를 대비해 바꿨다고 합니다.


예를 들어 Runtime.getRuntime().exec("C:\\My Programs\\foo.exe bar") 라는 문장이 있었다고 보면
예전같으면 C:\My Programs\foo.exe를 bar라는 인자를 주고 실행하는 정상적인 코드였지만
이제는 C:\My 라는 프로그램에 Programs\foo.exe와 bar라는 두 개의 인자를 주고 실행하는 명령으로 인식을 한다고 합니다.
(즉, 무조건 첫번째 공백 다음에는 인자라는 얘기겠죠..)


앞으로는 Runtime.exec 의 다른 계열 중 실행파일명과 인자를 별도로 입력받는 함수들을 사용하라고 합니다.
아니면 JDK 1.5 버전 이상이면 더욱 더 많은 기능을 제공하는 ProcessBuilder 클래스를 사용하라고 하네요.


스펙에 맞도록 바꿨다는 말인걸 보니 스펙문서를 좀 더 자세히 봐야 하겠습니다만 여튼 위와 같은 방식으로 호출하는 경우가 있으면 이번 업그레이드 후 오류납니다..


저같은 경우는 아래와 같이 사용을 했었습니다.
Process proc = Runtime.getRuntime().exec("C:/Program Files/My/SysinternalsSuite/pslist.exe")


저는 일단 두 번째 방법을 이용해서 ProcessBuilder로 실행방식만 아래와 같이 바꾸니 실행이 가능했습니다.
Process proc = new ProcessBuilder("C:/Program Files/My/SysinternalsSuite/pslist.exe").start()

참고하시기 바랍니다.

'Java' 카테고리의 다른 글

Java에서 SHA 기반 해시암호화  (1) 2015.04.29
"\ub4f1" 와 같은 unicode 문자열 읽어오기  (0) 2014.01.28
:
Posted by hanavy