'2009/05'에 해당되는 글 9건

Java UUID Generators :: 2009/05/27 18:41

UUID (또는 GUID)는 Universally Unique Identifier(또는 Globally Unique Identifier)의 약자로 프로그래밍에서 유일한 ID값을 얻기위해 종종 사용되어진다. 하나의 UUID는 16 바이트(128비트) 숫자이고, canonical 형태는 32개의 16진수 숫자와 이를 5개의 그룹(8-4-4-4-12)으로 나누는 4개의 하이픈(-)을 포함하여 총 36개의 문자열로 표현된다. UUID에는 다음과 같은 5가지 버전이 존재한다.
  • MAC address based (v1)
  • DCE Security based (v2)
  • Name based + MD5 hash (v3)
  • Random (v4)
  • Name based + SHA1 hash (v5)

일반적으로 UUID는 다양한 분산환경 머신에서 유일한 값이 필요할 때 좋은 선택이 될 수 있는데, 다음과 같이 다양한 경우 등에 사용되어 질 수 있다.
  • 임시파일 이름 생성
  • 웹싸이트 방문자에 대한 유일한 값(세션값 등) 생성
  • 트랜잭션 ID 생성
  • DB 시퀀스를 대신하는 Primary Key 생성

UUID 생성기는 여러 언어로 다양하게 구현되어 있다. 이곳에서 웹상으로 UUID를 생성해 볼 수 있다. 다음은 Java로 구현된 UUID 생성기들이다.

Random UUID (v4) 구현한 Java 소스
/*
 * RandomGUID
 * @version 1.2.1 11/05/02
 * @author Marc A. Mnich
 *
 * From www.JavaExchange.com, Open Software licensing
 *
 * 11/05/02 -- Performance enhancement from Mike Dubman.  
 *             Moved InetAddr.getLocal to static block.  Mike has measured
 *             a 10 fold improvement in run time.
 * 01/29/02 -- Bug fix: Improper seeding of nonsecure Random object
 *             caused duplicate GUIDs to be produced.  Random object
 *             is now only created once per JVM.
 * 01/19/02 -- Modified random seeding and added new constructor
 *             to allow secure random feature.
 * 01/14/02 -- Added random function seeding with JVM run time
 *
 */
import java.net.*;
import java.util.*;
import java.security.*;

/*
 * In the multitude of java GUID generators, I found none that
 * guaranteed randomness.  GUIDs are guaranteed to be globally unique
 * by using ethernet MACs, IP addresses, time elements, and sequential
 * numbers.  GUIDs are not expected to be random and most often are
 * easy/possible to guess given a sample from a given generator.
 * SQL Server, for example generates GUID that are unique but
 * sequencial within a given instance.
 *
 * GUIDs can be used as security devices to hide things such as
 * files within a filesystem where listings are unavailable (e.g. files
 * that are served up from a Web server with indexing turned off).
 * This may be desireable in cases where standard authentication is not
 * appropriate. In this scenario, the RandomGUIDs are used as directories.
 * Another example is the use of GUIDs for primary keys in a database
 * where you want to ensure that the keys are secret.  Random GUIDs can
 * then be used in a URL to prevent hackers (or users) from accessing
 * records by guessing or simply by incrementing sequential numbers.
 *
 * There are many other possiblities of using GUIDs in the realm of
 * security and encryption where the element of randomness is important.
 * This class was written for these purposes but can also be used as a
 * general purpose GUID generator as well.
 *
 * RandomGUID generates truly random GUIDs by using the system's
 * IP address (name/IP), system time in milliseconds (as an integer),
 * and a very large random number joined together in a single String
 * that is passed through an MD5 hash.  The IP address and system time
 * make the MD5 seed globally unique and the random number guarantees
 * that the generated GUIDs will have no discernable pattern and
 * cannot be guessed given any number of previously generated GUIDs.
 * It is generally not possible to access the seed information (IP, time,
 * random number) from the resulting GUIDs as the MD5 hash algorithm
 * provides one way encryption.
 *
 * ----> Security of RandomGUID: <-----
 * RandomGUID can be called one of two ways -- with the basic java Random
 * number generator or a cryptographically strong random generator
 * (SecureRandom).  The choice is offered because the secure random
 * generator takes about 3.5 times longer to generate its random numbers
 * and this performance hit may not be worth the added security
 * especially considering the basic generator is seeded with a
 * cryptographically strong random seed.
 *
 * Seeding the basic generator in this way effectively decouples
 * the random numbers from the time component making it virtually impossible
 * to predict the random number component even if one had absolute knowledge
 * of the System time.  Thanks to Ashutosh Narhari for the suggestion
 * of using the static method to prime the basic random generator.
 *
 * Using the secure random option, this class compies with the statistical
 * random number generator tests specified in FIPS 140-2, Security
 * Requirements for Cryptographic Modules, secition 4.9.1.
 *
 * I converted all the pieces of the seed to a String before handing
 * it over to the MD5 hash so that you could print it out to make
 * sure it contains the data you expect to see and to give a nice
 * warm fuzzy.  If you need better performance, you may want to stick
 * to byte[] arrays.
 *
 * I believe that it is important that the algorithm for
 * generating random GUIDs be open for inspection and modification.
 * This class is free for all uses.
 *
 *
 * - Marc
 */
