임동문의 횡설수설

dmlim.egloos.com

포토로그


라이프로그


Node.js 에서 유용한 Text 프로토콜 라이브러리 프로그래밍


Java JNI DLL 언로드

출처

문제

  • System.load()나 System.loadLibrary()로 DLL 모듈을 한번 로드하고 난뒤에, 어떻게 언로드할까?
  • 이론적으로 native 메소드가 구현된 클래스를 로딩한 ClassLoader가 가비지 컬렉트 되지 않으면 DLL을 언로드할 방법이없다.

클래스 로더는 언제 가비지 컬렉트 될까?

  • 자신이 로드한 모든 오프젝트가 가비지 컬렉트 되면 자신도 가비지 컬렉트 된다.
  • 물론 클래스 로더에 대한 레퍼런스들이 모두 null 이어야 한다.

어떤 클래스 로더를 써야 하나?

커스텀 클래스 로더를 이용하여 DLL을 언로드 하는 예제

다음과 같이 navive 메소드를 가진 클래스 A 를 만든다.

package com.codethesis.example;

public class A {
 public A() {}

 static {
  System.loadLibrary("mydll");
 }

 public native void print();

 public void finalize() {
  System.out.println("A garbage collected");
 }
}


print() 메소드가 구현된 mydll.dll 을 만들어서 클래스 패스에 넣는다..
JNI example 은
http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html여기에서 참고하시길
그리고, 테스트 클래스를 만든다.


package com.codethesis.example;

import java.lang.reflect.Method;

public class Test {
 public static void main(String[] args) throws Exception {
  CustomClassLoader cl = new CustomClassLoader();
  Class ca = cl.findClass("com.codethesis.example.A");
  Object a = ca.newInstance();
  Method p = ca.getMethod("print");
  p.invoke(a);
  p = null;
  ca = null;
  a = null;
  cl = null;
  System.gc();
 }
}


System.gc() 가 호출된 뒤에 클래스 로더가 가비지 컬랙트 되면, DLL도 언로드 될것이다.



왜 사용하기 귀찮게 Java reflection 을 썼나?

"a" 오르젝트를 그냥 "A" 클래스로 캐스팅할 수도 있지 않는가?

package com.codethesis.example;

import java.lang.reflect.Method;

public class Test {
 public static void main(String[] args) throws Exception {
  CustomClassLoader cl = new CustomClassLoader();
  Class ca = cl.findClass("com.codethesis.example.A");
  A a = (A)ca.newInstance(); //a ClassCastException is thrown!
  a.print();
  ca = null;
  a = null;
  cl = null;
  System.gc();
 }
}


실제로는 불가능하다. "A" 클래스는 디폴트 클래스로더에 의해서 생성이 되었고.
"a" 오프젝트는 커스텀 클래스로더에 의해서 생성이 되었기 때문에, 서로 다른 클래스가 된다.
그리하여, 인터페이스를 이용한 쉬운 대안을 마련했다.

IA 인터페이스를 작성한다.


package com.codethesis.example;

public interface IA {
 public void print();
}


IA 를 구현한 클래스 A를 다시 만든다.


package com.codethesis.example;

public class A implements IA {
 public A() {}

 static {
  System.loadLibrary("mydll");
 }

 public native void print();

 public void finalize() {
  System.out.println("A garbage collected");
 }
}


그리고 아래와 같이 테스트 코드를 작성 한다.


package com.codethesis.example;

public class Test {
 public static void main(String[] args) throws Exception {
  CustomClassLoader cl = new CustomClassLoader();
  Class ca = cl.findClass("com.codethesis.example.A");
  IA ia = (IA)ca.newInstance();
  ia.print();
  ca = null;
  ia = null;
  cl = null;
  System.gc();
 }
}


 


[깔대기] 오라클에 리포팅한 자바 런타임 버그가 등록되다. ㅋ 프로그래밍

자바 소켓에서 Socket 클래스를 이용할 때, SOCKS 프록시를 이용할 수 있습니다.
문제는 디폴트 ProxySelector 가 아닌 커스터마이징된 ProxySelector 를 이용할 때,
Fail-over 가 되지 않는 버그를 발견해서 리포팅 했더니 아래와 같이 메일이 왔네요. ㅋ

-------- 아래 메일 ---------

Subject: Your Report (Bug ID: 7141231) - SOCKS proxy failover using ProxySelector does not work.

Dear Java Developer,
 
