프로젝트하는 도중 재미있는 현상을 발견하여 글을 써 봅니다. 


서로 발급된 키로 sha1으로 암호화 하여 통신하는 api를 개발하는 도중 java 와 python의 sha1의 값이 다르게 나오는것을 발견하였습니다. 


아래는 java의 sha1 암호화의 기본 소스 입니다. 



import java.security.MessageDigest;
import java.security.Security;
import java.security.NoSuchAlgorithmException;


public class HelloWorld{
     public static void main(String []args) throws NoSuchAlgorithmException{

         String return_sha1 = makeSHA("81014614b19daf89e35c46ee299ff7ff1c2239e0641874ab");

        System.out.println(return_sha1);
     }

     public static String makeSHA(String inputText) throws NoSuchAlgorithmException{

      String test = inputText;
      MessageDigest md = MessageDigest.getInstance("SHA-1");
      md.update(test.getBytes());
      byte[] digest = md.digest();


      StringBuffer sb = new StringBuffer();
      
      for(byte b : digest){
       sb.append(Integer.toHexString(b & 0xff));
      }

      return sb.toString();
     }
}


그리고 해당 결과 값은 "4728865bb77267f77ddac8d7dbb579f447d27c" 가 나옵니다. 


다음엔 python의 소스 입니다.



import hashlib
m = hashlib.sha1()
str_sha1 = "81014614b19daf89e35c46ee299ff7ff1c2239e0641874ab"
m.update(str_sha1)
print m.hexdigest()

python의 결과 값은 "47028865bb77267f77ddac8d7dbb579f4407d27c" 가 나옵니다. 


java        4728865bb77267f77ddac8d7dbb579f447d27c

python   47028865bb77267f77ddac8d7dbb579f4407d27c


자세히 보시면 java에서 두자리가 나오지 않은것을 확인할수 있습니다.


해당 이슈는 java의 암호화 방식에 있습니다. 

java에서 sha1으로 암호화시 byte->hex로 변환하지만 해당 변환이 암호화된 값이라 다르게 표현됩니다. 

반면 인터프린터 언어(python포함)에서는 string 자체를 sha1으로 변환해주기 때문에 위와 같이 sha1으로 암호화 했어도 다른 값이 나오게 됩니다. 


이런 경우를 대비해서 java에 소스를 추가해 주시면 됩니다. 



import java.security.MessageDigest;
import java.security.Security;
import java.security.NoSuchAlgorithmException;
import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.DatatypeConverter.*;

public class HelloWorld{
     public static void main(String []args) throws NoSuchAlgorithmException{

         String return_sha1 = makeSHA("81014614b19daf89e35c46ee299ff7ff1c2239e0641874ab");
        System.out.println(return_sha1);
     }
     
     public static String makeSHA(String inputText) throws NoSuchAlgorithmException{

      String test = inputText;
      MessageDigest md = MessageDigest.getInstance("SHA-1");
      md.update(test.getBytes());
      byte[] digest = md.digest();
      String result = DatatypeConverter.printHexBinary(digest);

      return result;
     }
}

위의 소스로 변경하면 정상적으로 "47028865BB77267F77DDAC8D7DBB579F4407D27C" 을 출력합니다. 


추가한 소스는 javax의 DatatypeConverter으로 xml의 데이터 변환에 사용되는 함수 입니다. 

해당 함수를 사용하면 기존의 byte->hex 변환 필요 없이 sha1의 데이터를 가져 올수 있습니다. 


- java간의 통신이라면 굳이 수정할 필요는 없습니다.

- 위의 방법을 쓰지 않으면 sha1의 키값에 따라 정상적으로 나올수도 아닐수도 있습니다. (결과값에서 byte->hex 변환시 달라질수 있기 때문입니다.)

저작자 표시
신고

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret

더보기


저작자 표시
신고

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret

더보기


저작자 표시
신고

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret

더보기


저작자 표시
신고

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret

알고리즘을 하나 생각해본김에 만들고 있었다. 물론 int 배열로 1억개를 만드는것만큼 이상한것은 없지만, 

처리 속도 때문에 리스트로 만들다가 배열로 만들어보기로 했다. 