public class RandomGUID extends Object {

    public String valueBeforeMD5 = "";
    public String valueAfterMD5 = "";
    private static Random myRand;
    private static SecureRandom mySecureRand;
    private static String s_id;

    /*
     * Static block to take care of one time secureRandom seed.
     * It takes a few seconds to initialize SecureRandom.  You might
     * want to consider removing this static block or replacing
     * it with a "time since first loaded" seed to reduce this time.
     * This block will run only once per JVM instance.
     */

    static {
        mySecureRand = new SecureRandom();
        long secureInitializer = mySecureRand.nextLong();
        myRand = new Random(secureInitializer);
        try {
            s_id = InetAddress.getLocalHost().toString();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

    /*
     * Default constructor.  With no specification of security option,
     * this constructor defaults to lower security, high performance.
     */
    public RandomGUID() {
        getRandomGUID(false);
    }

    /*
     * Constructor with security option.  Setting secure true
     * enables each random number generated to be cryptographically
     * strong.  Secure false defaults to the standard Random function seeded
     * with a single cryptographically strong random number.
     */
    public RandomGUID(boolean secure) {
        getRandomGUID(secure);
    }

    /*
     * Method to generate the random GUID
     */
    private void getRandomGUID(boolean secure) {
        MessageDigest md5 = null;
        StringBuffer sbValueBeforeMD5 = new StringBuffer();

        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            System.out.println("Error: " + e);
        }

        try {
            long time = System.currentTimeMillis();
            long rand = 0;

            if (secure) {
                rand = mySecureRand.nextLong();
            } else {
                rand = myRand.nextLong();
            }

            // This StringBuffer can be a long as you need; the MD5
            // hash will always return 128 bits.  You can change
            // the seed to include anything you want here.
            // You could even stream a file through the MD5 making
            // the odds of guessing it at least as great as that
            // of guessing the contents of the file!
            sbValueBeforeMD5.append(s_id);
            sbValueBeforeMD5.append(":");
            sbValueBeforeMD5.append(Long.toString(time));
            sbValueBeforeMD5.append(":");
            sbValueBeforeMD5.append(Long.toString(rand));

            valueBeforeMD5 = sbValueBeforeMD5.toString();
            md5.update(valueBeforeMD5.getBytes());

            byte[] array = md5.digest();
            StringBuffer sb = new StringBuffer();
            for (int j = 0; j < array.length; ++j) {
                int b = array[j] & 0xFF;
                if (b < 0x10) sb.append('0');
                sb.append(Integer.toHexString(b));
            }

            valueAfterMD5 = sb.toString();

        } catch (Exception e) {
            System.out.println("Error:" + e);
        }
    }

    /*
     * Convert to the standard format for GUID
     * (Useful for SQL Server UniqueIdentifiers, etc.)
     * Example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6
     */
    public String toString() {
        String raw = valueAfterMD5.toUpperCase();
        StringBuffer sb = new StringBuffer();
        sb.append(raw.substring(0, 8));
        sb.append("-");
        sb.append(raw.substring(8, 12));
        sb.append("-");
        sb.append(raw.substring(12, 16));
        sb.append("-");
        sb.append(raw.substring(16, 20));
        sb.append("-");
        sb.append(raw.substring(20));

        return sb.toString();
    }

    /*
     * Demonstraton and self test of class
     */
    public static void main(String args[]) {
        for (int i=0; i< 100; i++) {
            RandomGUID myGUID = new RandomGUID();
            System.out.println("Seeding String=" + myGUID.valueBeforeMD5);
            System.out.println("rawGUID=" + myGUID.valueAfterMD5);
            System.out.println("RandomGUID=" + myGUID.toString());
        }
    }
}

Hibernate SQL Dialects :: 2009/05/26 23:19

Hibernate는 가장 유명하고 일반적으로 사용되는 ORM(Object-Relational Mapping) 프레임워크 중 하나이다. 국내에서는 사용법이 다소 쉬운 iBATIS를 많이 쓰이는 편인데, iBATIS는 ORM보다는 OQM(Object-Query Mapping) 프레임워크라고 부르는게 맞을 것이다.

실제 DB를 설계하고 개발을 해본 개발자라면 왜 Hibernate를 사용해야 하는지 알 수 있을 것이다. 기존의 정규화가 잘안된 복잡한 레거시 DB에 사용할 경우에는 iBATIS와 같은 프레임워크를 사용하는 것이 더 유리할 것이다. 하지만 새로운 DB를 설계하고 프로젝트를 수행한다면 DB 엔티티 설계(Normalization 등) 및 도메인 객체에 대한 설계를 제대로 하여 Hibernate를 사용할 것을 권하고 싶다.

Hibernate는 dialect (방언?)라는 프로퍼티를 통해서 특정 DB를 사용하고 그에 맞는 문법을 지원하겠다고 지정할 수 있다. 아래와 같은 다양한 DB에 대한 dialect 프로퍼티 값을 지원한다.
RDBMS Dialect
DB2 org.hibernate.dialect.DB2Dialect
DB2 AS/400 org.hibernate.dialect.DB2400Dialect
DB2 OS390 org.hibernate.dialect.DB2390Dialect
PostgreSQL org.hibernate.dialect.PostgreSQLDialect
MySQL org.hibernate.dialect.MySQLDialect
MySQL with InnoDB org.hibernate.dialect.MySQLInnoDBDialect
MySQL with MyISAM org.hibernate.dialect.MySQLMyISAMDialect
Oracle (any version) org.hibernate.dialect.OracleDialect
Oracle 9i/10g org.hibernate.dialect.Oracle9Dialect
Sybase org.hibernate.dialect.SybaseDialect
Sybase Anywhere org.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Server org.hibernate.dialect.SQLServerDialect
SAP DB org.hibernate.dialect.SAPDBDialect
Informix org.hibernate.dialect.InformixDialect
HypersonicSQL org.hibernate.dialect.HSQLDialect
Ingres org.hibernate.dialect.IngresDialect
Progress org.hibernate.dialect.ProgressDialect
Mckoi SQL org.hibernate.dialect.MckoiDialect
Interbase org.hibernate.dialect.InterbaseDialect
Pointbase org.hibernate.dialect.PointbaseDialect
FrontBase org.hibernate.dialect.FrontbaseDialect
Firebird org.hibernate.dialect.FirebirdDialect

그런 사람 또 없습니다 :: 2009/05/26 00:16

그 분의 빈자리에 마음 한 구석에 허전함을 느낍니다. 싸이월드의 한 동영상입니다. (이승철의 노래가 흐릅니다).

Log4j.xml vs. Log4j.properties :: 2009/05/22 18:06

자바 프로그램의 로깅에서 사실상 표준의 위치에 있는 Log4J의 설정 파일은 프로퍼티 형태의 log4j.properties와 XML 형태의 log4j.xml이 있다. XML 버전이 조금 늦게 나왔지만 향후에는 XML 설정파일만 지원하게 될 것이라는 얘기도 있지만, 프로퍼티 설정파일이 Java의 deprecated 메소드가 여전히 남아있는 것처럼 아주 없어질 것 같지는 않다.

한 가지 유의할 점은 Log4J가 자동 설정파일을 찾을 때 클래스패스에서 log4j.xml 파일을 먼저 찾고, 이 xml 파일이 없을 경우에만 log4j.properties를 찾는다는 것이다. 이 두 설정파일의 샘플은 아래와 같다.

log4j.properties
# Log4J Properties
log4j.rootLogger=DEBUG, console, logfile

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern={%-5p} [%d] <%c> : %m%n

log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=logs/logfile.log
log4j.appender.logfile.Append=true
log4j.appender.logfile.MaxFileSize=1024KB
log4j.appender.logfile.MaxBackupIndex=5
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern={%-5p} [%d] <%c> : %m%n

log4j.logger.org.apache.catalina=WARN

log4j.logger.org.apache.commons=WARN

log4j.logger.org.springframework=INFO

log4j.logger.org.aspectj.weaver=INFO

# Log Level
# TRACE : 가장 상세한 정보를 나타낼 때 사용한다.
# DEBUG : 일반 정보를 상세히 나타낼 때 사용한다.
# INFO  : 일반 정보를 나타낼 때 사용한다.
# WARN  : 에러는 아니지만 주의할 필요가 있을 때 사용한다.
# ERROR : 일반 에러가 일어 났을 때 사용한다.
# FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용한다.

# Options
# %d : 로깅 이벤트가 일어난 날자(date)
# %p : 로깅 이벤트의 priority
# %t : 로깅 이벤트를 생성한 스레드 이름
# %c : 로깅 이벤트의 category
# %F : 로깅요청을 일으킨 파일 이름
# %L : 로깅요청을 일으킨 파일의 행번호
# %x : 로깅이벤트를 발생시킨 스레드에 관련된 내포검사항목
#      (Nested Diagnostic Context : NDC)을 출력
# %C : 로깅요청을 일으킨 호출자의 완전한 클래스이름
# %M : 로깅요청을 일으킨 메소드
# %m : 메세지
# %n : 플랫폼 독립적인 개행문자
# %l : 소스코드의 위치정보를 출력한다. %C. %M(%F:%L) 의 축약형

log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
    debug="false">

    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="{%-5p} [%d] <%c> : %m%n" />
        </layout>
    </appender>