Thank you for reporting this issue.

We have determined that this report is a new bug and entered the bug into our internal bug tracking system under Bug Id: 7141231.

You can monitor this bug on the Java Bug Database at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7141231.

It may take a day or two before your bug shows up in this external database.  If you are a member of the Sun Developer Network (SDN), there are two additional options once the bug is visible.

1. Voting for the bug
   Click http://bugs.sun.com/bugdatabase/addVote.do?bug_id=7141231.

2. Adding the report to your Bug Watch list.
   You will receive an email notification when this bug is updated.
   Click http://bugs.sun.com/bugdatabase/addBugWatch.do?bug_id=7141231.

The Sun Developer Network (http://developers.sun.com) is a free service that Sun offers.  To join, visit https://softwarereg.sun.com/registration/developer/en_US/new_user.
 
Regards,
Java Developer Support

----------------- 아래는 버그 테스트 코드 -----------------

package dmlim.net;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;

public class SocksProxyBugTest
{
 static InetSocketAddress DEAD__HTTP__PROXY_ADDR = new InetSocketAddress("10.13.1.218", 8080);
 static InetSocketAddress ALIVE_HTTP__PROXY_ADDR = new InetSocketAddress("10.13.1.218", 8081);
 
 static InetSocketAddress DEAD__SOCKS_PROXY_ADDR = new InetSocketAddress("10.13.1.218", 1080);
 static InetSocketAddress ALIVE_SOCKS_PROXY_ADDR = new InetSocketAddress("10.13.1.218", 1081);

 static LinkedList<Proxy> HTTP__PROXY_LIST = new LinkedList<Proxy>();
 static LinkedList<Proxy> SOCKS_PROXY_LIST = new LinkedList<Proxy>();
 
 static
 {
  HTTP__PROXY_LIST.add(new Proxy(Proxy.Type.HTTP,  DEAD__HTTP__PROXY_ADDR));
  HTTP__PROXY_LIST.add(new Proxy(Proxy.Type.HTTP,  ALIVE_HTTP__PROXY_ADDR));
  
  SOCKS_PROXY_LIST.add(new Proxy(Proxy.Type.SOCKS, DEAD__SOCKS_PROXY_ADDR));
  SOCKS_PROXY_LIST.add(new Proxy(Proxy.Type.SOCKS, ALIVE_SOCKS_PROXY_ADDR));
  
 }

 static class MyProxySelector extends ProxySelector
 {
  @Override
  public void connectFailed(URI uri, SocketAddress addr, IOException e)
  {
   System.err.println(uri + ", " + addr + " " + e);
  }

  @Override
  public List<Proxy> select(URI uri)
  {
   if ( uri.getScheme().equals("socket") )
    return SOCKS_PROXY_LIST;
   else
    return HTTP__PROXY_LIST;
  }
 }
 
 public static void testProxyAlive()
 {
  InetSocketAddress[] addrs = {
   DEAD__HTTP__PROXY_ADDR, ALIVE_HTTP__PROXY_ADDR, DEAD__SOCKS_PROXY_ADDR, ALIVE_SOCKS_PROXY_ADDR
  };
  
  for ( InetSocketAddress addr : addrs )
  {
   System.err.println("Connecting to " + addr + "...");
   Socket sock = null;
   try
   {
    sock = new Socket(Proxy.NO_PROXY);
    sock.connect(addr);
    System.err.println("\t proxy is alive.");
   }
   catch (Exception e)
   {
    System.err.println("\t proxy is dead. (" + e + ")");
   }
   finally
   {
    if ( sock != null ) try { sock.close(); } catch (Exception e) {}
   }
  }
 }
 
 public static void testHttp() throws IOException
 {
  URL url = new URL("http://www.oracle.com/index.html");
  ByteArrayOutputStream os = new ByteArrayOutputStream();
  InputStream is = null;
  
  try
  {
   is = (InputStream)url.getContent();
   byte[] buf = new byte[1024];
   for ( int nRead = 0 ; (nRead = is.read(buf)) != -1 ; )
   {
    os.write(buf, 0, nRead);
   }
  }
  finally
  {
   if ( is != null )
    is.close();
  }

  FileOutputStream fos = new FileOutputStream("index.http.html");
  fos.write(os.toByteArray());
  fos.close();
 }
 
 public static void testSocks() throws IOException
 {
  Socket sock = null;
  InputStream is = null;
  ByteArrayOutputStream os = new ByteArrayOutputStream();
  
  try
  {
   sock = new Socket();
   sock.connect(new InetSocketAddress("www.oracle.com", 80));
   sock.getOutputStream().write("GET /index.html HTTP/1.0\r\nHost: www.oracle.com\r\nUser-Agent: Mozilla/1.0\r\n\r\n".getBytes());
   sock.getOutputStream().flush();
   is = sock.getInputStream();
   byte[] buf = new byte[1024];
   for ( int nRead = 0 ; (nRead = is.read(buf)) != -1 ; )
   {
    os.write(buf, 0, nRead);
   }
  }
  finally
  {
   if ( is != null )
    is.close();
   if ( sock != null )
    sock.close();
  }
  
  FileOutputStream fos = new FileOutputStream("index.socks.html");
  fos.write(os.toByteArray());
  fos.close();
 }
 
 public static void main(String[] args)
 {
  System.err.println("* Test for Proxy Alive...");

  testProxyAlive();
  
  System.err.println();

  ProxySelector.setDefault(new MyProxySelector());
  
  try
  {
   System.err.println("* Test for HTTP Proxy...");
   testHttp();
   System.err.println("Success");
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
  
  System.err.println();
  
  try
  {
   System.err.println("* Test for SOCKS Proxy...");
   testSocks();
   System.err.println("Success");
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
 }
}


Java Regular Expression Package 의 버그 프로그래밍

가금 서버가 행걸려서 CPU 를 100% 다 잡아먹고 서비스가 안되는 현상이 종종 발생 하였는데

오늘 지인의 도움에 힘입어 jmap 과 jhat 을 이용해서 JVM 의 Heap 을 디버깅 해본 결과

스팸 차단을 위해 패턴에 등록한 regular expression 이 특정 검색 문자열을 만나면 라이브러리 내부에서 무한루프를 도는 현상을 찾았습니다.

물론 regular expression 자체가 좀 난해하게 만들어진 점이 없쟎아 있지만, 무한루프를 도는건 아무래도 문제가 있는 듯 싶네요, 유닉스에 있는 awk, grep 에서는 별 문제 없이 동작하더군요

오늘 잡힌 regular expression 과 입력 문자열은 다음과 같습니다.


------------------------------------------------------------------------------
- 정규식 : [wW]{0,3}\.*.*\.*.*\.[cC][^a-zA-Z0-9]{1,32}[nN]
- 문자열 : 니마........{엄청길게썼음972개}........나...... 안된다~^.^
------------------------------------------------------------------------------
- 정규식: [wW]{0,3}\.*.*\.*.*\.[cC]*%63[nN]*
- 문자열: 나........{엄청길게썼음928개}.........내일 사랑니수술 하러가..ㅠ.ㅠ
------------------------------------------------------------------------------


참고로 JDK 1.6 에서 지원되는 jmap, jhat 사용법을 간단하게 설명 드립니다.

jmap, jhat 은 런타인중인 JVM 의 힙의 내용을 살펴보고자 할 때 사용하고요, 메모리에 있는 대부분의 객체들을 일목 요연하게 볼 수 있습니다.


1. jmap 으로 힙 덤프 파일을 생성한다.

>$ jmap -dump:live,file=heap.bin <pid>

2. jhat 으로 힙 덤프 파일을 분석한다

>$ jhat heap.bin

이렇게 하면 분석이 끝나고 디폴트 7000포트로 웹서버가 하나 뜹니다.

3. 웹 브라우저로 http://<host>:7000/ 로 접속하여 결과를 확인한다.

http://<host>:7000/oql/ 로 접속하면 직접 SQL 과 유사한 OQL (Object Query Language) 를 이용하여 객체들을 뒤져볼 수 있습니다. 

 


Unicode Surrogate Area 캐릭터의 MySQL 입력시 에러 프로그래밍

자바데몬에서 한글을 입력시에 MySQL 에 Insert 하는 과정에서 아래와 같은 Exception 이 종종 발생 했는데.. 며칠 삽질한 끝에 원인을 찾았습니다.

Incorrect string value: '\xF0\x9F\x98\x8D\xF0\x9F...' for column 'NICKNAME' at row 1java.sql.SQLException: Incorrect string value: '\xF0\x9F\x98\x8D\xF0\x9F...' for column 'NICKNAME' at row 1
    com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1056)
    com.mysql.jdbc.SQLError.createSQLException(SQLError.java:957)
    com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3376)
    com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3308)
    com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1837)
    com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1961)
    com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2543)
    com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1737)
    com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2022)
    com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1940)
    com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1925)
    nateon.qsync.table.SyncStatementInfo.sync(SyncStatementInfo.java:100)
    nateon.qsync.table.TableDistribute.syncRecord(TableDistribute.java:118)
    nateon.qsync.QSyncEntry.run(QSyncManager.java:124)
   
