검색

레이블이 Security인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Security인 게시물을 표시합니다. 모든 게시물 표시

2022년 1월 11일

SpotBugs Maven Plugin 을 이용한 Maven 프로젝트 정적 보안 취약점 분석

FindSecurityBugs

자바 웹 어플리케이션과 안드로이드 애플리케이션에 대한 보안 감사를 지원하는 StopBugs 플러그인이다. Command Injection, XPath Injection, SQL/HQL Injection, XXE 및 Cryptography 취약점을 포함하여 128개의 다른 취약점 유형을 탐지할 수 있다.


Spotbugs 는 AVA 언어를 대상으로 하지만 Groovy, Scala 및 Kotlin 에서도 작동하는 정적 분석도구이다.

Development 환경

HW : MacBook Pro 2019

  • 2.3Ghz 8Core Intel Corei9
  • 16GB 2667 Mhz DDR4
  • SSD 1TB
  • OS : macOS Monterey 12.0.1

SW :

  • JDK 14.0.2
  • Visual Studio Code 1.63.2
  • Maven 3.8.4

Maven

적용하려는 Maven 프로젝트  pom.xml 파일을 아래와 같이 ① SpotBugs Maven Plugin 을 설정을 추가한다.


	
    
	
		
			
				org.apache.maven.plugins
				maven-compiler-plugin
				2.3.2
				
					${javac.src.version}
					${javac.target.version}
					${project.build.sourceEncoding}
				
			
			
			
	            com.github.spotbugs
	            spotbugs-maven-plugin
	            4.5.0.0
	            
	                spotbugs-security-include.xml
	                spotbugs-security-exclude.xml
	                
	                    
	                        com.h3xstream.findsecbugs
	                        findsecbugs-plugin
	                        1.10.1
	                    
	                
	            
        	 
        	
		
	


다음으로 아래 ② 필터 파일 추가한다. ( 세부 설정은 매뉴얼 을 참고. 따로 설정하여 테스트 하지 않음)
  • spotbugs-security-include.xml
  • spotbugs-security-exclude.xml

spotbugs-security-exclude.xml





spotbugs-security-include.xml


    
        
    


취약점 검사 및 결과 확인

이제 다음 명령을 사용하여 취약점을 검사하고 결과를 보도록 하자.

mvn compile
mvn spotbugs:spotbugs

spotbugs:spotbugs 는 컴파일된 코드(target/classes)를 요구하기 때문에 반듯이 컴파일 이후에 실행해야한다.


이메 마지막으로 분석 결과를 확인해보자. 결과 확인인 아래와 같은 명령을 실행하여 GUI 도구를 통하여 확인할 수 있다.

mvn spotbugs:gui


GUI 도구는 검사된 보안  취약점 사항에 대한 결과를 아래와 같이 보여 준다. 영어로 되어 있지만 보안에 관한 기본적인 이해가 있다면 어렵지는 않다.



참고자료


2020년 7월 26일

JWT ( JSON WEB TOKEN ) 인증환경에서 이미지 다운로드

서버 세션이 아닌 JWT 토큰을 기반으로 하는 웹 프로그램은 서버와의 통신이 필요하면 HTTP 헤더에 JWT 토큰을 포함하여  요청하고 서버는 클라이언트 요청 헤더의  JWT 토큰을 꺼내어 인증 정보를 꺼네어 사용하게 된다. 이미지의 경우는 어떻까 ? 

HTML 에서 img  태그는 JWT 와 같은 보안 토큰을 헤더에 포함하는 기능을 제공하지 않는다. 그러나 이미지 데이터를 스크립트에서 다운로드하고  data : image / FILETYPE; base64 URI 체계를 사용하여 이미지를 웹 페이지에 포함시키는 방법을 사용하면 간단하게 제한을 극복할 수 있다. ( vue-auth-image 모듈이나 관련 글들을 참고하는 것이 많은 도움이 되었다.) 



다수의 이미지를 다운로드 하는 경우 요청간 지연을 주기 위하여 아래와 같은 방법을 사용했다.
  var delay = 600 ;
  $("#grid figure img[is-secure]").each(function () {
    var $this = $(this);
    delay = delay + 50 ;
    $this.parent().addClass('is-loading');
    studio.ui.setSecureImage($this, delay);
  });
  