    <appender name="logfile" class="org.apache.log4j.RollingFileAppender">
        <param name="Threshold" value="DEBUG" />
        <param name="Append" value="true" />
        <param name="MaxFileSize" value="1024KB" />
        <param name="MaxBackupIndex" value="5" />
        <param name="ImmediateFlush" value="true" />
        <param name="File" value="logs/logfile.log" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="{%-5p} [%d] <%c> : %m%n" />
        </layout>
    </appender>

    <logger name="org.apache.catalina">
        <level value="WARN" />
    </logger>

    <logger name="org.apache.commons">
        <level value="WARN" />
    </logger>

    <logger name="org.springframework">
        <level value="INFO" />
    </logger>

    <logger name="org.aspectj.weaver">
        <level value="INFO" />
    </logger>

    <root>
        <priority value="DEBUG" />
        <appender-ref ref="console" />
        <appender-ref ref="logfile" />
    </root>

</log4j:configuration>

아파치 Commons-logging을 이용한 Log4J 사용 예이다.
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

pubic class MyClass {

    static final Log log = LogFactory.getLog(MyClass.class);

    // ....

    log.debug("The value is null.");

}

SpringSource Tool Suite for Free! :: 2009/05/21 23:17

얼마 전 Eclipse 3.4.2 기반의 SpringSource Tool Suite (STS) 2.0.2 가 릴리즈 되었다. 스프링 기반의 어플리케이션 개발에 필요한 플러그인이나 기능 등을 모아 놓았고, 개발용으로 공짜로 사용할 수 있다. SpringSource dm Server를 통한 OSGi 런타임 환경 및 Spring Web Flow 등에 대한 지원과 Spring IDE, Maven 2.0, AspectJ Development Tools (AJDT), EclEmma 등의 플러그인들이 기본으로 포함되어 있다. 내가 즐겨쓰는 Subversive 플러그인은 빠져있다. SVN 클라이언트는 개발자들 간에 아직도 여러 버전을 사용해서 그런가? 아니면 라이센스 문제인가?

User inserted image

참조: http://www.springsource.com/products/sts

Tomcat + Ant 어플리케이션 배포 관리 :: 2009/05/18 12:42

Apache Tomcat은 어플리케이션 배포관리를 위한 Ant Tasks를 제공한다. 다음은 ANT 빌드를 사용하여 Tomcat 서버에 웹어플리케이션 배포를 관리하는 방법이다.

1. tomcatTasks.propertis
  - 프로퍼티 파일을 이용하여 Tomcat Ant Tasks를 정의할 경우에 사용한다. (XML로 정의할 경우 불필요).
# Tomcat Ant Taskdefs
deploy=org.apache.catalina.ant.DeployTask
reload=org.apache.catalina.ant.ReloadTask
undeploy=org.apache.catalina.ant.UndeployTask
list=org.apache.catalina.ant.ListTask
resources=org.apache.catalina.ant.ResourcesTask
roles=org.apache.catalina.ant.RolesTask
start=org.apache.catalina.ant.StartTask
stop=org.apache.catalina.ant.StopTask

2. tomcat.properties
   - Tomcat 서버 정보 및 배포할 어플리케이션 설정을 한다.
# JDK
java14.home=/java/j2sdk1.4.2
java15.home=/java/jdk1.5.0

# Tomcat Server
tomcat.home=/apache/tomcat-5.5.27
tomcat.server=localhost
tomcat.manager.url=http://${tomcat.server}:8080/manager
tomcat.username=admin
tomcat.password=admin

# Web App
webapp.name=myapp
webapp.war=/home/myapp/dist/war/myapp.war

3. tomcat-user.xml
  - Tomcat 서버에 어플리케이션 배포관리를 위해서는 manager 역할 권한을 갖는 사용자를 정의한다.
<tomcat-users>
  <role rolename="admin"/>
  <role rolename="manager"/>
  <user username="admin" password="admin" roles="admin,manager"/>
</tomcat-users>

4. build.xml
  - ANT 빌드 파일을 다음과 같이 작성한다.
  - tomcatTasks.properties를 이용하여 Tasks를 정의할 경우 상단 주석 처리된 부분을 사용한다.
  - 하단 주석 부분은 ANT로 Tomcat 서버를 시작/중지할 경우에 사용할 수 있다.
<?xml version="1.0" encoding="UTF-8"?>
<project name="tomcat" basedir="." default="">