문제는 Surrogate Area 라고 불리는 유니코드의 영억 때문인데요.
D800-DBFF, DC00-DFFF 이 영억에는 실제로 매핑된 캐릭터가 오는게 아니고, UTF-16 확장을 위해서 미리 잡아놓은 영역입니다.

JAVA String, Oracle NVARCHAR 는 UTF-16 입니다. (UCS-2 아님)
즉 UCS-4, UCS-8 도 수용이 가능하다는 얘기이지요.


그런데, MySQL의 유니코드 캐릭터셋은 아마도 UTF-16 이 아닌 UCS-2 를 그대로 사용하는 듯 합니다.
그래서 이 예비 영역의 글자가 들어오면 인식하지 못하고 에러를 뱉어 냅니다.

해결책은 아래 샘플 코드 처럼 스트링에서 Surrogate Area 에 있는 캐릭터를 제거하는 겁니다.

 public static String removeSurrogateArea(String val)
 {
  if ( val == null ) return null;
  
  StringBuffer buf = new StringBuffer();
  int len = val.length();
  for ( int i = 0 ; i < len ; i++ )
  {
   char c = val.charAt(i);
   if ( 0xD800 <= c && c <= 0xDBFF ||
     0xDC00 <= c && c <= 0xDFFF )
   {
   }
   else
   {
    buf.append(c);
   }
  }
  return buf.toString();
 }