이미지를 가져오지 못하는 경우에는 재요청을 수행하도록 코드하였다.
  
  function setSecureImage(elemment, delay, callback) {
  var delay = delay || 1000;
  let url = elemment.attr("secure-image");
  setImageAsBase64(
    url,
    function (data) {
      elemment.attr("src", data);
      elemment.parent().removeClass("is-loading");
      if (isFunction(callback)) callback();
    },
    function () {
      let maxRetry = 3,
        retry = 0;
      if (defined(elemment.data("max-retry"))) {
        maxRetry = elemment.data("max-retry");
      }
      if (defined(elemment.data("retry"))) {
        retry = elemment.data("retry");
      }
      if (retry < maxRetry) {
        elemment.data("retry", retry + 1);
        sleep(delay);
        setSecureImage(elemment);
      } else {
        elemment.parent().removeClass("is-loading");
      }
    }
  );
  }

  async function setImageAsBase64(url, successHandler, errorHandler) {
  const headers = {};
  // JWT 토큰 값 설정
  Object.assign(headers, studio.services.accounts.authHeader()); 
  await axios
    .get(url, { headers: headers, responseType: "arraybuffer" })
    .then((response) => {
      var mineType = response.headers["content-type"].toLowerCase();
      var b64encoded = Buffer.from(response.data, "binary").toString("base64");
      var prefix = "data:" + mineType + ";base64,";
      if (isFunction(successHandler)) successHandler(prefix + b64encoded);
    })
    .then((error) => {
      if (isFunction(errorHandler)) errorHandler();
    });
  } 
  



아쉽지만 이미지를 다운로드하여 data 방식으로 설정하는데 있어 오류가 발생되어 다시 시도하도록 추가한 코드는 효용성이 없는 것 같다. 테스트 환경 이슈일 수도 있으나 다운로드에 있어 무작위로 오류가 발생하며 일단 오류가 발생되면 다시 시도한 경우 역시 오류가 발생한다. (→ 서버 이슈인지 코드 이슈인지는 확실하지 않다.)

참고자료

2017년 12월 1일

스프링 시큐리티(Spring Security) - 도메인 객체 보안 (Domain Object Security ACL) 활용하기

대부분의  스프링 시큐리티를 사용하는 웹 응용프로그램들은  누가(Who)  어떤 URL 또는 어떤 메소드(Where)에 대한 호출에 대한 접근을 제안하는 방법으로 스프링 시큐리티를 사용하고 있다.


이런 접근 방식에서는 롤(ROLE)를 정의하고 사용자에게 롤(ROLE) 부여하고 특정 URL 또는 특정 클래스 함수에 대하여 ROLE 에 따른 접근제어를 하는 것으로 권한 관리를 구현하게 된다.

그러나 웹 프로그램들은 우리가 생각하는 이상으로 복잡하기 때문에 누가(Who) , 어디를 (Where) 뿐 아니라 무엇을 (What) 포함하여 권한을 설정할 수 있어야 한다.

예를 들어 아래와 게시판 프로그램을 디자인 한다고 가정해보자.

  • REQ1: Q&A게시판(B1)은 누구나(U1) 읽기(P1) / 쓰기(P2)가 가능하고 , 
  • REQ2: 자료실 게시판(B2)은 누구나(U1) 읽기(P1)는 가능하지만 관리자(U2)만 쓰기(P2)가 가능하고,  
  • REQ3: 고객지원 게시판(B3) 은 지정된 특정 사용자(U3) 만 읽기(P1) / 쓰기(P2)가 가능해야 한다.

누가(Who) , 어디를 (Where) 정보를 사용하여 권한을 결정하는 방식에서는 위의 기능을 구현하게 위하여 각기 다른 URL 또는 함수들 만들고 REQ3 요구사항을 위하여 추가로 권한 검사를 위하여 응용 프로그램 레벨에서 다시 권한결정을 위한  하드코딩이 필요하게 된다.

(Who) , 어디를 (Where) 뿐 아니라 무엇을 (What) 포함하여 권한을 결정하는 접근 방식에서는 게시판 객체 (What)에대한 권한을 설정할 수 있기 때문에 응용 프로그램 레벨에서의 하드 코딩 없이 구현이 가능하다. 이를 위하여 스프링 시큐리티는 도메인 객체 보안 (ACL) 서비스를 제공하고 있다.