    <property file="./tomcat.properties" />

    <!-- Taskdefs -->

    <!--
    <taskdef file="tomcatTasks.properties">
        <classpath>
            <pathelement path="${tomcat.home}/server/lib/catalina-ant.jar" />
        </classpath>
    </taskdef>
    -->

    <taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask">
        <classpath>
            <path location="${tomcat.home}/server/lib/catalina-ant.jar" />
        </classpath>
    </taskdef>

    <taskdef name="reload" classname="org.apache.catalina.ant.ReloadTask">
        <classpath>
            <path location="${tomcat.home}/server/lib/catalina-ant.jar" />
        </classpath>
    </taskdef>

    <taskdef name="undeploy" classname="org.apache.catalina.ant.UndeployTask">
        <classpath>
            <path location="${tomcat.home}/server/lib/catalina-ant.jar" />
        </classpath>
    </taskdef>

    <taskdef name="list" classname="org.apache.catalina.ant.ListTask">
        <classpath>
            <path location="${tomcat.home}/server/lib/catalina-ant.jar" />
        </classpath>
    </taskdef>

    <taskdef name="start" classname="org.apache.catalina.ant.StartTask">
        <classpath>
            <path location="${tomcat.home}/server/lib/catalina-ant.jar" />
        </classpath>
    </taskdef>