아래는 wiki 에서 찾은 Surrogates 의 원문입니다.

http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates

SurrogatesThe 2,048 surrogates are not characters, but are reserved for use in UTF-16 to specify code points outside the Basic Multilingual Plane. They are divided into leading or "high surrogates" (D800-DBFF) and trailing or "low surrogates" (DC00-DFFF). In UTF-16, they must always appear in pairs, as a high surrogate followed by a low surrogate, thus using 32 bits to denote one code point.

10000 + (H - D800) × 400 + (L - DC00)
where H and L are the numeric values of the high and low surrogates respectively.

Since high surrogate values in the range DB80-DBFF always produce values in the Private Use planes, the high surrogate range can be further divided into (normal) high surrogates (D800-DB7F) and "high private use surrogates" (DB80-DBFF).


Linux /dev/random vs /dev/urandom 삽질 후기 프로그래밍

프로그래밍을 하다가 보면 가끔 난수를 발생시켜야 하는 때가 있습니다.

난수를 발생시키기 위해서 여러 가지 방법을 사용하는데 몇몇 가지 대표적인 예를 들어 보죠.

1. ANSI C 라이브러리의 rand() 함수를 이용한다.
2. 외부 라이브러리가 (apr 이나, openssl 같은...) 가 제공하는 random 함수들을 이용한다
3. 디바이스 파일을 이용한다 (/dev/random, /dev/urandom)
4. PRNGd 같은 데몬을 이용한다.

여기서 삽질을 겪었던 방법이 3번입니다.

암호화를 위한 아파치 모듈을 하나 만들었는데, 모듈에서 종종 난수를 발생하기 위해서 apr (apache portable runtime library) 에 있는 apr_generate_random_bytes() 라는 함수를 콜하도록 했었고요.

스테이지 테스트까지는 별 문제가 없었는데, 라이브 서버에 올렸더니 일정 시간이 지나면.. apache 가 hang 걸린것 처럼 멈춰 버리는 현상이 발생을 하더군요.

strace 로 system call 을 모니터링 해봤는데,

open("/dev/random", O_RDONLY) = 28
read(28, 0x37ef8824, 4) = ...

여기서 wating 을 하고 있는 것이 아닌가... 몇초도 아니고 몇십초를 대기하고 있었다. 대략 1~2분 정도를..
아파치 소스를 까봤더니 apr_generate_random_byte 내부에서 /dev/random 디바이스를 오픈해서 그냥 읽도록 되어 있었네 그랴.
근데 왜 /dev/random 을 읽다가 wating을 할까....???

man 에서 그 해답을 찾았습니다.