(알다시피 리스트로 삭제/입력 속도와 배열의 삭제/입력 속도 차이는 엄청나다. 갯수가 늘어갈수록 기하급수적으로 속도차이가 난다. 언젠가 한번 테스트 해보는것도 도움이 될것이다.)


에러 두둥! 힙메모리가 부족하다고 뜬다.


물론 eclipse.ini 파일을 변경하여(1024m으로 변경) 실행해도 결과값은 똑같다.

아무래도 32비트에서는 이클립스 메모리 한계치가 있는듯하다.


1억미만 가장 큰 배열은 몇개를 선언할수 있을까? 하는 마음에 소스 수정

소스보기


결과는?



int[44708793] 까지 성공. 그다음은 에러가 발생.

결국 32비트에서는 1억도 아닌 5천만 밑에서 끝나고 말았다.



64비트에서는 어떻까?

64비트에서 성공한 최대 배열의 수는 다음과 같다. 

삼억오천사백칠만사천육백이십.

그 이상 넘어가면 바로 아웃오브메모리가 발생.


소스보기



1억은 가뿐하게 넘어가며 3억 5천을 찍어주었다.



1. 위의 상황들은 해당 컴퓨터의 프로그램상태(메모리 점유율)에 따라 달라질수 있음을 명시한다.

2. 첫번째 예시의 경우 for 문으로 계속적으로 생성하였기 때문에 가비지컬렉션에 따른 메모리 청소가 얼마나 효과적으로 작동했느냐에 따라서 결과가 달라질수 있음도 명시한다.

3. 해당 컴퓨터의 메모리는 32비트의 경우 4g 64비트의 경우 8g 임을 알려드립니다.


최종 목적은 32비트에서 int[] 이 1억개 를 선언할수 있는가? 였지만. 역시나 실패.

64비트에서는 성공. 

하지만!! 과연 int[] 을 1억개나 쓸려고 하는 사람이 있을까?ㅋㅋㅋ 


저작자 표시
신고

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret

[갈릴레오 버전]


프로그램 특성상 이클립스에서 java 실행시 메모리사용량을 변경했더니 위와 같은 에러가 발생한다.

(발생 이유는 다양할수 있습니다.)

간단하게 해결가능.





이클립스가 설치된 폴더로 들어가 보면 eclipse.ini 라는 설정파일이 있습니다. 이 설정파일을 열어보시면(메모장으로 열으셔도 됩니다.) openFile 란에 위와 같이 -vm 표기와 javaw.exe의 경로(사용PC/ java버전 마다 경로가 다르겠지요?)를 넣어주고 저장.

-vm

C:\Program Files\Java\jdk1.7.0_21\bin\javaw.exe



실행하면 잘됩니다. 



[juno 버전]


주노버전에서는 위와 같이 failed to create the java virtual machine 라고 뜨면서 안됩니다.(신기하네요. 두버전 다 하루에 이러니..ㅎ) 이클립스 설치된 폴더로 가서 eclipse.ini 파일을 열어봤더니 -vm 경로가 정상적으로 되어 있습니다. 이럴 경우엔 해당 메모리를 조금만 낮추어 줍니다.(저의 경우 -Xmx 를 1g로 했었습니다.)


-Xmx512m 


으로 변경했더니 정상적으로 실행이 됩니다. 




저작자 표시
신고

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret

재미있는 퀴즈를 내겠습니다.


public class  typetest {


public static void main(String[] args) {

typetest tt = new typetest();

}

public void typetest(){

System.out.println("void");

}

public typetest(){

System.out.println("null");

}

}


1. 위의 소스를 컴파일 한다면 에러나 날까요?

2. 만일 에러가 안난다면 결과값은 무엇일까요?


생각해 보셨나요?  아래의 정답과 비교해 보시기 바랍니다.

정답보기


1번 물음은 클래스안에 똑같은 함수의 이름이 두개가 있습니다. 물론 오버로딩도 오버라이딩도 아닙니다.

단지 리턴표시만 다름니다. 어떻게 컴파일이 가능할까요?