    <taskdef name="stop" classname="org.apache.catalina.ant.StopTask">
        <classpath>
            <path location="${tomcat.home}/server/lib/catalina-ant.jar" />
        </classpath>
    </taskdef>

    <!-- Tasks -->

    <target name="deploy" description="Deploy application in Tomcat">
        <deploy url="${tomcat.manager.url}"
                username="${tomcat.username}"
                password="${tomcat.password}"
                path="/${webapp.name}"
                war="file:${webapp.war}" />
    </target>

    <target name="reload" description="Reload application in Tomcat">
        <reload url="${tomcat.manager.url}"
                username="${tomcat.username}"
                password="${tomcat.password}"
                path="/${webapp.name}" />
    </target>

    <target name="undeploy" description="Undeploy application in Tomcat">
        <undeploy url="${tomcat.manager.url}"
                  username="${tomcat.username}"
                  password="${tomcat.password}"
                  path="/${webapp.name}" />
    </target>

    <target name="list" description="List Tomcat applications">
        <list url="${tomcat.manager.url}"
              username="${tomcat.username}"
              password="${tomcat.password}" />
    </target>

    <target name="start" description="Start Tomcat application">
        <start url="${tomcat.manager.url}"
               username="${tomcat.username}"
               password="${tomcat.password}"
               path="/${webapp.name}" />
    </target>

    <target name="stop" description="Stop Tomcat application">
        <stop url="${tomcat.manager.url}"
              username="${tomcat.username}"
              password="${tomcat.password}"
              path="/${webapp.name}" />
    </target>

    <!-- Tomcat Start/Stop -->
    <!--                  
    <target name="tomcat-start">
        <java jar="${tomcat.home}/bin/bootstrap.jar"
              jvm="${java15.home}/bin/java"
              fork="true">
            <jvmarg value="-Dcatalina.home=${tomcat.home}" />
        </java>
    </target>