리눅스에서는 /dev/random 과 /dev/urandom 두개를 PRNG 디바이스로 제공을 하는데
방식은 IO 디바이스 드라이버에서 발생하는 입력 신호의 노이즈를 게더링 하여 비트수를 연산하여 난수를 발생시킨다고 하는군요.

/dev/random, /dev/urandom 두개의 차이점은 전자는 엔트로피(얼마나 고른 난수를 발생 시킬 수 있을지를 결정할 수 있는 값이라는군요)가 충분해 져야만 난수를 발생 시키기 때문에 보안에 강하지만 느리고요(심지어지는 엔트로피가 채워잴때까지 blocking 합니다.), 후자는 이 값이 충분하지 않아도 발생을 시키긴 하는데 보안에 취약하지만 빠릅니다.(엔트로피가 채워질때까지 blocking 하지 않습니다. 현재있는 앤트로피만 가지고 생성을 한답니다.)

여기서 보안에 강하다 취약하다는 말은.. 발생된 random 값으로 암호화 키를 생성하는데 사용할 경우, 이 키가 얼마나 깨기 어렵느냐 쉬우냐를 얘기합니다.

서버와 같은 머신에서는 PC 와는 다르게, 키보드나, 마우스와 같은 IO 디바이스가 별로 없기 때문에 일정한 수준의 앤트로피 풀을 만들어 내는데 많은 시간이 걸린다고 합니다.

그리고 /dev/random 은 머신 자체의 영향도 많이 받습니다. 라이브 서버가 5년 전에 들어온 리눅스 머신이었고 스테이지 서버가 들어온지 1년도 안된 새삥 머신이었죠.. /dev/random 에서 wating 하는 걸 두대에서 측정해 봤더니 스테이지 서버가 라이브 서버보다 상당히 빠르더군요..

그래서 결국은 apr 에서 제공하는 random 대신에 /dev/urandom 을 직접 오픈해서 읽는 걸로 바꾸었더니 행 걸리는 현상이 없이 잘 동작 하드랍니다.

참고로 FreeBSD 의 경우에는 /dev/random 은 Linux 의 /dev/urandom 과 같은 방식입니다.
FreeBSD 의 /dev 를 자세히보면 random 이 urandom 으로 symbolic link 가 걸려 있는걸 알 수 가 있습니다.


4년에 한번씩 발생했던 오라클 날짜연산의 문제 프로그래밍

오라클 스토어드 프로시져 중에 날짜 연산을 하기 위한 코드가 있었는데..

30 년전 오늘의 날짜를 구하는 연산이다.

SELECT SYSDATE - TO_YMINTERVAL('30-00') FROM DUAL

얼핏 보면 문제가 없어 보인다.. 그러나 SYSDATE 가 4년에 한번씩 오는 2월 29일 되면 오류가 발생한다.

1978-02-29 이라는 날짜는 존재 하지 않기 때문이다... (쉬트...)

그래서 아래와 같이 수정했다.

SELECT ADD_MONTHS(SYSDATE, -12*30) FROM DUAL


이렇게 연산을 하면 1978-02-28 이라는 결과가 나온다.

못 믿으시겠다고요? 그럼 아래 쿼리를 각각 실행해 보시라..

SELECT TO_DATE('2008-02-29', 'YYYY-MM-DD') + TO_YMINTERVAL('01-00') FROM DUAL

SELECT ADD_MONTHS(TO_DATE('2008-02-29', 'YYYY-MM-DD'), -12*30) FROM DUAL


이 현상은 9.2.0.4 에서 목격되었다.. 상위 버전을 써본적이 없어서 10g 나 11g 에서도 여전히 발생하는지는 모르겠다.

사실 이 문제는 4년전에도 똑같이 겪었다... 소 잃고 외양간도 못 고쳤다... 오늘 또 이런 오류를 겪었다니.. 쩝..


한글 텍스트 파일의 캐릭터셋을 자동으로 판단하는 프로그램 프로그래밍

소스 코드들이 많이 있는 디렉토리가 있다..

내가 작성하는 코드들에 중국어나 일본어가 있으리 만무하지만,

울트라 에디트로 작성한 파일을 많이 저장하다보니...

가끔 서로 다른 캐릭터 셋으로 만들어 놓는 경우가 종종 있다.

윈도에서는 주로

ASCII, EUC-KR, CP-949, UTF-8 등의 캐릭터 셋으로 저장하는데,