먼저 생성자에 대해서 생각해봅시다. 생성자의 생성 규칙은 무엇일까요?  접근제한수식어인 public, protected, private는 사용가능하지만 static, final, synchronized 등의 수식어는 사용불가능합니다. 이는 접근제어를 통한 인스턴스를 할수 있지만 생성자는 해당 클래스의 고유한것으로 상속할수 없으므로 static, final 등은 사용이 불가능합니다. 또한 리턴값이 허용되지 않습니다. 


그렇다면 아래 보기중 생성자는 어떤것일까요?

1. public void typetest(){}

2. public typetest(){}


네 정답은 위의 첫번째 질문에서 2번 문항의 답과 같습니다. 2번입니다.

여기서 생각해야 될것은 void 는 리턴값이 없다는것을 나타내는 클래스입니다.(하지만 객체로 만들수 없습니다. API참조.) 함수 앞에 boolean, int, string 등 리턴값이 없을때 나타내는 값이 void 입니다. "이 함수는 리턴값이 없어!" 라는 예약어와 같습니다. 만일 함수 앞에 리턴을 표기 하지 않는다면 에러가 발생하게 됩니다.


 다시 의문이 듭니다. 앞에서 "리턴을 표기 하지 않는다면 에러가 발생하게 됩니다."라고 했습니다. 그렇다면 "리턴값이 없는 생성자는 함수인가?" 라는 의문이요. 

 이번엔 함수의 정의에 대해서 생각해 봅시다. 함수란 일정한 파라미터가 들어갔을때(파라미터가 없는것도 포함) 리턴값이 있는것을 함수라 합니다. 또한 객체지향언어에서는 오버라이딩이 가능하며, 정적메소드,synchronized (동기화)가 가능합니다. 또한 함수는 접근가능한곳에서 몇번이고 불러올수 있습니다.

 결국 생성자는 함수가 아닙니다. 그냥 흘러가는 , 무조건 표기되어야 하는 코드라 생각하면된다. (무조건이란말은 우리가 생성자를 작성하지 않아도 컴파일러가 자동으로 생성해 줍니다.) 변수의 초기화 및 선행되어야 하는 행동에 해서 보기 쉽게 묶어 놓은 코드덩어리 라 생각하면 됩니다.


 마지막으로 처음의 코드에 대해서 설명드리겠습니다. 클래스와 이름이 같은 void 형 함수 하나와 생성자 하나로 이루어진 클래스입니다. void 형 함수로 오버라이딩 또한 가능합니다.(한번 실습해 보세요.) 

 void 는 리턴값이 없다는것을 알려준다는 예약어라는것. 생성자는 함수가 아니라는것. 그리고 생성자가 아니더라도 함수 이름은 클래스와 똑같이 표기 가능하다는것을 기억해주시기 바랍니다. 




- 만일 

  public void typetest(){

System.out.println("void");

 } 

 이함수를 호출해야 겠다면? tt.typetest(); 로 불러오면됩니다.


- 아마 C를 먼저 배우신 분들은 이문제를 저와 같이 헷갈렸을 겁니다. c에서는 void* v; 이런 형식으로 선언 가능하지만 java 에서는 void 자체는 인스턴스화를 하지 못합니다.

  http://docs.oracle.com/javase/6/docs/api/java/lang/Void.html api를 보면 함수리턴에 대한 명시 일뿐 다른 기능이 전혀 없다.


- 솔직히 void를 쓰고 생성자라 생각하고 진행하다가 하루동안이나 프로그램이 실행되지 않아서 끙끙댔습니다. 생성자라고 생각했던 놈이 함수였던 것이지요.  결론은 기초를 튼튼히 하자.

저작자 표시
신고

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret

Swing jtable Thread

app/java 2013.01.07 16:16

원본 : http://stackoverflow.com/questions/11891457/java-update-jtable-row-from-another-thread

   에서 thread 종료와 마우스 이벤트 추가(클릭이벤트)를 해 보았습니다.


Begin 버튼을 눌러주면 Thread를 생성하여 칼럼값을 계속 바꿔줍니다.

마우스로 해당 로우를 클릭하고 finish 버튼을 눌러주면 해당 Thread가 죽습니다.


일단 원본 소스에 있던 SwingUtilities.invokeLater는 제외하였습니다.(굳이 사용이유가 없었습니다.)


각각의 Thread를 구분하기 위해서 Thread가 생성된 객체를 벡터에 넣고 