    <target name="tomcat-stop">
        <java jar="${tomcat.home}/bin/bootstrap.jar" fork="true">
            <jvmarg value="-Dcatalina.home=${tomcat.home}" />
            <arg line="stop" />
        </java>
    </target>
    -->

</project>

참조: http://tomcat.apache.org/tomcat-5.5-doc/manager-howto.html

XML Signature Generation & Validation :: 2009/05/14 22:10

웹서비스의 메시지 레벨 보안 및 XML 문서 보안의 핵심에는 XML 전자서명XML 암호화가 있다. 웹서비스의 경우 송신 측에서는 메시지에 대한 전자서명을 생성하며, 수신 측에서는 이를 올바르게 검증을 해야 한다. XML 전자서명의 생성과 검증에 대해 간단히 정리해 본다.

XML Digital Signature (XML-DSIG)
  • 메시지가 전달 중 변경되지 않았음을 보장 (무결성)
  • 메시지 무결성을 유지하며 변경을 가하는 공격으로부터 보호 (메시지 인증)
  • 서명자에 대한 신뢰와 메시지 부인방지의 감시 수단 제공 (부인방지)

XML Signature Structure (XML 전자서명 구조)
  • 스키마: http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd
  • 네임스페이스: xmlns="http://www.w3.org/2000/09/xmldsig#"
  • XML 구조:
    <Signature> 
         <SignedInfo>
              <CanonicalizationMethod/>
              <SignatureMethod/>
              (<Reference URI? >
                   (<Transforms>)?
                   <DigestMethod>
                   <DigestValue>
              </Reference>)+
         </SignedInfo>
         <SignatureValue> 
         (<KeyInfo>)?
         (<Object Id?>)*
    </Signature>
    

Types of Signatures (전자서명 유형)
  • Enveloping Signature
    - 대상 데이터가 Signature 구조 안에 존재
    - XML 패이로드 안에 패키지화된 데이터의 전자서명에 좋음
    <?xml version="1.0"?>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
        <CanonicalizationMethod 
            Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
        <SignatureMethod
            Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>
        <Reference URI="#myobj">
          <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
          <DigestValue>C2g9BLcGyGPCVKuF2byR1Ym+6pE=</DigestValue>
        </Reference>
      </SignedInfo>
      <SignatureValue>+R/XEOHDvR/jbmmpiuH4ZcRqC6c=</SignatureValue>
      <Object Id="myobj">Hello World!</Object>
    </Signature>

  • Enveloped Signature
    - 대상 데이터가 밖에서 Signture 구조를 포함함
    - XML 문서 일부 또는 전체를 전자서명에 하는데 좋음
    <?xml version="1.0"?>
    <Envelope>
      <Data>content</Data>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
        <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>
        <Reference>
          <Transforms>
            <Transform
                Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
          </Transforms>
          <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
          <DigestValue>MMMkB0ZPp82XrUvJMFqDIEuXy0o=</DigestValue>
        </Reference>
      </SignedInfo>
      <SignatureValue>mVPvfcVSXi9elKL+IcSCAzD4Jbk=</SignatureValue>
    </Signature></Envelope>

  • Detached Signature
    - 대상 데이터가 밖에 있으며 Signture 구조를 포함하지 않음
    - URI 주소로 명시된 리모트 위치에 존재하는 데이터를 전자서명함
    <?xml version="1.0"?>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
        <CanonicalizationMethod
            Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        <SignatureMethod 
            Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>
        <Reference URI="http://www.example.net/content.txt">
          <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
          <DigestValue>oLZZOWcLwsAQ9NXWoLPk5FkPuSs=</DigestValue>
        </Reference>
      </SignedInfo>
      <SignatureValue>O9ykpFMXmkddzJ3CySrpzHBUW/Q=</SignatureValue>
    </Signature>

XML Signature Generation (XML 전자서명 생성)
  1. 대상 데이터에 Transforms 적용 (있을 경우)
  2. DigestValue 값 계산
  3. Reference 요소 생성
  4. 포함될 각 데이터에 대해서 1~3을 반복하여 Reference 생성
  5. SignatureMethod, CanonicalizationMethod와 Reference 요소들을 가지는 SignedInfo 요소 생성
  6. SignedInfo 요소에 대해 Canonicalization 수행
  7. Canonicalized SignedInfo에 대해 SignatureMethod에 기반한 SignatureValue 값 계산
  8. Signature 요소를 조립하여 생성

XML Signature Validation (XML 전자서명 검증)
  1. 전자서명 검증 키 정보(KeyInfo)를 얻음
  2. SignedInfo 요소에 CanonicalizationMethod를 적용
  3. Canonicalized SignedInfo에 대해 SignatureMethod를 적용하여 SignatureValue 값 검증
  4. SignedInfo 안에 있는 각 Reference에 대해서 다음 5~7을 반복해서 수행
  5. Reference에 참조되는 Digest 대상 데이터를 얻음
  6. DigestMethod를 데이터를 Digest 값 계산
  7. 계산된 Digest 값과 DigestValue 요소의 디코딩된 값을 비교하여 검증


SOAP Request
  - 보안이 적용이 안된 경우


SOAP Request with WS-Security (Signature)
  - SOAP Body 전체를 전자서명 한 경우