스프링 시큐리티 ACL 사용하기 

도메인 객체 보안 (ACL) 서비스는   spring-security-acl-xxx.jar  라이브러리를 통하여 제공된다.  Spring 기반 웹 프로그램 개발 Part 2 - SpringSecurity 사용하기 와 같이 pom.xml 파일을 기술하였다면 관련 라이브러리는 포함되어 있다. (spring-security-taglibs 가 spring-security-acl 에 대한 의존성을 가지고 있기 때문에 자동으로 포함된다.)


    org.springframework.security
    spring-security-core
    ${project.dependency.spring-security.version}
    compile
  

    org.springframework.security
    spring-security-web
    ${project.dependency.spring-security.version}
    compile


    org.springframework.security
    spring-security-config
    ${project.dependency.spring-security.version}


    org.springframework.security
    spring-security-test
    ${project.dependency.spring-security.version}
    test  



또는 직접 다음과 같이 spring-security-acl 라이브러리를 추가해도 된다.


    org.springframework.security
    spring-security-core
    4.1.3.RELEASE
    compile
  

    org.springframework.security
    spring-security-web
    4.1.3.RELEASE
    compile


    org.springframework.security
    spring-security-config
    4.1.3.RELEASE


    org.springframework.security
    spring-security-acl
    4.1.3.RELEASE
    test  


다음으로 도메인 객체 보안 (ACL) 서비스를 사용하려면 ACL 정보를 어딘가에 저장해야 한다. 이를 위하여 ACL 데이터를 저장할 데이터베이스와 관련 테이블 생성이 필요하다. 테이블 생성은 spring-security-acl-xxx.jar 에 포함된 sql 파일을 이용하면 된다. ( mysql, oracle, postgres, SqlServer 에 대항하는 스크립트가 별도로 지원)



다음은 스크립트를 통하여 생성되는 테이블에 대한 다이어그램이다.



  • ACL_SID : 롤, 사용자에 대한 키 정보가 저장되는 테이블이다. ID 는 유니크한 숫자값, PRINCIPAL 는 롤의 경우는 0 사용자의 경우는 1 , SID 는 사용자 아이디 또는 롤 정보를 의미한다. 
  • ACL_CLASS : 도메인 객체 클래스에 대한 정보가 저장되는 테이블이다. ID 는 유니크한 숫자값, CLASS 는 클래스 이름을 의미한다. 
  • ACL_OBJECT_IDENTITY : 도메인 객체 인스턴스 정보가 저장되는 테이블이다. ID 는 유니크한 숫자값, OBJECT_ID_CLASS 는 클래스에 해당하는 ACL_CLASS.ID, OWNER_SID 는 생성자를 나타내는 ACL_SID.ID를 의미한다. 
  • ACL_ENTRY : 접근 권한 데이터가 저장되는 테이블이다. ID 는 유니크한 숫자값, OBJECT_ID_CLASS 는 클래스에 해당하는 ACL_CLASS.ID, OBJECT_ID_IDENTITY 는 객체 인스턴스를 나타내는 ACL_OBJECT_IDENTITY.ID , SID 는 권한이 부여된 대상을 의미하는 ACL_SID.ID, MASK 는 권한을 의미하는 마스크 값이다. 기본적으로 부여되는 모든 권한들은 유니크한 마스크 값을 갖는다.

예를 들어 dhson 사용자에게 ID 값이 9 인 architecture.community.board.Board 객체에 마스크 값이 1에 해당하는 READ 권한을 부여한다고 하면 다음과 같은 데이터들이 저장된다.

ACL_SID 
ID : 1, PRINCIPAL: 1, SID:dhson

ACL_CLASS
ID: 1, CLASS: architecture.community.board.Board

ACL_OBJECT_IDENTITY
ID: 1, OBJECT_ID_CLASS: 1, OBJECT_ID_IDENTITY: 9

ACL_ENTRY
ID: 1, ACL_OBJECT_IDENTITY: 1, SID: 1, MASH : 1

이제 아래와 같이 도메인 객체 보안 (ACL) 서비스 사용을 위하여 객체들을 **-context.xml 파일에 기술한다.

permissionSubsystemContext.xml


 Spring Security Domain Object ACL 설정

 
        
        
  
 
 
 
  
 
 
 
  
  
  
  
  
 
 
 
  
  
  
  
 
 
 
  
  
 
 
  
   
  
  
 
 
  
 
  
  
 
  
     
 