마우스 이벤트로 해당 벡터의 위치를 알아낸뒤

Thread.interrupt()함수를 통해서 Thread를 멈춥니다.



파일명은 final2.java 입니다.


소스보기





- 내가 이걸 왜 만들었는지 모르겠다;;ㅋ(무엇인가 만들려고 했는데 기억이 안난다..;)

- 언제나 느끼지만 Thread는 어려운데 재미있다~


저작자 표시
신고

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret

소켓을 사용하여 받아오는 과정에서 아래와 같이 한글이 깨집니다.

한글깨진다면 인코딩을 손보면 되겠지만 신기한건 중간중간이 깨집니다.


그것도 받아올때마다 똑같은 곳만 깨집니다.(이말은 받아오는 소켓에는 문제가 없다는것이지요.)

또한 않깨진 곳의 한글은 정확하게 잘 나온다는겁니다.(깨진곳만 계속 깨진다는것은 결국 인코딩 문제이지요.)


그래서 한글 인코딩으로 모두 확인했었습니다.

new String(str.getBytes("현재인코딩"), "변경인코딩")


utf-8, euc-kr, ksc5601, x-windows-949, iso-8859-1 까지..알고 있는 인코딩을 모두 해봤지만 전부 다 깨지던가 위에처럼 중간만 깨지더군요.

구글을 아무리 찾아봐도 않나왔고, 받을때 int 형으로 받은다음 char형으로 변경하면 된다라는 말도 있었지만 결과는 똑같았습니다.


해결은 제 실수 더군요. 소켓으로 받아올때 인코딩 방식을 쓰지 않았었습니다.


변경전

BufferedReader rd = new BufferedReader(new InputStreamReader(socket.getInputStream()));

변경후

BufferedReader rd = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));


아래와 같이 정상적으로 잘 나옵니다.


인코딩 방식을 정해주지 않자. java에서는 임의의(혹은 기본으로 정해진) 인코딩 방식으로 가져왔고, 

보여주는건 현재 가져온 인코딩 방식으로 출력가능한 한글문자만 출력된것으로 보입니다.


결론은 아는척 하지말고 기본에 충실하자.(기본적으로 해야될것을 않하고 대충 하니 몇시간을 날려버리네요.)

저작자 표시
신고

'app > java' 카테고리의 다른 글

java 생성자에 대한 고찰  (0) 2013.01.08
Swing jtable Thread  (0) 2013.01.07
socket url 이용시 한글 중간 깨짐 현상  (0) 2012.12.27
java BigInteger 사용하기  (0) 2012.02.23
java replace / replaceFirst / replaceAll $ 주의 할점.  (0) 2012.02.13
if 문에 대한 단상  (0) 2011.10.29

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret
import java.math.BigInteger;

public class bigInt {
  public static void main(String[] args) {
//BigInteger 객체 생성 MAX값은 없는듯하다.
  BigInteger bi = new BigInteger("1000000000000000000000000000000000");
//덧셈(더하기 10)
  bi = bi.add(BigInteger.valueOf(10));
  System.out.println(bi+ "\n");

//뺄셈(빼기 10)
  bi = bi.subtract(BigInteger.valueOf(10));
  System.out.println(bi+ "\n");

//곱셈 (곱하기 2)
  bi = bi.multiply(BigInteger.valueOf(2));
  System.out.println(bi+ "\n");
//나눗셈(나누기 2)
  bi = bi.divide(BigInteger.valueOf(2));
  System.out.println(bi+ "\n");

  }
}


 
 BigInteger 의 연산은 모두 함수로 이뤄지며(뭐 물론 쉽게 하려면 상속받아서 따로 클래스를 만들면 되지만) 
함수의 인자값은  BigInteger 가 되어야 합니다. 만일 일반 상수를 쓸경우 valueOf() 함수를 써서 넣으면 됩니다. valueOf()의 파라미터는 long 형입니다. 

레퍼런스 :  http://docs.oracle.com/javase/1.4.2/docs/api/java/math/BigInteger.html
저작자 표시
신고

WRITTEN BY
No.190
세계정복의 시작점

받은 트랙백이 없고 , 댓글이 없습니다.
secret