  - SOAP Header의 보안 헤더에 포함된 Signature 요소
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="Signature-867695">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<ds:Reference URI="#id-927929">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ds:DigestValue>O95oswj5A+ih7qTYDqAHRvyzC3c=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
IGA2hS7CDye7LizYZR3371OyiwYvvRcG4WZ6TnXHsxKCUah4BTbD4+DvlGwtmGf9kx3eRDcgvim6
PcoJ3uMK5q9c8ICKGFMSadk9jvPZDZ+R+VoMwt+0aJAI/MkhzK74IBhD7z5SQ7/xfMwlYafi28Tw
MjiqYdB+/gzurlFWQPM=
</ds:SignatureValue>
<ds:KeyInfo Id="KeyId-3874052">
......
</ds:KeyInfo>
</ds:Signature>

  - Reference의 URI에 의해 참조되는 SOAP Body 데이터 --> DigestValue 값 생성 (Base64 인코딩)
Stream Output:

<soapenv:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-ws
security-utility-1.0.xsd" wsu:Id="id-927929"><RequestMessage xmlns="http://www.java
2go.net/request"><RequestHeader><TotalCount>1</TotalCount></RequestHeader><RequestB
ody><RequestDataList><GetCertRequestData><CertId>343602C9666268C3297A</CertId></Get
CertRequestData></RequestDataList></RequestBody></RequestMessage></soapenv:Body> Pre-digest Input: <soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
wsu:Id="id-927929"><RequestMessage xmlns="http://www.java2go.net/request"><Request
Header><TotalCount>1</TotalCount></RequestHeader><RequestBody><RequestDataList><Get
CertRequestData><CertId>343602C9666268C3297A</CertId></GetCertRequestData></Request
DataList></RequestBody></RequestMessage></soapenv:Body>

  -  Canonicalized SignedInfo 데이터 --> SignatureValue 값 생성 (Base64 인코딩)
Stream Output:

<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\" />
<ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\" />
<ds:Reference URI=\"#id-927929\">
<ds:Transforms>
<ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\" />
</ds:Transforms>
<ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\" />
<ds:DigestValue>O95oswj5A+ih7qTYDqAHRvyzC3c=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>

Canonicalized SignedInfo:

<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></d
s:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:Si
gnatureMethod>
<ds:Reference URI="#id-927929"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMet
hod>
<ds:DigestValue>6oT2WXem4gq2V/+bQWzoOQ+wEJI=</ds:DigestValue> </ds:Reference> </ds:SignedInfo>

MAJOR.MINOR.PATCH :: 2009/05/07 22:48

하나의 소프트웨어는 라이프 싸이클 동안 꾸준한 변화를 가지게 된다. 이렇게 변화를 거치때마다 부여받는 것이 버전 번호이다. 많은 자바 프로그램들은 재사용을 목적으로 라이브러리 형태로 개발되어 지는데,  이런 라이브러리는 사용자에게 안정적인 API를 제공해야 할 뿐만 아니라 지속적으로 발전(변화)을 해나가야 할 양면의 필요성을 갖는다. 이 두가지의 균형을 맞추기 위해서 버전닝의 엄격한 정책이 필요하고, 그 정책을 통해 사용자는 한 버전에서 다음 버전으로의 바뀔 때 발생하는 제한점이나 변경사항 등을 이해할 수 있다.

버전 관리가 필요한 자바 라이브러리들을 개발하고 운영 중이라면 Apache Portable Runtime Project의 Versioning Guidelines을 준수하도록 노력해보자. 다음은 그 기본 골자이다.

"버전은 MAJOR.MINOR.PATCH의 전형적인 세쌍의 정수를 이용하여 표시된다. 기본적인 의도는 MAJOR 버전은 API에 호환되지 않는 대규모의 업그레이드를 수행한 것이며, MINOR 버전은 이전 마이너 버전과의 소스 및 바이너리 호환성은 유지한 것이고, PATCH 수준의 변경은 상하위 호환성을 완벽하게 유지한 버전이다."

"Versions are denoted using a standard triplet of integers: MAJOR.MINOR.PATCH. The basic intent is that MAJOR versions are incompatible, large-scale upgrades of the API. MINOR versions retain source and binary compatibility with older minor versions, and changes in the PATCH level are perfectly compatible, forwards and backwards."

하지만 1.0.0 버전에 도달하지 않은 라이브러리는 이러한 가이드라인에 종속되지 않음에 주의하자. 1.0 버전 전(버전 0.x.y)에서 API는 버전 가이드라인의 제약에 상관없이 자유롭게 변경되어질 수 있다.

한편 패키지 소프트웨어인 경우에는 추가로 네번째 숫자를 더해서 빌드(Build) 번호를 부여하기도 한다.

참조: http://apr.apache.org/versioning.html

Exclusive XML Canonicalization :: 2009/05/06 18:29

XML 데이터에 대한 표준 형태인 Canonical XML은 한 XML 안의 서브다큐먼트(또는 엘리먼트)에 적용되어 질 때, 부모의 모든 네임스페이스 선언들과 "xml:" 네임스페이스의 속성들를 포함해서 XML 직렬화를 한다.

그러나 일부 어플리케이션에서 좀 더 실용적면에서 canonicalized 서브다큐먼트로부터 부모 컨텍스트를 제거하는 방식을 요구하게 되었는데, 이것은 Exclusive XML Canonicalization에 의해 규정되어 있다. 예를 들면, XML 메시지 안의 XML payload (서브다큐먼트)에 대한 전자서명을 할 경우 해당 서브다큐먼트로가 부모 컨텍스트에 따라 달리 표현되지 않도록 할 수 있다.


Canonical XML (XML-C14N)
 