가끔 유니코드 텍스트 파일도 BOM 을 넣기도 하고 빼기도 하고 해서,

나도 울트라 에디트로 열어보기 전에는 어떤 캐릭터 셋으로 저장을 해놨는지 헤깔린다.

그래서 그 수많은 파일 중에서 UTF-8 으로 저장된 파을을 찾으려고 했더니 시간이 너무 많이 걸리더라.. ㅋㅋ

인터넷을 뒤져서 캐릭터셋을 자동으로 판별해 주는 코드를 입수해 보려고 노력을 했으나..

문득... 인터넷 뒤지는 시간이나 내가 만드는 시간이나 별반 차이가 없으리라 생각하고..

이틀간 위키를 뒤져가며 각 캐릭터셋의 인코딩 방식을 찾아서 뚝딱뚝딱 코딩을 했는데.. 생각보다 잘 동작 한다. ㅋㅋ

US-ASCII, EUC-KR, MS-949, UTF-8 형태로 된 인코딩을 자동으로 찾아서 판별해 준다..

물론 BOM 이 붙은 넘들은 BOM 만 가지고 판별을 하고 BOM 이 없는 넘들은 인코딩 오토마타의 간략버전으로 판별한다.

US-ASCII ⊂ EUC-KR ⊂ MS-949
US-ASCII ⊂ UTF-8

의 포함 관계는 대충 아시리라 보고..

유닉스 같은 데서는

find . -type f | xargs detectcharset

이런 식으로 하시면 대략 파일들의 캐릭터 셋을 아시리라

detectcharset.zip

트랙백이 뭘까?

Oracle 에서 DES, Triple DES 의 적용

트랙백을 보내면 원문과 링크가 연결된다고 한다..

첨 시도해 봄...

Oracle 에서 DES, Triple DES 의 적용 암호화

어제 영선 과장님이 오라클에서 Triple DES 로 암호화를 했는데, 자바로 암호화 한거랑 다르다는 거다

왜 다른지 물어보러 왔었는데, 알고보니 IV (Initial Vector) 값 때문이었다. DES, Triple DES, AES 류들은 모두 블럭 암호화 방식인데 이런 블럭 암호화 방식들은 암호화를 하기 위해서 암호화 키 말도고 필요한 몇가지가 있는데, 그런 것들을 파라미터라하고..

다음과 같은 파라미터를
  • Block Mode
  • Padding Mode
  • Initial Vector
  • Shift Bits
가지고 있다.

Block Mode

실 파라미터라기 보다는 알고리즘과 함쳐져서 하나의 알고리즘 이름 처럼 불리는 경우가 많다.
즉 예를 들어서
  • DES-ECB, DES-CBC, DES-CFB, DES-OFB
  • DES3-ECB, DES3-CBC, DES3-CFB, DES3-OFB
  • AES128-ECB, AES128-CBC, AES128-CFB, AES128-OFB
위와 같이 말이다... 블락 모드는 아래와 같다.
  • ECB : Eletric Code Book
  • CBC : Cipher Block Chain
  • OFB : Output Feed Back
  • CFB : Cipher Feed Back
Padding Mode

이건 블락 사이즈에 맞춰서 내용을 패딩할지 말지를 말하는데, PKCS#5 표준 패딩을 사용한다.

Initial Vector

ECB 모드를 제외한 나머지 알고리즘은 다음 블럭을 암호화 할 때 이전 블럭의 결과값 같은걸 XOR 하여 다시 한번 연산을 하는 방식인데, 이 경우 첫번째 블럭에서 이전 블럭의 결과값이 없기 때문에 임으로 암호화하는 쪽에서 값을 지정해야 한다, 암호화에서 사실 이 값은 노출이 되어도 별로 상관이 없다고 얘기를 한다.

Shift Bits

CFB 모드에서는 이전 블락을 연산할때 그냥 하는게 아니라 비트를 돌려서 하는 알고리즘을 가지고 있다. 그래서 그때 비트르 돌릴때 몇 비트식 돌릴지를 결정하는 파라미터 이다.

Oracle 에서도 암호화를 지원하는데 현재 DES, Triple DES 정도를 지원하고 있다. 그리고 도큐먼트에 특별히 다른 모드를 지정하는게 없는걸로 보아 CBC 모드 인것 같다.

아.. 더 작성할려 했는데 귀찮아 지려고 한다... 다음 번에 계속 해서...

1