이제 자바 클래스에서 communityAclService 를 직접 사용하여 권한을 확인 하거나 @PreAuthorize 어노테이션을 사용하여 손쉽게 권한 제어가 가능하다.


  @PreAuthorize("hasPermission(#message, write) or hasPermission(#message, admin)")
  public void editMessage(Message message) { ... }


또는 아래와 같은 어노테이션도 가능하다.

  @PreAuthorize("hasPermission(#boardId, 'architecture.community.board.Board', 'READ')")
  @RequestMapping(value = {"/{boardId:[\\p{Digit}]+}/list"}, method = { RequestMethod.POST, RequestMethod.GET } )
  public String displayThreadList ( @PathVariable Long boardId,  HttpServletRequest request, HttpServletResponse response,  Model model) throws BoardNotFoundException{  
    / ***
  }


2017년 6월 10일

Spring Security - Login 시 사용자 정의 입력 값 전달하기

SpringSecurity 기반의 인증을 사용하는 환경에서 username 과 password 이외의 값을 추가로 전달하여 사용자를 인증을 할 수 없을까? (예를들어 회사번호, 아이디, 비밀번호를 전달받아 인증을 하는 경우 )

환경

Spring 4.3.1.RELEASE
Spring Security 4.1.2.RELEASE

Stack overflow 및 몇가지 자료를 참고하면 아래와 같이 구현을 한면 되다고 한다.

How to pass an additional parameter with spring security login page
  1. UsernamePasswordAuthenticationFilter 를 확장하여 사용자 정의 필터를 구현.
  2. 사용자 정의 UserDetailsService 구현.
  3. <http/> 설정에 사용자 정의 필터를 추가
아쉽게도  SpringSecurity 4.1 부터는 내부 구현 로직을 변경으로 UsernamePasswordAuthenticationFilter 를 위와 같은 방식으로 사용자 정의 필터로 변경할 수 없다.

SpringSecurity 4.1 이상의 환경에서는 필터를 수정하지 않고 RequestContextFilter 를 사용하여 사용자 정의 AuthenticationProvider 구현체에서  RequestContextHolder 를 사용하여 로그인시에 전달된 추가적인 파라메터 값을 꺼내어 원하는 방법으로 인증을 구현하면 된다.

RequestContextFilter 필터는 web.xml 에 필터 설정을 추가하여 사용할 수 있다.