  http://www.w3.org/TR/2001/REC-xml-c14n-20010315#
  http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments

  • XML declaration and DTD are removed
  • The document is encoded in UTF-8
  • Line breaks normalized to #xA (linefeed) on input, before parsing
  • Empty elements are converted to start-end tag pairs
  • Whitespace outside of the document element and within start and end tags is normalized
  • Attribute value delimiters are set to double quotes
  • Superfluous namespace declarations are removed from each element
  • Lexicographic order is imposed on the namespace declarations and attributes of each element

Exclusive XML Canonicalization

  http://www.w3.org/2001/10/xml-exc-c14n#
  http://www.w3.org/2001/10/xml-exc-c14n#WithComments

  • Follows the same rules as Canonical XML, except…
  • Attributes in the xml namespace are not imported into orphan nodes
  • Namespaces not specially told to be added are only added on the starting element for which they are visible and not currently in scope within the output.

Inclusive vs. Exclusive Canonical XML Examples

1. 샘플 XML 문서
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="doc.xsl" type="text/xsl" ?>
<doc xmlns:d="http://www.example.org/d"
      xmlns:c="http://www.example.org/c" xml:lang="en">
  <e1 a:attr="out" b:attr="sorted" attr2="all" attr="I'm"
    xmlns:b="http://www.ietf.org" xmlns:a="http://www.w3.org"
    xmlns="http://example.org"/>
  <d:e2 xmlns="" xmlns:a="http://www.w3.org">
    <e3 xmlns="" xmlns:a="http://www.w3.org"/>
  </d:e2>
</doc>

2. XML 다큐먼트 전체에 대한 Canonical XML 결과
<?xml-stylesheet href="doc.xsl" type="text/xsl" ?>
<doc xmlns:c="http://www.example.org/c" xmlns:d="http://www.example.org/d" \
	xml:lang="en">
  <e1 xmlns="http://example.org" xmlns:a="http://www.w3.org" \
	xmlns:b="http://www.ietf.org" attr="I'm" attr2="all" b:attr="sorted" \
	a:attr="out"></e1>
  <d:e2 xmlns:a="http://www.w3.org">
    <e3></e3>
  </d:e2>
</doc>

3. 서브다큐먼트(요소 e2)에 대한 Canonical XML 결과
  - 부모의 네임스페이스와 xml: 속성을 포함한다.
  - XPath Nodeset 표현: (//. | //@* | //namespace::*)[ancestor-or-self::d:e2]
<d:e2 xmlns:a="http://www.w3.org" xmlns:c="http://www.example.org/c" \
	xmlns:d="http://www.example.org/d" xml:lang="en">
    <e3></e3>
  </d:e2>

4. 서브다큐먼트(요소 e2)에 대한 Exclusive Canonical XML 결과
  - 부모의 네임스페이스를 포함하지 않는다.
<d:e2 xmlns:d="http://www.example.org/d">
    <e3></e3>
  </d:e2>

참고: XML Canonicalization