WEB-INF/web.xml

 
 
   requestContextFilter
   org.springframework.web.filter.RequestContextFilter
 
 
   requestContextFilter
   /*
      
 
 
 
  springSecurityFilterChain
  org.springframework.web.filter.DelegatingFilterProxy
 
 
  springSecurityFilterChain
  /*
 


또는 Spring Security 설정 컨텍스트 ( 문서에서는 WEB-INF/context-config/securitySubsystemContext.xml 사용함 ) 에 아래와 같이 필터를 추가한다.

WEB-INF/context-config/securitySubsystemContext.xml
     
            
    
    
    


사용자 정의 인증 구현에서는 아래와 같이 RequestContextHolder 클래스를 사용하여 클라이언트에서 전달된 파라메터 값을 접근할 수 있다.

    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();  
    if( attr!=null && attr.getRequest() != null ) {
        long companyId = ParamUtils.getLongParameter(attr.getRequest(), "companyId", 0L);
        return super.retrieveUser(companyId, username, authentication);
    }else{  
        return super.retrieveUser(username, authentication);
    }



참고자료
Spring 기반 웹 프로그램 개발 Part 2 - SpringSecurity 사용하기

2017년 2월 15일

자바에서 새로운 암호화 프로바이더 사용하기

자바 암호화 프레임워크 

자바에서는 보안관련하여 JCA (Java Cryptography Architecture) 와 JCE (Java Cryptography Extension) 구조를 갖는다. 


  • JCA는 java.security 패키지에 포함되어 있으며 , 암호화 처리를 위한 표둔 구조, 인터페이스 , 팩토리 등을 정의한다. 
  • JCE는 javax.crypto 패키지에 포함되어 있으며 암/복호화 , 키 생성, 키 교환 , MAC 알고리즘등 실질적인 암호화 구현을 제공한다. 


 JCA 와 JCE 로 나누워 져있는 것은 자바 초기에 특정 암호화 알고리즘이 북미 외부로 수출되는 것이 제한되기 때문이다. 이런 이유에서 수출이 제한된 암호 알고리즘들은 JCA의 확장 패키지인 JCE 라는 형태로 제공되었다.  자바 1.4 버전 부터는 이러한 제한이 완화되면서 JDK에 포함되어 제공되고 있다.

 JCA 는 프로바이더 구조에 기반을 두고 있다. 모든 프로바이더는 java.security.Provider 클래스의 구현체로 CSP (Cryptographic Service Provider) 라고 부른다. 각 CSP 는 프로바이더의 이름과 구현한 보안 서비스 및 알고리즘 목록을 포함하고 있다. 사용자가 특정 알고리즘의 인스턴스를 요청하면 JCA 는 등록된 프로바이더중에서 구현 알고리즘에 해당하는 구현체를 찾아 인스턴스를 리턴한다.

그림1. 응용프로그램은 어떻게 "ACE" 암호화 인스턴스를 생성하는가


현재 우리가 사용하는 자바에는 다양한 프로바이더들이 기본으로 포함되어 있으며, 이들 프로바이더 목록은 java.security 파일에 기술되어 있다.

<JAVA_HOME>/jre/lib/security/java.security
<JRE_HOME>/jre/lib/security/java.security

그림 2. java.security 파일

프로바이더 추가

만일 "Cryptix"라는 새로운 프로바이더를 추가하여 사용하고 싶다면 어떤 방법을 있을까 ?

코드에서 다음과 같이 간단하게 프로바이더를 추가하여 사용할 수 있다. 

boolean hasCryptix = false;
for( java.security.Provider p : java.security.Security.getProviders()){
if("Cryptix".equals(p.getName())){
hasCryptix = true;
}
}
if( !hasCryptix ){
java.security.Security.addProvider( new cryptix.provider.Cryptix() );
}


또는 앞에서 언급한 java.security 파일에 프로바이더를 추가하여 사용할 수 있다.

security.provider.11=cryptix.provider.Cryptix  




참고자료



2012년 2월 16일

AES 암호화



AES 소개
1997년 1월에, 기존의 데이터 암호 표준, 즉 DES를 대체할 보다 강력한 알고리즘을 찾기 위한 공모 작업이 미국 상무부의 한 기관인 표준기술연구소(NIST)에 의해 시작되었다. 새로운 알고리즘이 충족해야 할 규격 요건으로는, 최소 128 비트나 192 비트 또는 256 비트 크기의 키를 지원하는 128 비트 크기의 블록 암호화를 사용한 대칭형 (암호화나 복호화를 하는데 동일한 키가 사용되는) 알고리즘으로서, 전 세계적으로 로열티 없이 사용할 수 있어야 하며, 향후 20년~30년 동안 데이터를 보호하기 위해 충분한 정도의 보안성을 제공할 것이 요구되었다. 또한, 이 알고리즘은 스마트카드 등과 같은 제한된 환경을 포함하여 하드웨어나 소프트웨어로 구현하기 쉬워야 했으며, 다양한 공격 기술에 대해서도 잘 방어할 수 있어야 했다.

전반적인 선정 과정은 대중적 조사와 평가에 완전히 공개되었으며, 이러한 투명성으로 인해 제출된 모든 설계안들에 대해 최적의 분석이 가능하였다. 1998년에 NIST는 미국 안보국을 포함, 세계의 암호화 단체에 의해 기본적인 분석을 받게 될 15개의 AES 후보작을 선정하였다. 여기에 기반을 두고 1999년 8월, NIST는 보다 심도 있는 2차 분석을 받게 될 다음의 5개 알고리즘을 선정하였다.


  • MARS: IBM 연구소 제출 
  • RC6: RSA Security 제출 
  • Rijndael: 두 명의 벨기에 암호학자 Joan Daemen와 Vincent Rijmen 공동 제출 
  • Serpent: Ross Andersen, Eli Biham 그리고 Lars Knudsen의 공동 제출 
  • Twofish: Counterpane의 존경받는 암호학자 Bruce Schneier를 비롯한 대규모 연구팀 제출 


위의 다섯 개 알고리즘은 모두 ANSI C와 자바 언어를 이용, 하드웨어와 소프트웨어 중심의 시스템 모두에서, 암호화와 복호화 속도 측정, 키와 알고리즘 설정 시간, 다양한 공격에 대한 저항성 등과 같은 심도 있는 시험을 거쳤다. 그 후, 이들 알고리즘은 새로운 암호화 체계를 깨보고자 자원하는 일부 팀을 포함 세계적인 암호화 단체들에 의해 다시 한번, 자세한 분석이 이루어졌다. 그 결과, 2000년 10월 2일에 NIST는 Rijndael를 표준안으로 최종 선정하였다. 2001년 12월 6일, 미 상무부 장관은 민감하지만 비밀로 분류되지 않은 모든 문서들에 AES로서 Rijndael을 사용할 것을 규정하는 연방 정보처리 표준, 즉 FIPS 197을 공식 승인하였다.




AES 사용하기

자바에서 AES 알고리즘을 적용하여 중요한 데이터를 암호화하고 복호화하는 것은 아주 쉬운 일이다.

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import org.junit.Test;

public class AESTest {

  private static String sKeyString = "" ;
  private static String message= "This is just an example";

  @Test
  public void testAESEncode() throws Exception {

    // 1. 128 비트 비밀키 생성
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    kgen.init(128);
    SecretKey skey = kgen.generateKey();

    // 2. 비밀 키를 이렇게 저장하여 사용하면 암호화/복호화가 편해진다.
    sKeyString = Hex.encodeHexString(skey.getEncoded());

    // 3. 암호화 수행
    SecretKeySpec skeySpec = new SecretKeySpec(skey.getEncoded(), "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(message.getBytes());

    System.out.println("encrypted string: " + Hex.encodeHexString(encrypted));

    // 4. 복호화 수행
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] original = cipher.doFinal(encrypted);
    String originalString = new String(original);
    System.out.println("Original string: " + originalString + " " + Hex.encodeHexString(original));

  }
}

예제와 같이 비밀키를 문자로 변환하여 저장해놓고 사용하면 웹 응용프로그램에서 개인정보를 편리하게 암호화하고 복호화 할 수 있다.

암호화와 복호화를 분리한 예제
package architecture.common;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Hex;
import org.junit.Test;


public class AESTest {

private static String encString = "" ;
 
 @Test 
 public void testASEEncodeWithKey() throws Exception {
  
  String password = "1234";
  String username = "원문";
  byte[] seedB = password.getBytes();
  SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
  sr.setSeed(seedB);  
  KeyGenerator kgen = KeyGenerator.getInstance("AES");
  kgen.init(128, sr); // 192 and 256 bits may not be available  
  // Generate the secret key specs.
  SecretKey skey = kgen.generateKey();
  String keyString = Hex.encodeHexString(skey.getEncoded());
  SecretKeySpec skeySpec = new SecretKeySpec(skey.getEncoded(), "AES");   
  Cipher cipher = Cipher.getInstance("AES");
  cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
     byte[] encrypted =  cipher.doFinal(username.getBytes());
     
     encString =  Hex.encodeHexString(encrypted);
     
     System.out.println("encrypted string: [" + keyString + "] " + encString );
 }
 
 @Test 
 public void testASEDecodeWithKey() throws Exception {
  String password = "1234";
  byte[] seedB = password.getBytes();
  SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
  sr.setSeed(seedB);
  
  KeyGenerator kgen = KeyGenerator.getInstance("AES");
  kgen.init(128, sr); // 192 and 256 bits may not be available
  
  // Generate the secret key specs.
  SecretKey skey = kgen.generateKey();
  String keyString = Hex.encodeHexString(skey.getEncoded());
  SecretKeySpec skeySpec = new SecretKeySpec(skey.getEncoded(), "AES"); 
  
  Cipher cipher = Cipher.getInstance("AES");
  cipher.init(Cipher.DECRYPT_MODE, skeySpec);
     byte[] decrypted =  cipher.doFinal(Hex.decodeHex(encString.toCharArray()));
     
     System.out.println("decrypted string: [" + keyString + "] " + new String(decrypted));
 }
 
 
 
}
참고자료
암호이용 안내서 (KISA 안내 해설 제 2010-23호)
http://www.terms.co.kr/AES.htm
AES 알고리즘 애니메이션 http://www.formaestudio.com/rijndaelinspector/