jar 파일 포맷의 힘

2007/05/14 18:48

서비 JAVA ,

원문 출처 : http://www-128.ibm.com/developerworks/kr/library/j-jar/
* 본 글은 저의 저작물이 아니며 그런 이유로 제 블로그에 포스팅 하는 글에 대한 CCL의 영향을 받지 않습니다. *

대부분의 자바 프로그래머들은 JAR 파일의 기본 작동에 익숙하다. 하지만 JAR 파일 포맷의 막강한 힘을 아는 개발자는 드물다.

JAR 파일

JAR 파일 포맷은 대중적인 ZIP 파일 포맷을 근간으로 하여 많은 파일들을 하나로 모으는데 사용된다. ZIP 파일과는 달리 JAR 파일은 압축과 디스트리뷰션 뿐만 아니라 라이브러리, 컴포넌트, 플러그인 등의 전개와 캡슐화에도 사용되며 컴파일러나 JVM 같은 툴이 직접 사용하기도 한다. 전개 디스크립터 같이 JAR에 포함된 특별한 파일은 특정 JAR가 취급되는 방법을 툴에 지시한다.

JAR 파일은 다음과 같은 데에 사용된다:

JAR 파일 포맷은 많은 혜택과 기능을 제공하며 ZIP 또는 TAR 같은 전통적인 아카이브 포맷이 줄 수 없는 많은 것들을 제공한다. 이를 테면:

JAR의 압축과 압축풀기

jar 툴( jar 참조)은 파일을 기본적으로 압축한다. 압축이 풀린 JAR 파일은 압축된 JAR 파일 보다 더 빠르게 로딩될 수 있다. 로딩 시간 동안 파일의 압축 풀기 시간이 줄어들기 때문이다. 하지만 네트워크를 통한 다운로드 시간은 압축이 풀린 파일이 더 길다.

META-INF 디렉토리

대부분의 JAR 파일에는 META-INF 디렉토리가 포함되어 있는데 이는 패키지의 저장과 보안 및 버저닝 정보 같은 확장 설정 데이터를 저장하는데 사용된다. META-INF 디렉토리의 파일과 디렉토리는 Java2platform에서 인식 및 인터프리팅되어 애플리케이션, 확장, 클래스 로더를 설정한다:

jar 툴

JAR 파일로 기본적인 태스크를 수행하려면 자바 개발 킷의 일부로 제공되는 Java Archive Tool (jar 툴)을 사용한다. jar 툴을 jar 명령어로 호출한다. 표 1은 일반 애플리케이션이다:

표 1. jar 툴의 일반적인 사용

기능 명령어
개별 파일에서 JAR 파일 만들기 jar cf jar-file input-file...
디렉토리에서 JAR 파일 만들기 jar cf jar-file dir-name
압축 풀린 JAR 파일 만들기 jar cf0 jar-file dir-name
JAR 파일 업데이트 jar uf jar-file input-file...
JAR 파일 내용보기 jar tf jar-file
JAR 파일 내용 추출하기 jar xf jar-file
JAR 파일에서 특정 파일 추출하기 jar xf jar-file archived-file...
실행 JAR 파일로 패키지된 애플리케이션 실행하기 java -jar app.jar



위로


실행 JAR 파일

실행 JAR 파일은 특별히 설정된 JAR 파일에 저장된 독립적인 자바 애플리케이션이다. 파일을 추출하거나 클래스 경로를 설정하지 않고 JVM에 의해 직접 실행될 수 있다. 비 실행 JAR에 저장된 애플리케이션을 구동하려면 이를 클래스 경로에 추가하고 애플리케이션의 메인 클래스를 이름별로 호출해야한다. 하지만 실행 JAR 파일을 사용하면 이를 추출하거나 메인 엔트리 포인트를 알 필요 없이 애플리케이션을 실행할 수 있다.

실행 JAR 파일 만들기

실행 JAR 파일을 만들기는 쉽다. 모든 애플리케이션 코드를 하나의 디렉토리에 놓는 것으로 시작한다. 애플리케이션의 메인 클래스가 com.mycompany.myapp.Sample이라고 가정해보자. 애플리케이션 코드를 포함하고 메인 클래스를 구분하는 JAR 파일 생성이 필요하다. 이를 위해 라는 manifest 파일을 어딘가에(애플리케이션 디렉토리는 아니다) 만들고 여기에 다음 행을 추가한다:


Main-Class: com.mycompany.myapp.Sample

그런 다음 JAR 파일을 다음과 같이 만든다:


jar cmf manifest ExecutableJar.jar application-dir

실행 JAR 파일 시작하기

애플리케이션을 ExecutableJar.jar라는 실행 JAR 파일로 묶었으므로 다음 명령어를 사용하여 파일에서 직접 애플리케이션을 시작할 수 있다:


java -jar ExecutableJar.jar




위로


패키지 실링(sealing)

JAR 파일안에 패키지를 봉합(sealing)한다는 것은 이 패키지에 정의된 모든 클래스가 같은 JAR 파일에서 찾아져야 한다는 것을 의미한다. 이로서 패키지 작성자는 패키지된 클래스들의 버전 영속성을 강화할 수 있다. 봉합은 보안 조치도 제공하여 코드 탬퍼링을 탐지한다.

패키지를 봉합하려면 패키지용 Name 헤더를 추가한다. 그 뒤에 Sealed 헤더 값을 JAR manifest 파일에 대해 "true"로 한다. 실행 JAR 파일과 마찬가지로 manifest 파일을 적절한 헤더 엘리먼트로 지정하여 JAR를 봉합할 수 있다:


Name: com/samplePackage/
Sealed: true

Name 헤더는 패키지의 관련 경로명을 정한다. 파일이름과 구별되도록 "/"로 끝난다. Name 헤더에 뒤따르는 모든 헤더는 공백 라인 없이 Name 헤더에 지정된 파일이나 패키지에 붙는다. 위 예제에서 Sealed 헤더가 공백 라인 없이 Name 헤더 다음에 발생했기 때문에 Sealed 헤더는 com/samplePackage 패키지에만 붙는것으로 인터프리팅된다.

확장 패키징

확장은 자바 플랫폼에 기능을 추가한다. 확장 메커니즘은 JAR 파일 포맷에 구현된다. 확장 메커니즘으로 JAR 파일이 다른 필요한 JAR 파일들을 Class-Path 헤더를 통해 manifest 파일에 지정할 수 있다.

extension1.jar와 extension2.jar가 같은 디렉토리 안의 두 개의 JAR 파일에 있다고 가정해보자. extension1.jar의 manifest는 다음 헤더를 포함하고 있다:


Class-Path: extension2.jar

이 헤더는 extension2.jar의 클래스들이 extension1.jar의 클래스를 목표에 맞춘 확장 클래스로서 작용한다는 것을 나타내고 있다. extension1.jar의 클래스들은 extension2.jar가 클랫의 경로의 일부가 될 필요 없이 extension2.jar의 클래스를 호출할 수 있다.

예를 들어 ExtensionDemo 클래스를 레퍼런싱하는 ExtensionClient 클래스가 ExtensionClient.jar라고 하는 JAR 파일에 번들되었고 ExtensionDemo 클래스가 ExtensionDemo.jar에 번들되었다고 가정해보자. ExtensionDemo.jar가 확장으로 취급되기 위해서는 ExtensionDemo.jar는 ExtensionClient.jar의 manifest 안의 Class-Path 헤더에 리스트되어야 한다:


Manifest-Version: 1.0
Class-Path: ExtensionDemo.jar




위로


JAR 파일의 보안

JAR 파일은 jarsigner 툴을 사용하거나 java.security API를 통해서 직접 서명될 수 있다. 서명된 JAR 파일은 원래 JAR 파일과 정확히 같다. manifest만이 업데이트 된 것과 두 개의 추가 파일들이 META-INF 디렉토리에 추가된 것을 제외하고.

Keystore 데이터베이스에 저장된 인증을 사용하여 JAR 파일은 서명된다. Keystore에 저장된 인증은 패스워드로 보호된다.


그림 1. Keystore 데이터베이스
Keystore Database

JAR의 각 서명자는 JAR 파일의 META-INF 디렉토리안에 있는 .SF 확장자가 붙은 서명으로 표현된다. 이 파일의 포맷은 manifest 파일과 비슷하다. 메인 섹션과 개별 엔트리들로 구성되어 있다. 서명된 JAR에서 오는 파일을 확인하기 위해 서명 파일의 다이제스트 값은 JAR 파일의 상응 엔트리에 대비하여 계산된 다이제스트와 비교된다.


Listing 1. Manifest와 서명 파일

Contents of signature file META-INF/MANIFEST.MF

Manifest-Version: 1.0
Created-By: 1.3.0 (Sun Microsystems Inc.)

Name: Sample.java
SHA1-Digest: 3+DdYW8INICtyG8ZarHlFxX0W6g=

Name: Sample.class
SHA1-Digest: YJ5yQHBZBJ3SsTNcHJFqUkfWEmI=

Contents of signature file META-INF/JAMES.SF

Signature-Version: 1.0
SHA1-Digest-Manifest: HBstZOJBuuTJ6QMIdB90T8sjaOM=
Created-By: 1.3.0 (Sun Microsystems Inc.)

Name: Sample.java
SHA1-Digest: qipMDrkurQcKwnyIlI3Jtrnia8Q=

Name: Sample.class
SHA1-Digest: pT2DYby8QXPcCzv2NwpLxd8p4G4=

디지틀 서명

디지틀 서명은 .SF 서명 파일의 서명완료된 버전이다. 디지틀 서명 파일은 바이너리 파일이며 .SF 파일과 같은 파일이름을 갖고 있지만 다른 확장이다. 확장은 디지틀 서명 유형에 따라 다양하고 (RSA, DSA, PGP). JAR 서명에 사용된 인증 유형에 따라 다르다.

Keystore

JAR 파일에 서명하려면 프라이빗 키를 가져야 한다. 프라이빗 키와 관련 퍼블릭 키 인증은 패스워드로 보호된 데이터베이스(keystores)에 저장된다. JDK는 Keystore를 구현 및 변경하는 툴을 포함하고 있다. Keystore의 각 키는 앨리어스에 의해 구분되는데 전형적으로 키를 소유한 서명자의 이름이다.

모든 Keystore 엔트리들은 고유 앨리어스로 액세스된다. 앨리어스는 Keystore에 엔터티를 추가할 때 keytool -genkey 명령어를 사용하여 지정되어 키 쌍을 만든다. 뒤따르는 keytool 명령어는 이와 같은 앨리어스를 사용하여 엔터티를 언급해야 한다.

예를 들어 "james"라는 앨리어스로 새로운 퍼블릭/프라이빗 키 쌍을 만들고 퍼블릭 키를 자가 서명된 인증으로 래핑하려면 다음 명령어를 사용한다:


keytool -genkey -alias james -keypass jamespass
-validity 80 -keystore jamesKeyStore
-storepass jamesKeyStorePass

jarsigner 툴

jarsigner 툴은 Keystore를 사용하여 JAR 파일에 대한 디지틀 서명을 만들거나 확인한다.

위 예제에서 처럼 "jamesKeyStore" Keystore를 만들었고 여기에 "james" 앨리어스와 키를 포함하고 있다고 가정해보자. 다음 명령어로 JAR 파일에 서명할 수 있다:


jarsigner -keystore jamesKeyStore -storepass jamesKeyStorePass
-keypass jamespass -signedjar SSample.jar Sample.jar james

이 명령어는 앨리어스가 "james"이고 패스워드가 "jamespass"인 키를 보내 Sample.jar 파일에 서명하고 SSample.jar라는 서명된 JAR를 만든다.


jarsigner -verify SSample.jar




위로


JAR 인덱싱(indexing)

애플리케이션 또는 애플릿이 다중의 JAR 파일들로 번들된다면 클래스 로더는 단순한 리니어 검색 알고리즘을 사용하여 클래스 경로의 엘리먼트를 검색한다. 클래스 로더가 존재하지 않은 리소스를 찾으려고 하면 애플리케이션 또는 애플릿 내의 모든 JAR 파일들은 다운로드 되어야한다. 큰 네트워크 애플리케이션과 애플릿의 경우 늦은 시작, 지연된 응답, 네트워크 대역 낭비를 초래한다.

JDK 1.3 이후 JAR 파일 포맷은 인덱싱(indexing)을 지원하여 네트워크 애플리케이션(특히 애플릿)의 클래스 검색 프로세스를 최적화했다. JarIndex 메커니즘은 애플릿 또는 애플리케이션에 정의된 모든 JAR 파일의 내용을 모아 첫 번째 JAR 파일의 인덱스 파일에 이 정보를 저장한다. 첫 번째 JAR 파일이 다운로드된 후에 애플릿 클래스 로더는 모아진 콘텐트 정보를 사용하여 JAR 파일을 효율적으로 다운로드한다. 이 디렉토리 정보는 INDEX.LIST라는 이름으로 간단한 텍스트 파일로 저장된다.(META-INF 디렉토리).

JarIndex 만들기


그림 2. JarIndex
JarIndex Demo

다음 명령어를 사용하여 JarIndex_Main.jar, JarIndex_test.jar, JarIndex_test1.jar용 인덱스 파일을 만든다:


jar -i JarIndex_Main.jar JarIndex_test.jar SampleDir/JarIndex_test1.jar

INDEX.LIST 파일은 간단한 포맷을 갖고 있으며 색인된 JAR 파일에 저장된 패키지 또는 클래스 이름을 포함하고 있다.(Listing 2):


Listing 2. JarIndex INDEX.LIST 파일

JarIndex-Version: 1.0

JarIndex_Main.jar
sp

JarIndex_test.jar
Sample

SampleDir/JarIndex_test1.jar
org
org/apache
org/apache/xerces
org/apache/xerces/framework
org/apache/xerces/framework/xml4j



2007/05/14 18:48 2007/05/14 18:48
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

Ajax와 Java EE의 통합

2007/04/24 17:33

서비 JAVA , ,

원문 출처 : http://www.ibm.com/developerworks/kr/library/j-ajaxee/index.html

비동기식 통신 모델과 동기식 통신 모델을 효과적으로 혼합하기

IT 전문가 Patrick Gan이 Ajax 기술을 Java EE 웹 애플리케이션에 적용할 때 전체적인 개발 과정 속에서 발생할 수 있는 문제들을 점검합니다. 패턴에 기반하고 있는 Ajax의 비동기식 통신을 채택할 때의 문제들을 파악하는 것이 효과적인 Ajax 통합의 지름길입니다.

Asynchronous JavaScript + XML (Ajax)은 매우 새로운 기술이다. Java EE 커뮤니티를 비롯한 많은 웹 개발 커뮤니티에서 이에 대한 많은 소문들이 만들어지고 있다. Ajax 기술은 과도한 웹 페이지 리프레쉬를 줄임으로서 애플리케이션 가용성을 향상시키고 있다. 그리고 Ajax의 best-of-both-worlds 접근 방식은 클라이언트 측 코드와 서버 측 코드를 활용하여 웹 사용자에게 거의 완벽한 UI를 제공하고자 한다. Ajax는 웹의 부활(Web 2.0)에 있어 핵심 인에이블러로서 간주되고 있다.

부지런한 Java EE 개발자라면 아마도 Ajax에 대한 많은 하우투 문서를 섭렵하고 이것이 여러분의 애플리케이션에 가져올 향상에 대해서도 기대했을지 모른다. 하지만 어떻게 Ajax의 비동기식 통신 기반 패턴이 Java EE 애플리케이션에 맞을까? 이 글에서는 Java EE 애플리케이션의 디자인, 개발, 퍼포먼스, 테스팅에 Ajax를 채택했을 때 미치는 영향을 검토하여 해결책을 모색하고자 한다. 이렇게 하는 이유는 Ajax 사용을 금하려는 것이 아니라, 오히려 Ajax를 효과적으로 사용할 수 있도록 문제들을 해결하려는 것이다.

디자인 문제점

자바 커뮤니티는 좋은 디자인 패턴들을 웹 관련 애플리케이션 개발에 적용하려고 부단히도 애썼다. 가장 광범위하게 사용되는 패턴들 중 하나가 Model-View-Controller (MVC)이다. Apache Struts 같은 여러 오픈 소스 프레임웍들은 이러한 디자인 패턴/아키텍처에 기반하고 있다. (참고자료) MVC의 많은 장점들 중에는 관심의 분리(separation of concerns)와 과잉 코드의 감소를 들 수 있다.

관심의 분리를 통해, 애플리케이션 아키텍처에서 사전에 협상된 인터페이스를 사용하면서 각 개발자는 애플리케이션 개발 프로젝트에서 지정된 역할에만 집중할 수 있다. 예를 들어, 모델-레이어 개발자들은 JDBC, Enterprise JavaBeans (EJB), 데이터 영속성 기술과 관련 있는 자바 클래스 같은 기술에 초점을 맞춘다. 뷰-레이어 개발자들은 JavaServer Pages (JSP), 태그 라이브러리, 기타 표현 관련 기술에 초점을 맞춘다. 컨트롤러 레이어는 모델과 뷰 사이를 구분 및 중재하고 인커밍 요청들을 백엔드 호출로 라우팅 하면서 관심들을 깨끗하게 분리한다. 그림 1은 MVC 아키텍처 모습이다.


그림 1. MVC 아키텍처
MVC architecture

Ajax를 Java EE 웹 애플리케이션에 도입하는 것에는 관심의 분리라는 의미가 내포되어 있다. (따라서 개발자 역할도 분리된다.) 어떤 경우, Ajax는 많은 양의 JavaScript 코드를 뷰-레이어(JSP) 페이지에 도입한다. 표 1은 Ajax가 적용되지 않은 MVC 뷰 레이어와 필요한 코드를 설명한다. 컨트롤러 레이어는 서블릿으로 구현되고 뷰 레이어는 JSP로 구현된 것으로 간주된다. (다음 개발 딜레마 처리하기 섹션에서는 동기식 요청과 비동기식 요청간 차이점을 설명하겠다.)


표 1. Ajax가 적용되지 않은 MVC: 전형적인 뷰-레이어 시퀀스와 관련된 코드 분량

시퀀스 설명 필요한 코드?
동기식 요청을 실행하기 전 폼(form) 제출을 준비할 때 스크립틀릿 코드가 필요하다. 없음.
동기식 요청 실행 폼 제출은 버튼이나 링크를 통해 실행된다. DOM 엘리먼트 값은 ( GET 또는 POST를 통해) HttpRequest로 자동 설정된다. 없음: 필요한 것은 페이지를 제출하는 방식이다.
동기식 요청에 대한 응답 처리하기 서버 측 코드가 실행을 완료하면 객체는 (HttpRequest를 통해서나 HttpSession에 저장되어) JSP로 보내진다. 이 시점에서 객체들은 HttpRequest 또는 HttpSession을 통해 JSP에 액세스 되고(스크립틀릿이나 태그 라이브러리 사용), 객체 콘텐츠를 디스플레이 하는데 최소한의 스크립팅이 요구된다. 있음: 최소한의 스크립틀릿

Ajax를 이용한 MVC 뷰 레이어를 나타내는 표 2와 표 1을 비교해보자. 여기에서도 마찬가지로 컨트롤러 레이어는 서블릿에 의해 구현되고 뷰 레이어는 JSP로 구현된다.


표 2. Ajax를 이용한 MVC: 전형적인 뷰-레이어 시퀀스와 관련된 코드

시퀀스 설명 필요한 코드?
비동기식 요청을 실행하기 전 Ajax 호출에 필요한 DOM 엘리먼트의 값을 가져오는데 JavaScript 코드가 필요하다. 있음
비동기식 요청 실행 XMLHTTPRequest를 만들고 (이전에 모은) DOM 엘리먼트의 값을 연결시키고 보내는데 (XMLHTTPRequest.send()) JavaScript 코드가 필요하다. 있음
비동기식 요청에 대한 응답 처리하기 서버 측 코드가 실행을 끝낸 후에 JavaScript 코드는 (XML 응답 스트림에서) 결과를 가져와서 해당 DOM 엘리먼트에 따라서 값을 전파한다. 있음

Ajax를 사용하면서 뷰 레이어에서의 스크립팅 코드의 양이 증가하는 것을 볼 수 있다. 세 가지 큰 단점이 있다.

  • JSP는 많은 JavaScript 코드를 필요로 한다.
  • 디자인은 역할 영역의 분리를 무시한다.
  • 디자인이 통합된 monolithic JSP (Model 1 접근 방식: 많은 HTML, CSS 코드, 이미지, 스크립팅 코드)를 사용한다. 이는 읽고 관리하기가 매우 까다로운 반패턴이다. (참고자료)

이러한 디자인 상의 단점을 피하거나 최소한 완화시킬 수 있는 여러 옵션들이 있다:

  • 재사용을 고려한 디자인: Ajax의 스크립팅 코드는 피하기 힘들다. 스크립팅 코드를 기획 및 설계하여 최대한 재사용할 수 있도록 한다.

  • 클라이언트 측에 MVC 방식 채택: 클라이언트 측에 MVC 방식 (Ajax in Action - 참고자료)을 사용할 수 있다. 이 방식은 관심의 분리 측면에서는 효과를 거둘 수 있지만 복잡해 질 수 있기 때문에 충분히 고려해야 한다.

  • Ajax 프레임웍 사용: Direct Web Remoting (DWR)(참고자료) 같은 여러 오픈 소스 Ajax 프레임웍들은 최소한의 코딩으로도 Ajax 패턴을 Java EE 애플리케이션으로 잘 통합한다.

  • 디자인의 유효성에 대한 재평가: Ajax는 웹 애플리케이션에 데스크탑 애플리케이션 애트리뷰트를 제공한다. 주어진 웹 애플리케이션에서 클라이언트 측 인터랙션 대부분이 Ajax를 활용한다. 애플리케이션은 데스크탑 애플리케이션으로서 보다 더 잘 설계될 것이다.




위로


개발 딜레마 처리하기

자바 웹 개발에 Ajax를 사용할 때 동기식(synchronous) 통신 모델과 비동기식(asynchronous) 통신 모델의 차이를 완전히 이해하는 것이 중요하다. (참고자료) 비동기식 통신 모델의 지원 부족은 클라이언트 측 개발, 웹 프레임웍과의 통합, 태그 라이브러리의 사용, IDE 사용, 쓰레딩 작동에 영향을 미칠 수 있다.

동기식 요청/응답 통신 모델에서 브라우저(웹 서버, 애플리케이션 서버, 웹 애플리케이션에 반(反)하는 개념)는 언제나 요청을 초기화 한다. 한편, 웹 서버, 애플리케이션 서버, 웹 애플리케이션은 인커밍 요청에 대응한다. 동기식 요청/응답 쌍이 처리되는 동안 사용자는 브라우저를 사용할 수 없다.

그림 2는 전통적인 웹 애플리케이션의 동기식 통신 모델을 나타내는 시퀀스 다이어그램이다. 서버의 생명 주기에서 클리어언트의 데이터 제출과 서버 측 프로세싱은 강결합 된다.


그림 2. 동기식 통신 시퀀스
Synchronous Communication sequence

비동기식 요청/응답 통신 모델에서, 웹 서버, 애플리케이션 서버, 웹 애플리케이션에 대한 브라우저 간 통신은 결합력이 약하다(decouple). 비동기식 요청/응답이 처리되는 동안 웹 사용자는 브라우저를 계속 사용할 수 있고 동시에 비동기식 요청이 처리된다. 비동기식 요청 프로세싱이 완료되면 비동기식 응답이 (웹 서버, 애플리케이션 서버, 웹 애플리케이션 에서) 클라이언트 페이지로 간다. 일반적으로 이 프로세스 동안에 실행은 웹 사용자들에게는 어떤 영향력도 없다. 응답을 기다릴 필요가 없다.

그림 3의 시퀀스 다이어그램은 비동기식 통신 모델을 묘사한 것이다. 첫 번째 dataSubmission(서버 측 프로세싱)과 리턴된 dataSubmission 모두 빨간색 원이 그려져 있다. 이 시퀀스들은 분리되어 있다. 이 그림에서는 중요한 요소들을 강조하고 있다. (쓰레딩 문제 참조) 다시 말해서 이 모드에서 다중 제출(쓰레드)가 빈번히 일어날 것 같다.


그림 3. 비동기식 통신 시퀀스
Asynchronous communication sequence

클라이언트 측에 미치는 영향

Ajax를 웹 애플리케이션에 도입할 때 개발 팀은 여러 가지 함정을 조심해야 한다. 대개는 생성된 HTML 페이지와 이것이 브라우저와 인터랙팅 하는 방식과 관련되어 있다. 이러한 문제들은 Chris Laffra의 Considering Ajax 시리즈에 잘 설명되어 있다. (참고자료)

  • 스크립팅이 실행되지 않을 수 있다: 여러 가지 다양한 이유로 인해, JavaScript는 많은 사용자 브라우저에서는 실행되지 않는다.

  • 크로스 브라우저 지원 때문에 많은 코드가 필요하다: 여러 브라우저들과 브라우저 버전들을 지원하는 애플리케이션은 스크립팅 코드가 많아져야 한다. 브라우저가 DOM 엘리먼트(그리고 그러한 엘리먼트를 연산하는 자바 스크립트 코드)를 인터프리팅 하는 방식에 미묘한 변수들이 존재하기 때문이다.

  • JavaScript는 안전하지 않다: 대부분의 브라우저에서, HTML 페이지와 제휴된 JavaScript 소스 코드는 뷰 소스 옵션을 선택하면 볼 수 있다. Ajax 패턴을 사용할 때 스크립팅 코드의 로직이 민감하지 않은지를 확인해야 한다.

웹 프레임웍과의 통합

Ajax 개발을 자신의 Java EE 웹 프레임웍과 통합하려는 시도는 자연스러운 현상이다. 하지만 몇몇 Java EE 웹 프레임웍은 비동기식 통신 모델을 지원하지 않는다. 서블릿이 동기식 통신과 비동기식 통신을 처리하는 방법을 이해해야 한다. 그림 4는 전통적인 서블릿이 동기식 요청을 처리하는 모습이다.


그림 4. 동기식 요청을 처리하는 서블릿 시퀀스
Traditional Servlet sequence (synchronous)

그림 4는 Java EE 웹 개발자에게는 익숙할 것이다. 브라우저에서 온 요청이 처음에는 컨트롤러 서블릿의 service()에 의해 처리된다. 서블릿은 필요한 값을 HttpRequest (매개변수 또는 애트리뷰트로서)를 가져올 수 있다. 컨트롤러 프로세싱이 처리되면 결과는 HttpRequest (또는 HttpSession)로 보내지고 RequestDispatcher는 컨트롤을 페이지로 전달한다.

그림 5는 비동기식 요청을 처리하는 서블릿 시퀀스이다:


그림 5. 비동기식 요청을 처리하는 서블릿 시퀀스
Servlet sequence (asynchronous)

그림 5의 시퀀스는 동기식 시퀀스와는 약간 다르다. 브라우저에서 온 요청은 처음에는 컨트롤러 서블릿의 service()에 의해 처리된다. 이 서블릿은 필요한 모든 값을 HttpRequest (매개변수 또는 애트리뷰트로서)에서 가져올 수 있다. 일단 컨트롤러 프로세싱이 끝나면 HttpServletResponse의 콘텐트 유형이 XML로 설정되어야 한다. 또한 컨트롤러 로직의 결과는 PrintWriter로 작성된다. 이 시점에서 RequestDispatcher의 사용이 바이패스(bypass)된다.

이것은 대부분의 Java EE 웹 프레임웍이 지원하지 않는 정확한 (비동기식) 시퀀스이다. 이것 때문에 Ajax와의 통합이 어렵게 된다. 비동기식 통신 모델을 지원하지 않는 포틀릿과 JavaServer Faces (JSF) 프레임웍도 같은 상황에 처해있다.

이러한 문제를 해결할 수 있는 몇 가지 옵션이 있다:

  • 웹 프레임웍과의 공존: 빌트인 Ajax 지원을 기다리거나 자신의 프레임웍에 Ajax를 강제적으로 지원하는 대신 개별 서블릿으로 모든 비동기식 요청들을 처리할 수 있다. DWR이 이 방법을 사용한다. 이 방식의 단점은 Ajax 요청이 프레임웍의 기능들을 쉽게 활용할 수 없다는 점이다.

  • 웹 프레임웍과 통합하기: 확장을 사용하거나 커스텀 확장을 작성하여 웹 프레임웍과 통합할 수 있다.

  • Ajax를 지원하는 프레임웍으로 마이그레이션: 새로운 프레임웍이 비동기식 통신 모델을 지원하기 시작했다. 이중 하나가 Apache Shale 이다. (참고자료)

태그 라이브러리

자바 웹 애플리케이션 개발 시 일반적으로 태그 라이브러리(taglibs)를 많이 사용한다. 많은 Java EE 웹 프레임웍과 마찬가지로 taglibs는 비동기식 통신 모델을 지원하지 않는다. XMLHttpRequest를 통해서 제출된 데이터를 HttpServletRequest로 트랜슬레이트 할 방법이 없다. 본질적으로 비동기식 통신을 지원하지 않는 taglibs는 Ajax XMLHttpRequest 호출이 실행되는 동안 기능을 하지 않는다. 여러분에게 주어진 옵션은 다음과 같다.

  • 비동기식 모델을 지원하지 않는 taglibs를 사용하지 않는다: taglibs로 만든 코드를 HTML/JavaScript 코드로 마이그레이션 한다. (웹 애플리케이션이 taglibs에 많이 의존하는 경우 이 방법을 사용하면 뷰-레이어의 페이지 크기만 늘어난다.)

  • 문제를 해결한다: 이 문제에 대한 해결책을 갖고 있는 Ajax 프레임웍을 사용한다. 그 한 가지 예가 DWR이다. (ExecutionContext.forwardToString() 참조) 이 경우 여러분이 사용했던 taglibs를 계속 사용할 수 있다.

  • Ajax 지원 taglibs를 사용한다: Ajax JSP Tag Library (AjaxTags) 같은 비동기식 모델을 지원하는 taglibs를 사용한다. (참고자료)

IDE를 이용한 개발과 디버깅

많은 JavaScript 디버깅 툴은 JavaScript 솔루션을 개발할 때 도움이 된다. 하지만 전통적인 자바 개발 환경에서는 XMLHTTPRequest의 가치와 Ajax와 관련된 특징을 확인할 수 없다.

한 가지 방법은 AJAX Toolkit Framework (ATF) (참고자료)을 사용하는 것이다. ATF는 강화된 JavaScript 에디팅 기능을 갖춘 Eclipse 플러그인이다. 편집 시 신택스 검사를 할 수 있고, Mozilla 웹 브라우저, DOM 브라우저, JavaScript 디버거 등이 내장되어 있다. ATF에는 Personality Builder가 있는데 이것은 임의의 Ajax 런타임 프레임웍용 IDE 기능의 구현에 도움이 되고 ATF에 있는 런타임 환경에 추가된다.

쓰레딩 문제

전형적인 동기식 웹 애플리케이션에서 버튼이나 링크 클릭에 다소 긴 처리 시간이 필요하다. 인내심이 없거나 경험이 없는 웹 사용자들은 버튼과 링크를 한 번 이상 클릭하여 여러 개의 폼 제출을 실행하곤 한다. 이것이 프로세싱 속도를 높일 것이라고 생각하면서 말이다. 사용자들은 (데스크탑 애플리케이션 처럼) 더블 클릭이 필요하다고 생각한다. 웹 애플리케이션에서의 여러 폼 제출은 어떤 경우에는 무해하다. 하지만 어떤 경우에는 심각한 쓰레딩 문제나 경쟁 조건(여러 쓰레드가 코드 블록의 실행을 위해 경쟁하는 것)을 야기할 수 있다. 예를 들어, 뱅킹 애플리케이션에서 Transfer Funds 버튼을 여러 번 클릭하면 원치 않는 다중 이체 결과를 초래할 수 있다.

동기식 통신 모델과 비동기식 통신 모델 모두를 지원하는 웹 애플리케이션은, 기능이 올바르게 분석 및 기획되지 않으면, 비슷한 문제에 봉착한다. 두 개의 통신 모델을 지원하는 애플리케이션은 해당 페이지에서 서버 측 호출을 혼합한다. (완전한 동기식, 완전한 비동기식, 동기식과 비동기식의 혼합). 여러 번 클릭되는 상황에서 비동기식 호출은 보다 느리게 처리된다. 애플리케이션이 이를 방지하지 않는다면 사용자는 비동기식 쓰레드가 처리되고 있는 동안 동기식 호출을 처리한다. 이 페이지는 리프레시 되지 않아 페이지가 더 이상 작동하지 않기 때문이다. 웹 페이지상의 같은 버튼이나 링크에서 나오지 않았더라도 같은 상황이 서버 측 코드에 쓰레딩 문제를 야기할 수 있다. (다중 클릭 문제와 비슷함)

뱅킹 애플리케이션의 자금 이체 페이지 예를 들어보자. (그림 6)


그림 6. 자금 이체 예제
Transfer funds example

빨간색 Transfer Funds 버튼은 Ajax 호출을 실행한다. Logout 링크(노란색)는 동기식 호출을 실행한다. 성급하거나 경험이 없는 사용자는 빨간색 버튼과 노란색 링크를 연속적으로 클릭하면 (그리고 두 링크 모두 공통 경로를 갖고 있다고 생각한다면) 경쟁 조건이 발생한다.

일반적으로 이러한 상황을 방지할 수 있는 두 가지 방법이 있다. 첫 번째는 클라이언트 측 솔루션이다. 링크나 버튼이 실행되면 JavaScript를 사용하여 추가의 페이지 제출이 현재 쓰레드가 실행을 종료할 때까지 금지되었는지를 확인한다. 두 번째 솔루션은 서버 측 코드의 동기화에 의존하여 경쟁 조건을 지키면서 멀티 쓰레드 제출을 허용하는 것이다. 이 문제를 해결할 때 동기화를 적용한다면 Java EE 웹 컴포넌트(서블릿, 포틀릿, JSF)가 멀티 쓰레딩 된다. 코드의 큰 섹션을 동기화 할 때 주의하라. (특히 요청/응답 프로세싱과 관련된 코드) 실제로, 동기화를 잘못 사용하면 애플리케이션이 싱글-쓰레드 애플리케이션으로 변하고 쓰루풋도 줄어든다.




위로


퍼포먼스 함정 피하기

Ajax를 사용하면 Java EE 웹 기반 애플리케이션의 퍼포먼스에도 영향을 미친다. 요청 당 추가 쓰레드를 허용하게 되면 두 가지 리소스가 영향을 받는다.

우선, 서블릿 컨테이너에 있는 쓰레드 풀(thread pool))이 영향을 받는다. 쓰레드 풀은 웹 컨테이너에서 동시에 실행할 수 있는 최대 쓰레드의 수를 지정한다. 클라이언트 요청에 쓰레드가 필요하다. 하지만 클라이언트 요청이 사용자 요청과 언제나 같은 것은 아니다. 브라우저는 사용자 요청 당 여러 클라이언트 요청을 필요로 할 수 있다. 예를 들어, 사용자가 제출한 폼에 여러 클라이언트 요청이 필요하다. (폼 값 제출, GIF 파일 가져오기, JavaScript 파일 가져오기, CSS 파일 가져오기) 동기식/비동기식 요청이 동시에 제출될 수 있다면 사용자 요청당 (Ajax 요청에 대해) 적어도 한 개 이상의 쓰레드 소비를 지원할 수 있다는 의미이다. 사용자 요청당 한 개 이상의 쓰레드를 추가하는 것이 가능하지만 애플리케이션이 로딩중이면 결과는 자명하다. (사용자 요청 당 추가 쓰레드가 평균 사용자 카운트 만큼 배가될 것이다.) 분명 서블릿 컨테이너의 퍼포먼스에 영향을 미칠 것이다.

또 한가지 리소스는 데이터베이스 커넥션 풀(database connection pool)이다. 전형적인 Java EE 웹 애플리케이션은 사용자 요청에 대해 두 가지 유형의 시퀀스를 실행한다. 가벼운(shallow) 요청과 무거운(deep) 요청이다. 가벼운 요청은 서버 측 코드를 실행하는 웹 페이지에서 기원한 요청이지만 요청을 완료하기 위해 (데이터베이스 같은) 영속 스토어에 액세스 하지 않는다. 무거운 요청은 서버 측 코드를 실행하는 웹 페이지에서 기원한 요청이고 요청을 완료할 때 영속 스토어에 액세스 한다.

무거운 요청 시퀀스(데이터베이스 연결이 필요함)에서, 더 많은 쓰레드를 허용하면 데이터베이스 커넥션 풀링에는 다음과 같은 요소들이 영향을 받는다.

  • 커넥션을 기다리는 쓰레드의 평균 수
  • 커넥션을 기다리는 평균 시간 (밀리초 단위)
  • 커넥션을 사용하는 평균 시간

결국, 커넥션 풀의 크기나 커넥션 수를 늘려야 한다.




위로


테스팅

자바 개발자들은 Java SE와 Java EE 코드에 단위 테스트를 수행하는 것을 중요하게 생각하고 있다. Ajax 때문에 브라우저에 내장된 JavaScript의 양이 많아지면서 견고한 단위 테스팅 프레임웍이 절실히 요구된다. JsUnit, Selenium, HttpUnit 등을 사용할 수 있다. (참고자료)

이러한 프레임웍들은 웹 페이지 상에서 DOM 엘리먼트를 조작하는 JavaScript 함수에 대한 단위 테스트를 개발하는 장치를 제공한다. 또한 단위 테스트를 테스트 슈트로 그룹핑 할 수 있다. Selenium의 브라우저 호환성 테스팅 기능으로 다양한 브라우저와 운영 체계에서 JavaScript 함수를 테스트 할 수 있다. 이것은 JavaScript와 Iframes를 사용하여 테스트 자동화 엔진을 브라우저에 삽입한다. 이 기술은 JavaScript가 작동되는 브라우저에 적용해야 하고 다중 브라우저와 브라우저 버전들을 지원하는 애플리케이션에 특히 유용하다. Selenium과 JsUnit 모두 연속 통합(continuous integration)을 지원한다. JavaScript 단위 테스트와 테스트 슈트를 자동화 구현 프로세스에 통합할 수 있다.




위로


결론

다른 기술이나 패턴과 마찬가지로 Ajax를 Java EE 애플리케이션에 적용할 때 장단점이 있다. 이 글에서는 Ajax를 Java EE 웹 애플리케이션으로 통합하는 것에 대한 큰 그림을 보여주었다. Ajax의 비동기식 통신 모델은 전통적인 Java EE 웹 애플리케이션의 동기식 모델과는 매우 다르다. 따라서 Ajax를 채택하기 전에 문제가 되는 부분에 대한 충분한 고려가 있어야 한다.

Java EE 프레임웍과 유틸리티 지원은 계속 발전한다. Ajax가 지원되는 프레임웍을 활용하여 통합 시 생기는 복잡함을 줄여야 할 것이다. JSF 기반 Apache Shale과 서블릿 기반 DWR을 계속 주목하기 바란다.

기사의 원문보기



참고자료

교육


제품 및 기술 얻기


토론


2007/04/24 17:33 2007/04/24 17:33
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

Web Services (JAX-WS) in Java EE 5

Java API for XML Web Services (JAX-WS) 2.0, JSR 224, is an important part of the Java EE 5 platform. A follow-on release of Java API for XML-based RPC 1.1(JAX-RPC), JAX-WS simplifies the task of developing web services using Java technology. It addresses some of the issues in JAX-RPC 1.1 by providing support for multiple protocols such as SOAP 1.1, SOAP 1.2, XML, and by providing a facility for supporting additional protocols along with HTTP. JAX-WS uses JAXB 2.0 for data binding and supports customizations to control generated service endpoint interfaces. With its support for annotations, JAX-WS simplifies web service development and reduces the size of runtime JAR files.

This document takes you through the basics of using the IDE to develop a JAX-WS web service and to consume it in three different clients—either a Java class in a Java SE application, or a servlet or JSP page in a web application. The three clients that you create in this document are separate applications, all consuming the same web service.

Expected duration: 25 minutes

Software Needed for the Tutorial

Before you begin, you need to install the following software on your computer:

  • NetBeans IDE 5.5 (download).
  • Java Standard Development Kit (JDK) version 5.0 or version 6.0 (download).
  • Sun Java System Application Server 9.0 (if not bundled with your NetBeans IDE installation, download and install it separately).

Tutorial Exercises

Installing and Configuring the Tutorial Environment

If you have not registered an instance of the Sun Java System Application Server 9.0, you must do so before you can begin developing Java EE 5 applications:

  1. Choose Tools > Server Manager from the main window.
  2. Click Add Server. Select Sun Java System Application Server and give a name to the instance. Then click Next.
  3. Specify the server information, the location of the local instance of the application server, and the domain to which you want to deploy.
  4. Click Finish.

Note: If you want to deploy to the Tomcat Web Server, you can do so, except that, since it only has a web container, you should create a web application, not an EJB module, in the next section. JAX-WS web services, unlike JSR-109 web services, can deploy successfully to the Tomcat web container.

Creating a Web Service

The goal of this exercise is to create a project appropriate to the deployment container that you decide to use. Once you have a project, you will create a web service in it.

Choosing a Container

You can either deploy your web service in a web container or in an EJB container. This depends on implementation choices. For example, if you plan to deploy to the Tomcat Web Server, which only has a web container, you should choose to create a web application, and not an EJB module.

  1. Choose File > New Project (Ctrl-Shift-N). Select Web Application from the Web category or EJB Module from the Enterprise category.
  2. Name the project CalculatorWSApplication.
  3. Depending on the deployment server that you want to use, do the following:
    • For the Sun Java System Application Server, set the J2EE Version to Java EE 5.
    • For the Tomcat Web Server, unselect the Set Source Level to 1.4 checkbox.
  4. Click Finish.

Creating a Web Service from a Java Class

  1. Right-click the CalculatorWSApplication node and choose New > Web Service.
  2. Name the web service CalculatorWS, type org.me.calculator in Package, and click Finish.

    The Projects window displays the new web service. For example, for web applications the Projects window now looks as follows:

    Projects window displaying the web service

    The IDE automatically creates the deployment descriptors required for the server, if any. For the Sun Java System Application Server, no deployment descriptor is needed. For web services deployed to the Tomcat Web Server, sun-jaxws.xml and a WSServlet item in web.xml are added.

Summary

In this exercise, you created a NetBeans project and set up the web service.

Coding the Web Service

The goal of this exercise is to do something meaningful with the files and code that the IDE has generated for you. You will add an operation that will add two numbers received from a client.

Adding Business Logic to the Web Service

  1. Expand the Web Services node and double-click the CalculatorWS node. The web service opens in the Source Editor. Note that an operation exists in the code already. It is commented out. Here, we create a new operation from scratch. Another way of creating the operation would be to remove the lines that comment out the code.
  2. Right-click within the body of the class, either above or below the code that is commented out, and choose Web Service > Add Operation.
  3. In the upper part of the Add Operation dialog box, type add in Name and choose int from the Return Type drop-down list.
  4. In the lower part of the Add Operation dialog box, click Add and create a parameter of type int named i. Click OK.
  5. Click Add again and create a parameter of type int called j.
  6. Click OK at the bottom of the Add Operation dialog box. Notice that a skeleton for the add method has been added to the Source Editor:
        
    1. @WebMethod
    2.     public int add(@WebParam(name = "i") int i, @WebParam(name = "j") int j) {
    3.         // TODO implement operation
    4.         return 0;
    5.     }

  7. Change the add method to the following (changes are in bold):
       
    1. @WebMethod
    2.     public int add(@WebParam(name = "i") int i, @WebParam(name = "j") int j) {
    3.         int k = i + j;
    4.         return k;
    5.     }


Summary

In this exercise, you added code to the web service.

Deploying and Testing the Web Service

When you deploy a web service to a web container, the IDE lets you test the web service to see if it functions as you expect. The Tester application, provided by the Sun Java System Application Server, is integrated into the IDE for this purpose. For the Tomcat Web Server, there is a similar tool. However, while the Sun Java System Application Server's Tester page lets you enter values and test them, the Tomcat Web Server does not. In the latter case, you can only see that the web service is deployed, you cannot test the values. No facility for testing whether an EJB module is deployed successfully is currently available.

To test successful deployment to a web container:

  1. Right-click the project node, choose Properties, and click Run. Depending on the deployment server that you want to use, do the following:
    • For the Sun Java System Application Server, type /CalculatorWSService?Tester in the Relative URL field.
    • For the Tomcat Web Server, type /CalculatorWS?Tester in the Relative URL field.

    Note: Since the result of a deployed EJB module is not displayed in a browser, you cannot take the step above if you are working with an EJB module.

  2. Right-click the project node and choose Run Project.

    The IDE starts the application server, builds the application, and opens the tester page in your browser, if you deployed a web application to the Sun Java System Application Server. For the Tomcat Web Server and deployment of EJB modules, the situation is different:

    • If you deployed to the Tomcat Web Server, you will see the following instead, which indicates that you have successfully deployed your web service:

      Web page displayed when web service was successfully deployed to Tomcat server

    • If you deployed an EJB module, successful deployment is indicated by the following messages in the Output window:
          Deployment of application CalculatorWSApplication  completed successfully
          Enable of CalculatorWSApplication in target server   completed successfully
          Enable of application in all targets  completed successfully
          All operations completed successfully
          run-deploy:
          run:
          BUILD SUCCESSFUL
    • If you deployed to the Sun Java System Application Server, type two numbers in the tester page, as shown below:

      Web service tester page when service successfully deployed to Sun Java System Application Server

      The sum of the two numbers is displayed:

      Web page showing result of web service test

Summary

In this exercise, you deployed a web service and tested it.

Consuming the Web Service

Now that we have deployed our web service, we need to create a client to make use of the web service's add method. Here, we create three clients— a Java class in a Java SE application, a servlet, and a JSP page in a web application.

Client 1: Java Class in Java SE Application

  1. Choose File > New Project (Ctrl-Shift-N). Select Java Application from the General category. Name the project CalculatorWS_Client_Application.

    Note: At the time of writing, issue Issue 10 was unresolved; therefore, you must create your web service client in a project folder that does not contain spaces in its path. For example, the path should not be "C:\Documents and Settings\...".

    Click Finish.
  2. Right-click the CalculatorWS_Client_Application node and choose New > Web Service Client.
  3. In Project, click Browse. Browse to the web service that you want to consume. When you have selected the web service, click OK.
  4. Type org.me.calculator.client in Package, and click Finish.

    The Projects window displays the new web service client:

    New web service client in Java SE application displayed in the Projects window

  5. Double-click Main.java so that it opens in the Source Editor. Delete the TODO comment and right-click in that line. Choose Web Service Client Resources > Call Web Service Operation.
  6. Browse to the Add operation and click OK.
  7. Change the line that is underlined in red to the following:
        System.out.println("Sum: " + port.add(3,4));
  8. Right-click the project node and choose Run Project.

    The Output window should now show the following:

        compile:
        run:
        Sum: 7
            BUILD SUCCESSFUL (total time: 1 second)

Client 2: Servlet in Web Application

  1. Choose File > New Project (Ctrl-Shift-N). Select Web Application from the Web category. Name the project CalculatorWSServletClient.

    Note: At the time of writing, issue Issue 10 was unresolved; therefore, you must create your web service client in a project folder that does not contain spaces in its path. For example, the path should not be "C:\Documents and Settings\...".

    Click Finish.

  2. Right-click the CalculatorWSServletClient node and choose New > Web Service Client.
  3. In Project, click Browse. Browse to the web service that you want to consume. When you have selected the web service, click OK.
  4. Type org.me.calculator.client in Package, and click Finish.

    The Projects window displays the new web service client:

    New web service client in servlet displayed in the Projects window

  5. Right-click the project node and choose New > Servlet. Name the servlet ClientServlet and house it in a package called org.me.calculator.client. Click Finish. To make the servlet the entry point to your application, right-click the project node, choose Properties, click Run, and type /ClientServlet in Relative URL. Click OK.
  6. In the Source Editor, remove the line that comments out the body of the processRequest method. This is the line that starts the section that comments out the code:
        /* TODO output your page here

    Next, delete the line that ends the section of commented out code:

        */

    Add some empty lines after this line:

        out.println("<h1>Servlet ClientServlet at " + request.getContextPath () + "</h1>");

    Now, right-click in one of the empty lines that you added. Choose Web Service Client Resources > Call Web Service Operation. The Select Operation to Invoke dialog box appears.

  7. Browse to the add operation and click OK.

    The processRequest method now looks as follows (the added code is in bold below):


    1.     protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    2.     throws ServletException, IOException {
    3.         response.setContentType("text/html;charset=UTF-8");
    4.         PrintWriter out = response.getWriter();
    5.         out.println("<html>");
    6.         out.println("<head>");
    7.         out.println("<title>Servlet ClientServlet</title>");
    8.         out.println("</head>");
    9.         out.println("<body>");
    10.         out.println("<h1>Servlet ClientServlet at " + request.getContextPath () + "</h1>");
    11.  
    12.         try { // Call Web Service Operation
    13.             org.me.calculator.client.CalculatorWSService service = new org.me.calculator.client.CalculatorWSService();
    14.             org.me.calculator.client.CalculatorWS port = service.getCalculatorWSPort();
    15.             // TODO initialize WS operation arguments here
    16.             int arg0 = 0;
    17.             int arg1 = 0;
    18.             // TODO process result here
    19.             int result = port.add(arg0, arg1);
    20.             System.out.println("Result = "+result);
    21.         } catch (Exception ex) {
    22.             // TODO handle custom exceptions here
    23.         }
    24.  
    25.         out.println("</body>");
    26.         out.println("</html>");
    27.         out.close();
    28.     }

    Change the value for arg0 and arg1 to other numbers, such as 3 and 4.

    Change the System.out.println statement to out.println.

    Add a line that prints out an exception, if an exception is thrown.

    The try/catch block should now look as follows (new and changed lines are highlighted):


    1.     try { // Call Web Service Operation
    2.         org.me.calculator.client.CalculatorWSService service = new org.me.calculator.client.CalculatorWSService();
    3.         org.me.calculator.client.CalculatorWSApplication port = service.getCalculatorWSApplicationPort();
    4.         // TODO initialize WS operation arguments here
    5.         int arg0 = 3;
    6.         int arg1 = 4;
    7.         // TODO process result here
    8.         int result = port.add(arg0, arg1);
    9.         out.println("<p>Result: " + result);
    10.     } catch (Exception ex) {
    11.         out.println("<p>Exception: " + ex);
    12.     }

  8. Right-click the project node and choose Run Project.

    The server starts, if it wasn't running already; the application is built and deployed, and the browser opens, displaying the calculation result.

Client 3: JSP Page in Web Application

  1. Choose File > New Project (Ctrl-Shift-N). Select Web Application from the Web category. Name the project CalculatorWSJSPClient.

    Note: At the time of writing, issue Issue 10 was unresolved; therefore, you must create your web service client in a project folder that does not contain spaces in its path. For example, the path should not be "C:\Documents and Settings\...".

    Click Finish.

  2. Right-click the CalculatorWSJSPClient node and choose New > Web Service Client.
  3. In Project, click Browse. Browse to the web service that you want to consume. When you have selected the web service, click OK.
  4. Type org.me.calculator.client in Package, and click Finish.

    The Projects window displays the new web service client.

  5. In the Web Pages folder, double-click index.jsp so that it opens in the Source Editor.
  6. In the Web Service References node, expand the node that represents the web service. The add operation, which you want to invoke from the client, is now exposed.
  7. Drag the add operation to the client's index.jsp page, and drop it below the H1 tags. The code for invoking the service's operation is now generated in the index.jsp page.

    Change the value for arg0 and arg1 to other numbers, such as 3 and 4.

  8. Right-click the project node and choose Run Project.

    The server starts, if it wasn't running already; the application is built and deployed, and the browser opens, displaying the calculation result:

    JSP page showing result 

    @http://www.netbeans.org/kb/55/websvc-jax-ws.html
2007/03/21 15:36 2007/03/21 15:36
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

Tomcat/JSP와 한글 문제...

◎ Tomcat 4.x
단순히 JSP 혹은 서블릿의 최 상단에 request.setCharacterEncoding("euc-kr");을 하면 된다.
GET과 POST 방식에 상관없이 인코딩을 해준다.


◎ Tomcat 5.x
POST 방식은 request.setCharacterEncoding("euc-kr");로 계속 하면된다.
하지만 GET 방식은 server.xml의 설정 부분을 바꿔줘야만 한다.

Server.xml 보기



위에서 URIEncoding="euc-kr" 부분이다.그리고 아래의 port="8009" 부분은 mod_jk를 이용해서 Apache와 Tomcat 5.x 를 연동할 때 URIEncoding="euc-kr"을 지정할 필요가 있다.

URIEncoding으로 지정할 경우, 만약 웹 어플리케이션이 EUC-KR과 UTF-8을 각 요청 별로 따로 처리할 필요가 있을경우 그에 대응할 수 없다. 대신, useBodyEncodingForURI="true"을 사용하면 Tomcat 4.x와 동일한 방식으로 즉, request.setCharacterEncoding() 값에 따라 GET/POST 방식 모두를 처리할 수도 있다. (참조 : http://tomcat.apache.org/tomcat-5.5-doc/config/http.html)

결론적으로 Tomcat 4.x와 Tomcat 5.x 는 모두 request.setCharacterEncoding()이 필요하다는 사실에는 변함이 없다.
2007/03/20 11:38 2007/03/20 11:38
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

QUEUE와 DELAYED 프로세싱

J2SE 5.0 출시에는 새로운 top-level Queue 인터페이스가 Collections Framework에 추가되어 Map, List, Set 인터페이스와 어우러진다. 일반적으로 queue는 먼저 들어온 것이 먼저 나가는 데이터 스트럭쳐이지만 priority queue같은 몇몇 구현들에서는 queue 뒤에 엘리먼트가 첨부되지 않는다. 이 queue와 FIFO(first in, first out)구조에 관한 얘기는 은행에서의 대기선에 비유할 수 있겠다. 은행직원은 고객이 대기선에 있는지 확인한 후 대기선의 첫번째 손님을 맞이하여 손님이 원하는 거래를 처리한다. 그리고 그 손님은 이제 대기선에서 제외된다.

J2SE 5.0에는 Queue 인터페이스와 함께 몇 가지 새로운 queue 구현이 있다. DelayQueue가 그 중 하나인데, DelayQueue에서 queue 안의 아이템들은 지연시간 동안 처리되지 않는다. 이번 테크팁에서는 새로운 Queue 인터페이스와 DelayQueue 구현에 대해 알아보도록 하자.

첫번째로 Queue 인터페이스를 분석해보자. 이 인터페이스는 Collection을 확장하고 다섯개의 고유 메서드들을 추가한다.

  • E element()
  • boolean offer(E o)
  • E peek()
  • E poll()
  • E remove()

J2SE 5.0이 새롭게 generics를 지원하므로 여기에서의 E는 어느 타입이든지 가능하며, Queue가 생성될 때 정의된 엘리먼트 타입으로 결정된다.

Queue의 엘리먼트를 추가하고 제거하는 데 당연히 Collection 인터페이스 메서드들을 사용할 수도 있지만, 그 메서드들은 동작할 때 부가적인 요구사항을 가지므로, Queue 인터페이스에서는 사용하지 않을 것을 권장하고 있다. 예를 들어 Collection.add 메서드로 queue에 엘리먼트를 추가하는 대신에 offer 메서드로 queue에 엘리먼트를 추가할 수 있다. 이 둘은 무슨 차이가 있을까? add 실행이 오류가 날 수 있다. 그 한 예는 queue에 사이즈 제한이 있을 때이다.(은행 대기선에 비유하자면, 10명만 대기 가능할 경우) Collectionadd 메서드를 사용하면 add는 예외를 던지면서 실패한다. 이와 비교할 때 offer 메서드는 false를 리턴하며 "실패"하게 된다. 따라서 offer 메서드를 사용하면 실제로 예외적인 상황에서만(특히 체크 안 된 런타임 예외가 던져졌을 경우) 예외처리(exception handling)을 사용하게 된다.

Queue의 다른 네가지 메서드는 두개씩 짝지어 설명할 수 있다. remove/poll, element/peek. removepoll 메서드는 둘 다 queue의 첫번째 엘리먼트 즉 "head"를 제거하는 데 사용된다. 빈 Collection 객체에서 호출되었을 때, remove 메서드는 예외를 던지고, poll 메서드는 단순히 null 값을 리턴한다. head 엘리먼트를 제거하는 대신 단지 그 엘리먼트를 살펴볼 수도 있다. 이 때 element와 peek 메서드가 사용된다. 여기서 element 메서드는 빈 queue에서는 예외를 던지고, peek은 null값을 리턴한다. Queue는 일반적으로 태스크를 진행하는 데 사용되므로 빈 queue를 갖는 것이 예외상황일 필요는 없다. 따라서, poll/peek 모델이 사용하기에 보다 적합할 것이다. (앞에서 본 메서드들 중 예외를 안 던지고 null 리턴하는 메서드들)

Queue는 다음과 같은 상황에서 일반적으로 사용된다.

   class Producer implements Runnable {
     private final Queue queue;
     Producer(Queue q) { queue = q; }
     public void run() {
       try {
         while(true) { queue.offer(produce()); }
       } catch (InterruptedException ex) { ... handle ...}
     }
     Object produce() { ... }
   }

   class Consumer implements Runnable {
     private final Queue queue;
     Consumer(Queue q) { queue = q; }
     public void run() {
       try {
         Object o;
         while((o = queue.poll()) != null) { consume(o); }
       } catch (InterruptedException ex) { ... handle ...}
     }
     void consume(Object x) { ... }
   }

Queue가 꽉 차 있거나(생산자의 입장에서) 비어있을 때(소비자의 입장에서) 어떤 일이 일어나는 지 궁금할 것이다. 이 시점에서 새로운 queue 인터페이스 BlockingQueue를 설명하는 것이 좋겠다. Queue를 사용하여 엘리먼트를 무작정 추가하거나(offer사용) 삭제하는(poll 사용) 대신 BlockingQueueput 메서드로 엘리먼트를 추가하고 take메서드로 제거할 수 있다. puttake는 모두 이를 호출한 쓰레드가, 특정 조건 하에서 블로킹되게끔 한다. put은 queue가 꽉 차 있을 경우, take는 queue가 비어있을 경우가 블로킹 조건이다.

BlockingQueue의 일반적인 사용패턴은 다음과 같다.

   class Producer implements Runnable {
     private final BlockingQueue queue;
     Producer(BlockingQueue q) { queue = q; }
     public void run() {
       try {
         while(true) { queue.put(produce()); }
       } catch (InterruptedException ex) { ... handle ...}
     }
       Object produce() { ... }
  }
  
   class Consumer implements Runnable {
     private final BlockingQueue queue;
     Consumer(BlockingQueue q) { queue = q; }
     public void run() {
       try {
         while(true) { consume(queue.take()); }
       } catch (InterruptedException ex) { ... handle ...}
     }
     void consume(Object x) { ... }
   }

각 생성된 생산자가 꽉 찬 queue에 새로운 아이템을 추가하려 하면 put메서드에서 기다리고, take 메서드에서는 꺼내갈 것이 추가될 때까지 기다린다. queue가 비어있다면 while(true) 조건문에는 아무런 변화도 일어나지 않을 것이다.

DelayQueue는 BlockingQueue 인터페이스의 구체적인 구현이다. DelayQueue 에 추가된 아이템들은 반드시 새로운 Delayed 인터페이스를 구현해야하며, 이 Delayed는 한 개의 메서드, long getDelay(TimeUnit unit)를 갖고 있다. DelayQueue는 우선순위 힙 데이터 스트럭쳐에 기반한 시간 기준 스케쥴링 Queue로 동작한다

데모를 위해, 다음의 프로그램 DelayTest는 몇 초안에 실행되는 Delayed 인터페이스를 구현한다. 알아둬야할 것은1) nanosecond는 10억분의 1초, 2) nanosecond 유니트에서 작업을 가능케하는 System의 새로운 메서드, nanoTime 이 있다는 것이다. getDelay 메서드가 nanosecond로 리턴된 횟수를 필요로 하기 때문에 nanosecond에서 작업하는 것이 중요하다.

   import java.util.Random;
   import java.util.concurrent.Delayed;
   import java.util.concurrent.DelayQueue;
   import java.util.concurrent.TimeUnit;

   public class DelayTest {
     public static long BILLION = 1000000000;
     static class SecondsDelayed implements Delayed { 
       long trigger;
       String name;
       SecondsDelayed(String name, long i) { 
         this.name = name;
         trigger = System.nanoTime() + (i * BILLION);
       }
       public int compareTo(Delayed d) {
         long i = trigger;
         long j = ((SecondsDelayed)d).trigger;
         int returnValue;
         if (i < j) {
           returnValue = -1;
         } else if (i > j) {
           returnValue = 1;
         } else {
           returnValue = 0;
         }
         return returnValue;
       }
       public boolean equals(Object other) {
         return ((SecondsDelayed)other).trigger == trigger;
       }
       public long getDelay(TimeUnit unit) {
         long n = trigger - System.nanoTime();
         return unit.convert(n, TimeUnit.NANOSECONDS);
       }
       public long getTriggerTime() {
         return trigger;
       }
       public String getName() {
         return name;
       }
       public String toString() {
         return name + " / " + String.valueOf(trigger);
       }
     }
     public static void main(String args[]) 
             throws InterruptedException {
       Random random = new Random();
       DelayQueue<SecondsDelayed> queue = 
             new DelayQueue<SecondsDelayed>();
       for (int i=0; i < 10; i++) {
         int delay = random.nextInt(10);
         System.out.println("Delaying: " + 
               delay + " for loop " + i);
         queue.add(new SecondsDelayed("loop " + i, delay));
       }
       long last = 0;
       for (int i=0; i < 10; i++) {
         SecondsDelayed delay = (SecondsDelayed)(queue.take());
         String name = delay.getName();
         long tt = delay.getTriggerTime();
         if (i != 0) {
           System.out.println("Delta: " + 
                 (tt - last) / (double)BILLION);
         }
         System.out.println(name + " / Trigger time: " + tt);
         last = tt;
       }
     }
   }

DelayTest 프로그램은 엘리먼트를 실행하기 전 DelayQueue 안에 그 10개의 엘리먼트를 위치시킨다.

다음은 DelayQueue를 한번 구동했을 때의 출력물이다.

   Delaying: 8 for loop 0
   Delaying: 7 for loop 1
   Delaying: 2 for loop 2
   Delaying: 4 for loop 3
   Delaying: 0 for loop 4
   Delaying: 9 for loop 5
   Delaying: 3 for loop 6
   Delaying: 4 for loop 7
   Delaying: 6 for loop 8
   Delaying: 2 for loop 9
   loop 4 / Trigger time: 1883173869520000
   Delta: 1.9995545
   loop 2 / Trigger time: 1883175869074500
   Delta: 0.0012475
   loop 9 / Trigger time: 1883175870322000
   Delta: 0.9995177
   loop 6 / Trigger time: 1883176869839700
   Delta: 0.9995187
   loop 3 / Trigger time: 1883177869358400
   Delta: 6.408E-4
   loop 7 / Trigger time: 1883177869999200
   Delta: 2.0001667
   loop 8 / Trigger time: 1883179870165900
   Delta: 0.9986953
   loop 1 / Trigger time: 1883180868861200
   Delta: 0.9995595
   loop 0 / Trigger time: 1883181868420700
   Delta: 1.001262
   loop 5 / Trigger time: 1883182869682700

이 출력물은 loop4를 위한 아이템이 time 0, 즉 지연 없이 시작하기로 설정되어있다는 것을 가리키고 있으며,

   Delaying: 0 for loop 4

따라서 첫번째로 다음과 같이 구동한다.

   loop 4 / Trigger time: 1883173869520000

Loop 2에서는 2초간의 지연이 있으며,

   Delaying: 2 for loop 2

따라서 다음과 같이 나타난다.

   loop 2 / Trigger time: 1883175869074500

여기서의 delta는 1.9995545로 약 2초이다.

   Delta: 1.9995545

다른 loop를 위한 비슷한 delta들도 존재한다.

좀 더 실제적인 예를 위해 DelayQueue에서 pull된 것을 단지 출력하는 대신, queue의 아이템들을 Runnable 구현시키고, 그 아이템들의 run 메서드를 호출할 수 있다.

Queue, DelayQueue 와 J2SE 5.0에서 바뀐 다른 Collections Framework에 대한 좀 더 많은 정보를 원한다면 Collection Framework Enhancements를 참조하기 바란다.


@http://kr.sun.com/developers/techtips/c2004_1019.html

2007/03/16 18:01 2007/03/16 18:01
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

HTTPURLCONNECTION를 사용하여 웹 페이지 액세스하기

이 글은 HttpURLConnection 과 이의 서브클래스인 HttpsURLConnection 을 사용하여 보안 웹 페이지에 액세스하는 방법을 보여준다. 또한 비보안 페이지(non-secure page)에서 보안 페이지(secure one)로의 리다이렉트를 쉽게 할 수 있는 방법도 볼 수 있다. HTTP 와 HTTPS에 관한 정보는 HTTP 1.1 RFC 2616HTTPS RFC 2818를 참고하기 바란다.

첫번째 예로, 주어진 URL에 접속하기 위해 다음 WebPageReader 프로그램의 HttpURLConnection 를 이용해 보자. 그리고 페이지의 내용을 스탠다드 아웃(standard out)에 출력하자.

[code] import java.net.URL; import java.net.MalformedURLException; import java.net.URLConnection; import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; public class WebPageReader { private static URLConnection connection; private static void connect( String urlString ) { try { URL url = new URL(urlString); connection = url.openConnection(); //for jdk 1.5 or higher connection.setConnectionTimeout(1000); connection.setReadTimeout(3000); //for jdk 1.4 above System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "50000"); } catch (MalformedURLException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static void readContents() { BufferedReader in = null; try { in = new BufferedReader( new InputStreamReader( connection.getInputStream())); String inputLine; while ( (inputLine = in.readLine()) != null) { System.out.println(inputLine); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java WebPageReader " + "<url>"); System.exit(0); } connect(args[0]); readContents(); } } [/code]

만약 현재의 위치가 방화벽 뒤라면, 다음과 같이 설정된 proxyHost과 proxyPort 변수들이 필요하다.

[code] http.proxyHost=webcache http.proxyPort=8080 https.proxyHost=webcache https.proxyPort=8080 [/code]

커맨드 라인에 -D플래그를 이용해서 값을 직접 입력하거나 프로그램 상에서 System.setProperty()를 호출함으로써 변수를 구할 수있다. WebPageReader 를 컴파일한 후에 이하와 같은 커맨드로 Core Java Technologies Tech Tips 홈 페이지의 내용을 열거해 볼 수 있다.

[code] java WebPageReader http://java.sun.com/developer/JDCTechTips/ [/code]

URLConnection는 추상 클래스이다. URL클래스의 openConnection() 메소드는 지정된 URL을 읽을 수 있도록 적절하고 구체적인 서브클래스를 리턴한다. http나 https URL을 입력하면 이는 HttpURLConnection나 HttpsURLConnection의 서브클래스가 될 것이다. 만약 다음을 openConnection()를 호출하는 라인에 추가하면,

[code] System.out.println(connection.getClass()); [/code]

HttpURLConnection의 숨겨진 구현 클래스의 인스턴스를 리턴하는 커넥션(connection)을 보게 된다. 예를 들면, 다음과 같다.

[code] class sun.net.www.protocol.http.HttpURLConnection [/code]

이와 유사하게 보안 페이지를 읽기 위해 동일한 WebPageReader코드를 사용할 수가 있다.

[code] java WebPageReader https://today.dev.java.net [/code]

후자의 경우, 커넥션은 HttpsURLConnection의 서브클래스인 HttpURLConnection타입이라는 것을 알아야 한다. 보다 명확하게 말하자면 다음과 같은 숨어있는 구현 클래스가 있다는 것을 인식할 수 있어야 한다.

[code] class sun.net.www.protocol.https.HttpsURLConnectionImpl [/code]

일반적으로 브라우저에 URL을 입력할 때, 이동하고자하는 페이지가 보안 페이지인지 아닌지는 알 수가 없다. 다시 말하면, today.dev.java.net 페이지를 보기 위해서는 http://today.dev.java.net를 입력한다. 필요하다면 브라우저가 리다이렉트할 것이고 사용자를 https://today.dev.java.net로 연결하기 위해 적절한 신호변경을 수행한다. WebPageReader프로그램이 요청된 리다이렉션을 수행하는지를 살펴보자.

[code] java WebPageReader http://today.dev.java.net [/code]

원하는 페이지로 리다이렉트되는 대신에 다음과 같은 메시지를 보게 된다.

[code] <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <HTML><HEAD> <TITLE>301 Moved Permanently</TITLE> </HEAD><BODY> <H1>Moved Permanently</H1> The document has moved <A HREF="https://today.dev.java.net/">here</A>.<P> <HR> <ADDRESS> Apache/1.3.26 Server at today.dev.java.net Port 80 </ADDRESS> </BODY></HTML> [/code]

이 정보로부터 무언가를 읽어내는 것은 어렵지만 문제는 리다이렉션에 관한 것이 아니다. URL http://linux.java.net으로 프로그램을 실행하면, 프로그램은 http://community.java.net/linux으로 적절히 이를 리다이렉트하고 사용자는 원하는 컨텐츠를 볼 수가 있다. 어떤 일이 일어나는지를 자세히 살펴보려면 HttpURLConnection를 명시적으로 이용해야 한다. 스탠다드 아웃에 웹 페이지의 컨텐츠를 출력하는 코드를 삭제해서 일을 간단히 해보자. RedirectingReader프로그램이다.

[code] import java.net.URL; import java.net.MalformedURLException; import java.net.HttpURLConnection; import java.io.IOException; public class RedirectingReader { private static HttpURLConnection connection; private static void connect( String urlString ) { try { URL url = new URL(urlString); connection = (HttpURLConnection)url.openConnection(); //for jdk 1.5 or higher connection.setConnectionTimeout(1000); connection.setReadTimeout(3000); //for jdk 1.4 above System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "30000"); System.out.println(connection.getURL()); System.out.println( connection.getResponseCode() + " " + connection.getResponseMessage()); System.out.println(connection.getURL()); } catch (MalformedURLException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { if (args.length != 1) { System.err.println( "usage: java WebPageReader " + "<url>"); System.exit(0); } connect(args[0]); } } [/code]

다음과 같이 리다이렉트하는 URL을 입력하여 RedirectingReader 를 컴파일하고 실행해 보자.

[code] java RedirectingReader http://linux.java.net/ [/code]

다음과 같은 출력값을 보게 될 것이다.

[code] http://linux.java.net/ 200 OK http://community.java.net/linux/ [/code]

첫번째 라인이 재빨리 나타난다. String값으로 URL을 생성하고 HttpURLConnection객체를 실행하자. 그러면 http://linux.java.net를 요청하는 동안에 잠깐의 멈춤이 생긴다. 페이지가 리다이렉트되는 이 시간동안 받게 되는 응답 코드와 메시지는 리다이렉트된 페이지로부터 온 것이다. 이 리다이렉트된 페이지는 출력값의 3번째 라인에 열거된다. URL url = new URL(urlString);아래의 라인을 추가해서 리다이렉션을 허용하지 않았을 때 발생하는 현상을 보자.

[code] HttpURLConnection.setFollowRedirects(false); [/code]

출력값은 다음과 같다.

[code] http://linux.java.net/ 302 Found http://linux.java.net/ [/code]

이는 302 에러가 발생했다는 것을 나타낸다. 곧 리다이렉트되지 않고 URL이 그대로 유지되었다는 것이다. 이를 처리하기 전에, 좀 전에 추가했던 라인을 지우고 리다이렉션이 다시 작동하는지를 확인하기 위해 RedirectingReader 프로그램을 실행시켜보자. RedirectingReader를 다시 한번 실행시키고, http://today.dev.java.net 를 입력하자. 다음과 같은 출력값을 보게 된다.

[code] http://today.dev.java.net 301 Moved Permanently http://today.dev.java.net [/code]

위는 이 프로그램이 "Moved Permanently" 에러를 처리할 수 없어서 리다이렉트 했다는 것을 말한다. 이것이 바로 원했던 디폴트 속성이다. 보안문제 때문에 http 과 https간에는 리다이렉트할 수 없다. 어디로 리다이렉트할 것이냐하는 정보는 이 응답의 헤더 부분에 있다. 이전의 요청에 대한 전체 응답 헤더는 다음과 같다.

[code] HTTP/1.1 301 Moved Permanently Date: Tue, 03 Feb 2004 01:38:43 GMT Server: Apache/1.3.26 (Unix) mod_ssl/2.8.10 OpenSSL/0.9.6b mod_jk/1.2.1 Location: https://today.dev.java.net/ Content-type: text/html; charset=iso-8859-1 [/code]

HttpURLConnection 내의 getResponseCode() 과 getResponseMessage() 메소드가 이 응답의 첫번째 라인에 포함된 정보를 리턴하는 것을 본 적이 있을 것이다. 혹은 getHeaderField() 메소드를 이용해서 헤더 필드의 이름에 스트링값을 넣어주어도 된다. 가령, getHeaderField("Location")는 https://today.dev.java.net/값을 리턴한다.

이 글의 시작부분에서 언급했던 HTTP 1.1 과 HTTPS RFCs에서 요청과 응답 헤더의 포맷에 관해 자세히 살펴 볼 수 있다. 300 레벨 응답에서, "Location:"는 리다이렉션을 위한 위치값을 제공해야만 한다. 301나 302 에러를 발생하는지를 확인하기 위해 다음 사항을 추가하자.

[code] private static boolean mustRedirect(int code){ if (code == HttpURLConnection.HTTP_MOVED_PERM || code == HttpURLConnection.HTTP_MOVED_TEMP) { System.out.println("Received error " + code + ", trying secure redirect"); return true; } else return false; } [/code]

리다이렉트가 자동적으로 처리되지 않는다면 사용자는 301 나 302 응답 코드만을 받게 된다는 것을 기억하자. 지금까지 이것은 리다이렉트가 HttpURLConnection가 아닌 HttpsURLConnection가 필요하다는 것을 의미했다. "Location:" 필드에 제공된 정보를 따르자. 다음과 같이 새로운 정보를 이용해서 url 값과 커넥션을 재설정하자.

[code] url = new URL("https://"+ url.getHost() + url.getFile()); connection = (HttpsURLConnection)url.openConnection(); [/code]

마지막으로, 웹 리더를 받기 위해 위의 모든 사항을 모으고 필요할 때 보안 페이지로 리다이렉트될 수 있도록 하자.

[code] import javax.net.ssl.HttpsURLConnection; import java.net.URL; import java.net.MalformedURLException; import java.net.HttpURLConnection; import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; public class RedirectingReader { private static HttpURLConnection connection; private static URL url; private static void connect(String urlString) { try { url = new URL(urlString); connection = (HttpURLConnection) url.openConnection(); //for jdk 1.5 or higher connection.setConnectionTimeout(1000); connection.setReadTimeout(3000); //for jdk 1.4 above System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "50000"); int code = connection.getResponseCode(); if (mustRedirect(code)) secureRedirect( connection.getHeaderField("Location")); readContents(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static boolean mustRedirect(int code) { if (code == HttpURLConnection.HTTP_MOVED_PERM || code == HttpURLConnection.HTTP_MOVED_TEMP) { return true; } else return false; } private static void secureRedirect(String location) throws IOException { System.out.println(location); url = new URL(location); connection = (HttpsURLConnection) url.openConnection(); } private static void readContents() { BufferedReader in = null; try { in = new BufferedReader( new InputStreamReader( connection.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println(inputLine); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java WebPageReader " + "<url>"); System.exit(0); } connect(args[0]); } } [/code]

업데이트된 RedirectingReader 를 컴파일하고 동작이 제대로 이루어지는지 확인하기 위해 몇 개의 다른 입력값을 넣어 실행해보자. 예를 들면 http://today.dev.java.net 와 http://linux.java.net를 넣어보자. 리다이렉트되지 않는 보안페이지와 비보안 페이지를 입력해서 RedirectingReader를 실행해도 동작이 적절하게 이루어져야 한다. 이제 다시 뒤로 돌아가서 최초 WebReader 프로그램에 모든 println() 메소드를 제거하고 readContents()를 추가할 수 있다.


@http://kr.sun.com/developers/techtips/c2004_02_10.html

2007/03/15 12:58 2007/03/15 12:58
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. Blog Icon
    감사합니다.

    좋은 정보 감사합니다. 비공개로 담아갑니다 ^^;

  2. Blog Icon
    비밀방문자

    관리자만 볼 수 있는 댓글입니다.

  3. Blog Icon
    비밀방문자

    관리자만 볼 수 있는 댓글입니다.

JavaTM Cryptography Extension (JCE) Reference Guide


JavaTM Cryptography Extension (JCE)

Reference Guide

for the JavaTM 2 Platform Standard Edition Development Kit (JDK) 5.0



Introduction


What's New in JCE in the JDK 5.0


Cryptographic Concepts

Encryption and Decryption

Password-Based Encryption

Cipher

Key Agreement

Message Authentication Code


Core Classes

The Cipher Class

The Cipher Stream Classes

The CipherInputStream Class

The CipherOutputStream Class

The KeyGenerator Class

The SecretKeyFactory Class

The SealedObject Class

The KeyAgreement Class

The Mac Class


How to Make Applications "Exempt" from Cryptographic Restrictions


Installing JCE Providers for the JDK 5.0


JCE Keystore


Code Examples

Using Encryption

Using Password-Based Encryption

Using Key Agreement



Appendix A: Standard Names


Appendix B: SunJCE Default Keysizes


Appendix C: SunJCE Keysize Restrictions


Appendix D: Jurisdiction Policy File Format


Appendix E: Maximum Key Sizes Allowed by "Strong" Jurisdiction Policy Files


Appendix F: Sample Programs

Diffie-Hellman Key Exchange between 2 Parties

Diffie-Hellman Key Exchange between 3 Parties

Blowfish Example

HMAC-MD5 Example

Introduction

This document is intended as a companion to the JavaTM Cryptography Architecture (JCA) API Specification & Reference. References to chapters not present in this document are to chapters in the JCA Specification.

The JavaTM Cryptography Extension (JCE) provides a framework and implementations for encryption, key generation and key agreement, and Message Authentication Code (MAC) algorithms. Support for encryption includes symmetric, asymmetric, block, and stream ciphers. The software also supports secure streams and sealed objects.

JCE was previously an optional package (extension) to the JavaTM 2 SDK, Standard Edition (Java 2 SDK), versions 1.2.x and 1.3.x. JCE has been integrated into the Java 2 SDK since the 1.4 release.

JCE is based on the same design principles found elsewhere in the JCA: implementation independence and, whenever possible, algorithm independence. It uses the same "provider" architecture. Providers signed by a trusted entity can be plugged into the JCE framework, and new algorithms can be added seamlessly.

The JCE API covers:

  • Symmetric bulk encryption, such as DES, RC2, and IDEA
  • Symmetric stream encryption, such as RC4
  • Asymmetric encryption, such as RSA
  • Password-based encryption (PBE)
  • Key Agreement
  • Message Authentication Codes (MAC)

The JDK 5.0 release comes standard with a JCE provider named "SunJCE", which comes pre-installed and registered and which supplies the following cryptographic services:

  • An implementation of the DES (FIPS PUB 46-1), Triple DES, and Blowfish encryption algorithms in the Electronic Code Book (ECB), Cipher Block Chaining (CBC), Cipher Feedback (CFB), Output Feedback (OFB), and Propagating Cipher Block Chaining (PCBC) modes. (Note: Throughout this document, the terms "Triple DES" and "DES-EDE" will be used interchangeably.)
  • Key generators for generating keys suitable for the DES, Triple DES, Blowfish, HMAC-MD5, and HMAC-SHA1 algorithms.
  • An implementation of the MD5 with DES-CBC password-based encryption (PBE) algorithm defined in PKCS #5.
  • "Secret-key factories" providing bi-directional conversions between opaque DES, Triple DES and PBE key objects and transparent representations of their underlying key material.
  • An implementation of the Diffie-Hellman key agreement algorithm between two or more parties.
  • A Diffie-Hellman key pair generator for generating a pair of public and private values suitable for the Diffie-Hellman algorithm.
  • A Diffie-Hellman algorithm parameter generator.
  • A Diffie-Hellman "key factory" providing bi-directional conversions between opaque Diffie-Hellman key objects and transparent representations of their underlying key material.
  • Algorithm parameter managers for Diffie-Hellman, DES, Triple DES, Blowfish, and PBE parameters.
  • An implementation of the HMAC-MD5 and HMAC-SHA1 keyed-hashing algorithms defined in RFC 2104.
  • An implementation of the padding scheme described in PKCS #5.
  • A keystore implementation for the proprietary keystore type named "JCEKS".

A Note on Terminology

The JCE within the JDK 5.0 includes two software components:

  • the framework that defines and supports cryptographic services that providers can supply implementations for. This framework includes everything in the javax.crypto package.
  • a provider named "SunJCE"
Throughout this document, the term "JCE" by itself refers to the JCE framework in the JDK 5.0. Whenever the JCE provider supplied with the JDK 5.0 is mentioned, it will be referred to explicitly as the "SunJCE" provider.

What's New in JCE in the JDK 5.0

Here are the differences in JCE between v1.4 and 5.0:

Support for PKCS #11 Based Crypto Provider

In JDK 5.0, a JCA/JCE provider, SunPKCS11 that acts as a generic gateway to the native PKCS#11 API has been implemented. PKCS#11 is the de-facto standard for crypto accelerators and also widely used to access cryptographic smartcards. The administrator/user can configure this provider to talk any PKCS#11 v2.x compliant token.

Here's an example of the configuration file format.

Integration with Solaris Cryptographic Framework

On Solaris 10, the default Java security provider configuration has been changed in JDK 5.0 to include an instance of the SunPKCS11 provider that uses the Solaris Cryptographic Framework. It is the provider with the highest precedence thereby allowing all existing applications to take advantage of the improved performance on Solaris 10. There is no change in behavior on Solaris 8 and Solaris 9 systems.

As a result of this change, many cryptographic operations will execute several times as fast as before on all Solaris 10 systems. On systems with cryptographic hardware acceleration, the performance improvements may be two orders of magnitude.

Support for ECC Algorithm

Prior to JDK 5.0 the JCA/JCE framework did not include support classes for ECC-related crypto algorithms. Users who wanted to use ECC had to depend on a 3rd party library that implemented ECC. However, this did not integrate well with existing JCA/JCE framework.

Starting in JDK 5.0, full support for ECC classes to facilitate providers that support ECC have been included.

The following interfaces have been added:

The following classes have been added:

Added ByteBuffer API Support

Methods that take ByteBuffer arguments have been added to the JCE API and SPI classes that are used to process bulk data. Providers can override the engine* methods if they can process ByteBuffers more efficiently than byte[].

The following JCE methods have been added to support ByteBuffers:

    javax.crypto.Mac.update(ByteBuffer input)
javax.crypto.MacSpi.engineUpdate(ByteBuffer input)
javax.crypto.Cipher.update(ByteBuffer input, ByteBuffer output)
javax.crypto.Cipher.doFinal(ByteBuffer input, ByteBuffer output)
javax.crypto.CipherSpi.engineUpdate(ByteBuffer input, ByteBuffer output)
javax.crypto.CipherSpi.engineDoFinal(ByteBuffer input, ByteBuffer output)
The following JCA methods have been added to support ByteBuffers:
    java.security.MessageDigest.update(ByteBuffer input)
java.security.Signature.update(ByteBuffer data)
java.security.SignatureSpi.engineUpdate(ByteBuffer data)
java.security.MessageDigestSpi.engineUpdate(ByteBuffer input)

Support for RC2ParameterSpec

The RC2 algorithm implementation has been enhanced in JDK 5.0 to support effective key size that is distinct from the length of the input key.

Full support for XML Encryption RSA-OAEP Algorithm

Prior to JDK 5.0, JCE did not define any parameter class for specifying the non-default values used in OAEP and PSS padding as defined in PKCS#1 v2.1 and the RSA-OAEP Key Transport algorithm in the W3C Recommendation for XML Encryption. Therefore, there was no generic way for applications to specify non-default values used in OAEP and PSS padding.

In JDK 5.0, new parameter classes have been added to fully support OAEP padding and the existing PSS parameter class was enhanced with APIs to fully support RSA PSS signature implementations. Also, SunJCE provider has been enhanced to accept OAEPParameterSpec when OAEPPadding is used.

The following classes have been added:

The following methods and fields have been added to java.security.spec.PSSParameterSpec:

    public static final PSSParameterSpec DEFAULT
public PSSParameterSpec(String mdName, String mgfName,
AlgorithmParameterSpec mgfSpec,
int saltLen, int trailerField)
public String getDigestAlgorithm()
public String getMGFAlgorithm()
public AlgorithmParameterSpec getMGFParameters()
public int getTrailerField()

Simplified Retrieval of PKCS8EncodedKeySpec from javax.crypto.EncryptedPrivateKeyInfo

In JDK 5.0, javax.crypto.EncryptedPrivateKeyInfo only has one method, getKeySpec(Cipher) for retrieving the PKCS8EncodedKeySpec from the encrypted data. This limitation requires users to specify a cipher which is initialized with the decryption key and parameters. When users only have the decryption key, they would have to first retrieve the parameters out of this EncryptedPrivateKeyInfo object, get hold of matching Cipher implementation, initialize it, and then call the getKeySpec(Cipher) method.

To make EncyptedPrivateKeyInfo easier to use and to make its API consistent with javax.crypto.SealedObject, the following methods have been added to javax.crypto.EncryptedPrivateKeyInfo:

    getKeySpec(Key decryptKey)
getKeySpec(Key decryptKey, String provider)

Ability to Dynamically Determine Maximum Allowable Key Length

In 1.4.2, the crypto jurisdiction policy files bundled in J2SE limits the maximum key length (and parameter value for some crypto algorithms) that can be used for encryption/decryption. Users who desire unlimited version of crypto jurisdiction files must download them separately.

Also, an exception is thrown when the Cipher instance is initialized with keys (or parameters for certain crypto algorithms) exceeds the maximum values allowed by the crypto jurisdiction files.

In JDK 5.0, the Cipher class has been updated to provide the maximum values for key length and parameters configured in the jurisdiction policy files, so that applications can use a shorter key length when the default (limited strength) jurisdiction policy files are installed.

The following methods have been added to javax.crypto.Cipher:

    public static final int getMaxAllowedKeyLength(String transformation)
throws NoSuchAlgorithmException

public static final AlgorithmParameterSpec
getMaxAllowedParameterSpec(String transformation)
throws NoSuchAlgorithmException;

Support for HmacSHA256, HmacSHA384, HmacSHA512

Support for HmacSHA-256, HmacSHA-384, and HmacSHA-512 algorithms have been added to JDK 5.0.

Support for RSA Encryption to SunJCE Provider

A publicly accessible RSA encryption implementation has been added to the SunJCE provider.

Support for RC2 and ARCFOUR Ciphers to SunJCE Provider

The SunJCE provider now implements the RC2 (RFC 2268) and ARCFOUR (an RC4TM-compatible algorithm) ciphers.

Support for "PBEWithSHA1AndDESede" and "PBEWithSHA1AndRC2_40" Ciphers

Added support for PBEWithSHA1AndDESede and PBEWithSHA1AndRC2_40 ciphers in SunJCE provider.

Support for XML Encryption Padding Algorithm in JCE Block Encryption Ciphers

W3C XML Encryption defines a new padding algorithm, "ISO10126Padding," for block ciphers. See 5.2 Block Encryption Algorithms for more information.

To allow Sun's provider to be used by XML Encryption implementations and JSR 106 providers, we have added support for this padding in JDK 5.0.

Cryptographic Concepts

This section provides a high-level description of the concepts implemented by the API, and the exact meaning of the technical terms used in the API specification.

Encryption and Decryption

Encryption is the process of taking data (called cleartext) and a short string (a key), and producing data (ciphertext) meaningless to a third-party who does not know the key. Decryption is the inverse process: that of taking ciphertext and a short key string, and producing cleartext.

Password-Based Encryption

Password-Based Encryption (PBE) derives an encryption key from a password. In order to make the task of getting from password to key very time-consuming for an attacker, most PBE implementations will mix in a random number, known as a salt, to create the key.

Cipher

Encryption and decryption are done using a cipher. A cipher is an object capable of carrying out encryption and decryption according to an encryption scheme (algorithm).

Key Agreement

Key agreement is a protocol by which 2 or more parties can establish the same cryptographic keys, without having to exchange any secret information.

Message Authentication Code

A Message Authentication Code (MAC) provides a way to check the integrity of information transmitted over or stored in an unreliable medium, based on a secret key. Typically, message authentication codes are used between two parties that share a secret key in order to validate information transmitted between these parties.

A MAC mechanism that is based on cryptographic hash functions is referred to as HMAC. HMAC can be used with any cryptographic hash function, e.g., MD5 or SHA-1, in combination with a secret shared key. HMAC is specified in RFC 2104.


Core Classes

  • The Cipher Class

    The Cipher class provides the functionality of a cryptographic cipher used for encryption and decryption. It forms the core of the JCE framework.

    Creating a Cipher Object

    Like other engine classes in the API, Cipher objects are created using the getInstance factory methods of the Cipher class. A factory method is a static method that returns an instance of a class, in this case, an instance of Cipher, which implements a requested transformation.

    To create a Cipher object, you must specify the transformation name. You may also specify which provider you want to supply the implementation of the requested transformation:

        
    public static Cipher getInstance(String transformation);

    public static Cipher getInstance(String transformation,
    String provider);

    If just a transformation name is specified, the system will determine if there is an implementation of the requested transformation available in the environment, and if there is more than one, if there is a preferred one.

    If both a transformation name and a package provider are specified, the system will determine if there is an implementation of the requested transformation in the package requested, and throw an exception if there is not.

    A transformation is a string that describes the operation (or set of operations) to be performed on the given input, to produce some output. A transformation always includes the name of a cryptographic algorithm (e.g., DES), and may be followed by a mode and padding scheme.

    A transformation is of the form:

    For example, the following are valid transformations:

        "DES/CBC/PKCS5Padding"

    "DES"

    If no mode or padding is specified, provider-specific default values for the mode and padding scheme are used. For example, the SunJCE provider uses ECB as the default mode, and PKCS5Padding as the default padding scheme for DES, DES-EDE and Blowfish ciphers. This means that in the case of the SunJCE provider,

        Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");

    and

        Cipher c1 = Cipher.getInstance("DES");

    are equivalent statements.

    When requesting a block cipher in stream cipher mode (e.g., DES in CFB or OFB mode), you may optionally specify the number of bits to be processed at a time, by appending this number to the mode name as shown in the "DES/CFB8/NoPadding" and "DES/OFB32/PKCS5Padding" transformations. If no such number is specified, a provider-specific default is used. (For example, the SunJCE provider uses a default of 64 bits.)

    Appendix A of this document contains a list of standard names that can be used to specify the algorithm name, mode, and padding scheme components of a transformation.

    The objects returned by factory methods are uninitialized, and must be initialized before they become usable.

    Initializing a Cipher Object

    A Cipher object obtained via getInstance must be initialized for one of four modes, which are defined as final integer constants in the Cipher class. The modes can be referenced by their symbolic names, which are shown below along with a description of the purpose of each mode:

    • ENCRYPT_MODE
      Encryption of data.
    • DECRYPT_MODE
      Decryption of data.
    • WRAP_MODE
      Wrapping a Key into bytes so that the key can be securely transported.
    • UNWRAP_MODE
      Unwrapping of a previously wrapped key into a java.security.Key object.

    Each of the Cipher initialization methods takes a mode parameter (opmode), and initializes the Cipher object for that mode. Other parameters include the key (key) or certificate containing the key (certificate), algorithm parameters (params), and a source of randomness (random).

    To initialize a Cipher object, call one of the following init methods:

        public void init(int opmode, Key key);

    public void init(int opmode, Certificate certificate)

    public void init(int opmode, Key key,
    SecureRandom random);

    public void init(int opmode, Certificate certificate,
    SecureRandom random)

    public void init(int opmode, Key key,
    AlgorithmParameterSpec params);

    public void init(int opmode, Key key,
    AlgorithmParameterSpec params,
    SecureRandom random);

    public void init(int opmode, Key key,
    AlgorithmParameters params)

    public void init(int opmode, Key key,
    AlgorithmParameters params,
    SecureRandom random)

    If a Cipher object that requires parameters (e.g., an initialization vector) is initialized for encryption, and no parameters are supplied to the init method, the underlying cipher implementation is supposed to supply the required parameters itself, either by generating random parameters or by using a default, provider-specific set of parameters.

    However, if a Cipher object that requires parameters is initialized for decryption, and no parameters are supplied to the init method, an InvalidKeyException or InvalidAlgorithmParameterException exception will be raised, depending on the init method that has been used.

    See the section about Managing Algorithm Parameters for more details.

    The same parameters that were used for encryption must be used for decryption.

    Note that when a Cipher object is initialized, it loses all previously-acquired state. In other words, initializing a Cipher is equivalent to creating a new instance of that Cipher, and initializing it. For example, if a Cipher is first initialized for decryption with a given key, and then initialized for encryption, it will lose any state acquired while in decryption mode.

    Encrypting and Decrypting Data

    Data can be encrypted or decrypted in one step (single-part operation) or in multiple steps (multiple-part operation). A multiple-part operation is useful if you do not know in advance how long the data is going to be, or if the data is too long to be stored in memory all at once.

    To encrypt or decrypt data in a single step, call one of the doFinal methods:

        public byte[] doFinal(byte[] input);

    public byte[] doFinal(byte[] input, int inputOffset,
    int inputLen);

    public int doFinal(byte[] input, int inputOffset,
    int inputLen, byte[] output);

    public int doFinal(byte[] input, int inputOffset,
    int inputLen, byte[] output, int outputOffset)

    To encrypt or decrypt data in multiple steps, call one of the update methods:

        public byte[] update(byte[] input);

    public byte[] update(byte[] input, int inputOffset, int inputLen);

    public int update(byte[] input, int inputOffset, int inputLen,
    byte[] output);

    public int update(byte[] input, int inputOffset, int inputLen,
    byte[] output, int outputOffset)

    A multiple-part operation must be terminated by one of the above doFinal methods (if there is still some input data left for the last step), or by one of the following doFinal methods (if there is no input data left for the last step):

        public byte[] doFinal();

    public int doFinal(byte[] output, int outputOffset);

    All the doFinal methods take care of any necessary padding (or unpadding), if padding (or unpadding) has been requested as part of the specified transformation.

    A call to doFinal resets the Cipher object to the state it was in when initialized via a call to init. That is, the Cipher object is reset and available to encrypt or decrypt (depending on the operation mode that was specified in the call to init) more data.

    Wrapping and Unwrapping Keys

    Wrapping a key enables secure transfer of the key from one place to another.

    The wrap/unwrap API makes it more convenient to write code since it works with key objects directly. These methods also enable the possibility of secure transfer of hardware-based keys.

    To wrap a Key, first initialize the Cipher object for WRAP_MODE, and then call the following:

        public final byte[] wrap(Key key);

    If you are supplying the wrapped key bytes (the result of calling wrap) to someone else who will unwrap them, be sure to also send additional information the recipient will need in order to do the unwrap:

    1. the name of the key algorithm, and
    2. the type of the wrapped key (one of Cipher.SECRET_KEY, Cipher.PRIVATE_KEY, or Cipher.PUBLIC_KEY).

    The key algorithm name can be determined by calling the getAlgorithm method from the Key interface:

        public String getAlgorithm();

    To unwrap the bytes returned by a previous call to wrap, first initialize a Cipher object for UNWRAP_MODE, then call the following:

        public final Key unwrap(byte[] wrappedKey,
    String wrappedKeyAlgorithm,
    int wrappedKeyType));

    Here, wrappedKey is the bytes returned from the previous call to wrap, wrappedKeyAlgorithm is the algorithm associated with the wrapped key, and wrappedKeyType is the type of the wrapped key. This must be one of Cipher.SECRET_KEY, Cipher.PRIVATE_KEY, or Cipher.PUBLIC_KEY.

    Managing Algorithm Parameters

    The parameters being used by the underlying Cipher implementation, which were either explicitly passed to the init method by the application or generated by the underlying implementation itself, can be retrieved from the Cipher object by calling its getParameters method, which returns the parameters as a java.security.AlgorithmParameters object (or null if no parameters are being used). If the parameter is an initialization vector (IV), it can also be retrieved by calling the getIV method.

    In the following example, a Cipher object implementing password-based encryption is initialized with just a key and no parameters. However, the selected algorithm for password-based encryption requires two parameters - a salt and an iteration count. Those will be generated by the underlying algorithm implementation itself. The application can retrieve the generated parameters from the Cipher object as follows:

        import javax.crypto.*;
    import java.security.AlgorithmParameters;

    // get cipher object for password-based encryption
    Cipher c = Cipher.getInstance("PBEWithMD5AndDES");

    // initialize cipher for encryption, without supplying
    // any parameters. Here, "myKey" is assumed to refer
    // to an already-generated key.
    c.init(Cipher.ENCRYPT_MODE, myKey);

    // encrypt some data and store away ciphertext
    // for later decryption
    byte[] cipherText = c.doFinal("This is just an example".getBytes());

    // retrieve parameters generated by underlying cipher
    // implementation
    AlgorithmParameters algParams = c.getParameters();

    // get parameter encoding and store it away
    byte[] encodedAlgParams = algParams.getEncoded();

    The same parameters that were used for encryption must be used for decryption. They can be instantiated from their encoding and used to initialize the corresponding Cipher object for decryption, as follows:

        import javax.crypto.*;
    import java.security.AlgorithmParameters;

    // get parameter object for password-based encryption
    AlgorithmParameters algParams;
    algParams =
    AlgorithmParameters.getInstance("PBEWithMD5AndDES");

    // initialize with parameter encoding from above
    algParams.init(encodedAlgParams);

    // get cipher object for password-based encryption
    Cipher c = Cipher.getInstance("PBEWithMD5AndDES");

    // initialize cipher for decryption, using one of the
    // init() methods that takes an AlgorithmParameters
    // object, and pass it the algParams object from above
    c.init(Cipher.DECRYPT_MODE, myKey, algParams);

    If you did not specify any parameters when you initialized a Cipher object, and you are not sure whether or not the underlying implementation uses any parameters, you can find out by simply calling the getParameters method of your Cipher object and checking the value returned. A return value of null indicates that no parameters were used.

    The following cipher algorithms implemented by the SunJCE provider use parameters:

    • DES, DES-EDE, and Blowfish, when used in feedback (i.e., CBC, CFB, OFB, or PCBC) mode, use an initialization vector (IV). The javax.crypto.spec.IvParameterSpec class can be used to initialize a Cipher object with a given IV.
    • PBEWithMD5AndDES uses a set of parameters, comprising a salt and an iteration count. The javax.crypto.spec.PBEParameterSpec class can be used to initialize a Cipher object implementing PBEWithMD5AndDES with a given salt and iteration count.

    Note that you do not have to worry about storing or transferring any algorithm parameters for use by the decryption operation if you use the SealedObject class. This class attaches the parameters used for sealing (encryption) to the encrypted object contents, and uses the same parameters for unsealing (decryption).

    Cipher Output Considerations

    Some of the update and doFinal methods of Cipher allow the caller to specify the output buffer into which to encrypt or decrypt the data. In these cases, it is important to pass a buffer that is large enough to hold the result of the encryption or decryption operation.

    The following method in Cipher can be used to determine how big the output buffer should be:

        public int getOutputSize(int inputLen)
  • The Cipher Stream Classes

    JCE introduces the concept of secure streams, which combine an InputStream or OutputStream with a Cipher object. Secure streams are provided by the CipherInputStream and CipherOutputStream classes.

    • The CipherInputStream Class

      This class is a FilterInputStream that encrypts or decrypts the data passing through it. It is composed of an InputStream, or one of its subclasses, and a Cipher. CipherInputStream represents a secure input stream into which a Cipher object has been interposed. The read methods of CipherInputStream return data that are read from the underlying InputStream but have additionally been processed by the embedded Cipher object. The Cipher object must be fully initialized before being used by a CipherInputStream.

      For example, if the embedded Cipher has been initialized for decryption, the CipherInputStream will attempt to decrypt the data it reads from the underlying InputStream before returning them to the application.

      This class adheres strictly to the semantics, especially the failure semantics, of its ancestor classes java.io.FilterInputStream and java.io.InputStream. This class has exactly those methods specified in its ancestor classes, and overrides them all, so that the data are additonally processed by the embedded cipher. Moreover, this class catches all exceptions that are not thrown by its ancestor classes. In particular, the skip(long) method skips only data that has been processed by the Cipher.

      It is crucial for a programmer using this class not to use methods that are not defined or overridden in this class (such as a new method or constructor that is later added to one of the super classes), because the design and implementation of those methods are unlikely to have considered security impact with regard to CipherInputStream.

      As an example of its usage, suppose cipher1 has been initialized for encryption. The code below demonstrates how to use a CipherInputStream containing that cipher and a FileInputStream in order to encrypt input stream data:

          FileInputStream fis;
      FileOutputStream fos;
      CipherInputStream cis;

      fis = new FileInputStream("/tmp/a.txt");
      cis = new CipherInputStream(fis, cipher1);
      fos = new FileOutputStream("/tmp/b.txt");
      byte[] b = new byte[8];
      int i = cis.read(b);
      while (i != -1) {
      fos.write(b, 0, i);
      i = cis.read(b);
      }

      The above program reads and encrypts the content from the file /tmp/a.txt and then stores the result (the encrypted bytes) in /tmp/b.txt.

      The following example demonstrates how to easily connect several instances of CipherInputStream and FileInputStream. In this example, assume that cipher1 and cipher2 have been initialized for encryption and decryption (with corresponding keys), respectively.

          FileInputStream fis;
      FileOutputStream fos;
      CipherInputStream cis1, cis2;

      fis = new FileInputStream("/tmp/a.txt");
      cis1 = new CipherInputStream(fis, cipher1);
      cis2 = new CipherInputStream(cis1, cipher2);
      fos = new FileOutputStream("/tmp/b.txt");
      byte[] b = new byte[8];
      int i = cis2.read(b);
      while (i != -1) {
      fos.write(b, 0, i);
      i = cis2.read(b);
      }

      The above program copies the content from file /tmp/a.txt to /tmp/b.txt, except that the content is first encrypted and then decrypted back when it is read from /tmp/a.txt. Of course since this program simply encrypts text and decrypts it back right away, it's actually not very useful except as a simple way of illustrating chaining of CipherInputStreams.

    • The CipherOutputStream Class

      This class is a FilterOutputStream that encrypts or decrypts the data passing through it. It is composed of an OutputStream, or one of its subclasses, and a Cipher. CipherOutputStream represents a secure output stream into which a Cipher object has been interposed. The write methods of CipherOutputStream first process the data with the embedded Cipher object before writing them out to the underlying OutputStream. The Cipher object must be fully initialized before being used by a CipherOutputStream.

      For example, if the embedded Cipher has been initialized for encryption, the CipherOutputStream will encrypt its data, before writing them out to the underlying output stream.

      This class adheres strictly to the semantics, especially the failure semantics, of its ancestor classes java.io.OutputStream and java.io.FilterOutputStream. This class has exactly those methods specified in its ancestor classes, and overrides them all, so that all data are additionally processed by the embedded cipher. Moreover, this class catches all exceptions that are not thrown by its ancestor classes.

      It is crucial for a programmer using this class not to use methods that are not defined or overridden in this class (such as a new method or constructor that is later added to one of the super classes), because the design and implementation of those methods are unlikely to have considered security impact with regard to CipherOutputStream.

      As an example of its usage, suppose cipher1 has been initialized for encryption. The code below demonstrates how to use a CipherOutputStream containing that cipher and a FileOutputStream in order to encrypt data to be written to an output stream:

          FileInputStream fis;
      FileOutputStream fos;
      CipherOutputStream cos;

      fis = new FileInputStream("/tmp/a.txt");
      fos = new FileOutputStream("/tmp/b.txt");
      cos = new CipherOutputStream(fos, cipher1);
      byte[] b = new byte[8];
      int i = fis.read(b);
      while (i != -1) {
      cos.write(b, 0, i);
      i = fis.read(b);
      }
      cos.flush();

      The above program reads the content from the file /tmp/a.txt, then encrypts and stores the result (the encrypted bytes) in /tmp/b.txt.

      The following example demonstrates how to easily connect several instances of CipherOutputStream and FileOutputStream. In this example, assume that cipher1 and cipher2 have been initialized for decryption and encryption (with corresponding keys), respectively:

          FileInputStream fis;
      FileOutputStream fos;
      CipherOutputStream cos1, cos2;

      fis = new FileInputStream("/tmp/a.txt");
      fos = new FileOutputStream("/tmp/b.txt");
      cos1 = new CipherOutputStream(fos, cipher1);
      cos2 = new CipherOutputStream(cos1, cipher2);
      byte[] b = new byte[8];
      int i = fis.read(b);
      while (i != -1) {
      cos2.write(b, 0, i);
      i = fis.read(b);
      }
      cos2.flush();

      The above program copies the content from file /tmp/a.txt to /tmp/b.txt, except that the content is first encrypted and then decrypted back before it is written to /tmp/b.txt.

      There is one important difference between the flush and close methods of this class, which becomes even more relevant if the encapsulated Cipher object implements a block cipher algorithm with padding turned on:

      flush flushes the underlying OutputStream by forcing any buffered output bytes that have already been processed by the encapsulated Cipher object to be written out. Any bytes buffered by the encapsulated Cipher object and waiting to be processed by it will not be written out.

      close closes the underlying OutputStream and releases any system resources associated with it. It invokes the doFinal method of the encapsulated Cipher object, causing any bytes buffered by it to be processed and written out to the underlying stream by calling its flush method.

  • The KeyGenerator Class

    A key generator is used to generate secret keys for symmetric algorithms.

    Creating a Key Generator

    Like other engine classes in the API, KeyGenerator objects are created using the getInstance factory methods of the KeyGenerator class. A factory method is a static method that returns an instance of a class, in this case, an instance of KeyGenerator which provides an implementation of the requested key generator.

    getInstance takes as its argument the name of a symmetric algorithm for which a secret key is to be generated. Optionally, a package provider name may be specified:

        public static KeyGenerator getInstance(String algorithm);

    public static KeyGenerator getInstance(String algorithm,
    String provider);

    If just an algorithm name is specified, the system will determine if there is an implementation of the requested key generator available in the environment, and if there is more than one, if there is a preferred one.

    If both an algorithm name and a package provider are specified, the system will determine if there is an implementation of the requested key generator in the package requested, and throw an exception if there is not.

    Initializing a KeyGenerator Object

    A key generator for a particular symmetric-key algorithm creates a symmetric key that can be used with that algorithm. It also associates algorithm-specific parameters (if any) with the generated key.

    There are two ways to generate a key: in an algorithm-independent manner, and in an algorithm-specific manner. The only difference between the two is the initialization of the object:

    • Algorithm-Independent Initialization

      All key generators share the concepts of a keysize and a source of randomness. There is an init method that takes these two universally shared types of arguments. There is also one that takes just a keysize argument, and uses a system-provided source of randomness, and one that takes just a source of randomness:

          public void init(SecureRandom random);

      public void init(int keysize);

      public void init(int keysize, SecureRandom random);

      Since no other parameters are specified when you call the above algorithm-independent init methods, it is up to the provider what to do about the algorithm-specific parameters (if any) to be associated with the generated key.

    • Algorithm-Specific Initialization

      For situations where a set of algorithm-specific parameters already exists, there are two init methods that have an AlgorithmParameterSpec argument. One also has a SecureRandom argument, while the source of randomness is system-provided for the other:

          public void init(AlgorithmParameterSpec params);

      public void init(AlgorithmParameterSpec params,
      SecureRandom random);

    In case the client does not explicitly initialize the KeyGenerator (via a call to an init method), each provider must supply (and document) a default initialization.

    Creating a Key

    The following method generates a secret key:
        public SecretKey generateKey();
  • The SecretKeyFactory Class

    This class represents a factory for secret keys.

    Key factories are used to convert keys (opaque cryptographic keys of type java.security.Key) into key specifications (transparent representations of the underlying key material in a suitable format), and vice versa.

    A javax.crypto.SecretKeyFactory object operates only on secret (symmetric) keys, whereas a java.security.KeyFactory object processes the public and private key components of a key pair.

    Objects of type java.security.Key, of which java.security.PublicKey, java.security.PrivateKey, and javax.crypto.SecretKey are subclasses, are opaque key objects, because you cannot tell how they are implemented. The underlying implementation is provider-dependent, and may be software or hardware based. Key factories allow providers to supply their own implementations of cryptographic keys.

    For example, if you have a key specification for a Diffie Hellman public key, consisting of the public value y, the prime modulus p, and the base g, and you feed the same specification to Diffie-Hellman key factories from different providers, the resulting PublicKey objects will most likely have different underlying implementations.

    A provider should document the key specifications supported by its secret key factory. For example, the SecretKeyFactory for DES keys supplied by the "SunJCE" provider supports DESKeySpec as a transparent representation of DES keys, the SecretKeyFactory for DES-EDE keys supports DESedeKeySpec as a transparent representation of DES-EDE keys, and the SecretKeyFactory for PBE supports PBEKeySpec as a transparent representation of the underlying password.

    The following is an example of how to use a SecretKeyFactory to convert secret key data into a SecretKey object, which can be used for a subsequent Cipher operation:

        // Note the following bytes are not realistic secret key data 
    // bytes but are simply supplied as an illustration of using data
    // bytes (key material) you already have to build a DESKeySpec.
    byte[] desKeyData = { (byte)0x01, (byte)0x02, (byte)0x03,
    (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08 };
    DESKeySpec desKeySpec = new DESKeySpec(desKeyData);
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
    SecretKey secretKey = keyFactory.generateSecret(desKeySpec);

    In this case, the underlying implementation of secretKey is based on the provider of keyFactory.

    An alternative, provider-independent way of creating a functionally equivalent SecretKey object from the same key material is to use the javax.crypto.spec.SecretKeySpec class, which implements the javax.crypto.SecretKey interface:

        byte[] desKeyData = { (byte)0x01, (byte)0x02, ...};
    SecretKeySpec secretKey = new SecretKeySpec(desKeyData, "DES");
  • The SealedObject Class

    This class enables a programmer to create an object and protect its confidentiality with a cryptographic algorithm.

    Given any object that implements the java.io.Serializable interface, one can create a SealedObject that encapsulates the original object, in serialized format (i.e., a "deep copy"), and seals (encrypts) its serialized contents, using a cryptographic algorithm such as DES, to protect its confidentiality. The encrypted content can later be decrypted (with the corresponding algorithm using the correct decryption key) and de-serialized, yielding the original object.

    A typical usage is illustrated in the following code segment: In order to seal an object, you create a SealedObject from the object to be sealed and a fully initialized Cipher object that will encrypt the serialized object contents. In this example, the String "This is a secret" is sealed using the DES algorithm. Note that any algorithm parameters that may be used in the sealing operation are stored inside of SealedObject:

        // create Cipher object
    // Note: sKey is assumed to refer to an already-generated
    // secret DES key.
    Cipher c = Cipher.getInstance("DES");
    c.init(Cipher.ENCRYPT_MODE, sKey);

    // do the sealing
    SealedObject so = new SealedObject("This is a secret", c);

    The original object that was sealed can be recovered in two different ways:

    • by using a Cipher object that has been initialized with the exact same algorithm, key, padding scheme, etc., that were used to seal the object:
          c.init(Cipher.DECRYPT_MODE, sKey);
      try {
      String s = (String)so.getObject(c);
      } catch (Exception e) {
      // do something
      };

      This approach has the advantage that the party who unseals the sealed object does not require knowledge of the decryption key. For example, after one party has initialized the cipher object with the required decryption key, it could hand over the cipher object to another party who then unseals the sealed object.

    • by using the appropriate decryption key (since DES is a symmetric encryption algorithm, we use the same key for sealing and unsealing):
          try {
      String s = (String)so.getObject(sKey);
      } catch (Exception e) {
      // do something
      };

      In this approach, the getObject method creates a cipher object for the appropriate decryption algorithm and initializes it with the given decryption key and the algorithm parameters (if any) that were stored in the sealed object. This approach has the advantage that the party who unseals the object does not need to keep track of the parameters (e.g., the IV) that were used to seal the object.

  • The KeyAgreement Class

    The KeyAgreement class provides the functionality of a key agreement protocol. The keys involved in establishing a shared secret are created by one of the key generators (KeyPairGenerator or KeyGenerator), a KeyFactory, or as a result from an intermediate phase of the key agreement protocol.

    Creating a KeyAgreement Object

    Each party involved in the key agreement has to create a KeyAgreement object. Like other engine classes in the API, KeyAgreement objects are created using the getInstance factory methods of the KeyAgreement class. A factory method is a static method that returns an instance of a class, in this case, an instance of KeyAgreement which provides the requested key agreement algorithm.

    getInstance takes as its argument the name of a key agreement algorithm. Optionally, a package provider name may be specified:

        public static KeyAgreement getInstance(String algorithm);

    public static KeyAgreement getInstance(String algorithm,
    String provider);

    If just an algorithm name is specified, the system will determine if there is an implementation of the requested key agreement available in the environment, and if there is more than one, if there is a preferred one.

    If both an algorithm name and a package provider are specified, the system will determine if there is an implementation of the requested key agreement in the package requested, and throw an exception if there is not.

    Initializing a KeyAgreement Object

    You initialize a KeyAgreement object with your private information. In the case of Diffie-Hellman, you initialize it with your Diffie-Hellman private key. Additional initialization information may contain a source of randomness and/or a set of algorithm parameters. Note that if the requested key agreement algorithm requires the specification of algorithm parameters, and only a key, but no parameters are provided to initialize the KeyAgreement object, the key must contain the required algorithm parameters. (For example, the Diffie-Hellman algorithm uses a prime modulus p and a base generator g as its parameters.)

    To initialize a KeyAgreement object, call one of its init methods:

        public void init(Key key);

    public void init(Key key, SecureRandom random);

    public void init(Key key, AlgorithmParameterSpec params);

    public void init(Key key, AlgorithmParameterSpec params,
    SecureRandom random);

    Executing a KeyAgreement Phase

    Every key agreement protocol consists of a number of phases that need to be executed by each party involved in the key agreement.

    To execute the next phase in the key agreement, call the doPhase method:

        public Key doPhase(Key key, boolean lastPhase);

    The key parameter contains the key to be processed by that phase. In most cases, this is the public key of one of the other parties involved in the key agreement, or an intermediate key that was generated by a previous phase. doPhase may return an intermediate key that you may have to send to the other parties of this key agreement, so they can process it in a subsequent phase.

    The lastPhase parameter specifies whether or not the phase to be executed is the last one in the key agreeement: A value of FALSE indicates that this is not the last phase of the key agreement (there are more phases to follow), and a value of TRUE indicates that this is the last phase of the key agreement and the key agreement is completed, i.e., generateSecret can be called next.

    In the example of Diffie-Hellman between two parties (see Appendix F), you call doPhase once, with lastPhase set to TRUE. In the example of Diffie-Hellman between three parties, you call doPhase twice: the first time with lastPhase set to FALSE, the 2nd time with lastPhase set to TRUE.

    Generating the Shared Secret

    After each party has executed all the required key agreement phases, it can compute the shared secret by calling one of the generateSecret methods:

        public byte[] generateSecret();

    public int generateSecret(byte[] sharedSecret, int offset);

    public SecretKey generateSecret(String algorithm);
  • The Mac Class

    The Mac class provides the functionality of a Message Authentication Code (MAC). Please refer to the code example in Appendix F.

    Creating a Mac Object

    Like other engine classes in the API, Mac objects are created using the getInstance factory methods of the Mac class. A factory method is a static method that returns an instance of a class, in this case, an instance of Mac which provides the requested MAC algorithm.

    getInstance takes as its argument the name of a MAC algorithm. Optionally, a package provider name may be specified:

        public static Mac getInstance(String algorithm);

    public static Mac getInstance(String algorithm,
    String provider);

    If just an algorithm name is specified, the system will determine if there is an implementation of the requested MAC algorithm available in the environment, and if there is more than one, if there is a preferred one.

    If both an algorithm name and a package provider are specified, the system will determine if there is an implementation of the requested MAC algorithm in the package requested, and throw an exception if there is not.

    Initializing a Mac Object

    A Mac object is always initialized with a (secret) key and may optionally be initialized with a set of parameters, depending on the underlying MAC algorithm.

    To initialize a Mac object, call one of its init methods:

        public void init(Key key);

    public void init(Key key, AlgorithmParameterSpec params);

    You can initialize your Mac object with any (secret-)key object that implements the javax.crypto.SecretKey interface. This could be an object returned by javax.crypto.KeyGenerator.generateKey(), or one that is the result of a key agreement protocol, as returned by javax.crypto.KeyAgreement.generateSecret(), or an instance of javax.crypto.spec.SecretKeySpec.

    With some MAC algorithms, the (secret-)key algorithm associated with the (secret-)key object used to initialize the Mac object does not matter (this is the case with the HMAC-MD5 and HMAC-SHA1 implementations of the SunJCE provider). With others, however, the (secret-)key algorithm does matter, and an InvalidKeyException is thrown if a (secret-)key object with an inappropriate (secret-)key algorithm is used.

    Computing a MAC

    A MAC can be computed in one step (single-part operation) or in multiple steps (multiple-part operation). A multiple-part operation is useful if you do not know in advance how long the data is going to be, or if the data is too long to be stored in memory all at once.

    To compute the MAC of some data in a single step, call the following doFinal method:

        public byte[] doFinal(byte[] input);

    To compute the MAC of some data in multiple steps, call one of the update methods:

        public void update(byte input);

    public void update(byte[] input);

    public void update(byte[] input, int inputOffset, int inputLen);

    A multiple-part operation must be terminated by the above doFinal method (if there is still some input data left for the last step), or by one of the following doFinal methods (if there is no input data left for the last step):

        public byte[] doFinal();

    public void doFinal(byte[] output, int outOffset);

How to Make Applications "Exempt" from Cryptographic Restrictions

[Note 1: This section should be ignored by most application developers. It is only for people whose applications may be exported to those few countries whose governments mandate cryptographic restrictions, if it desired that such applications have fewer cryptographic restrictions than those mandated. If you want to skip this section, you can go on to Installing JCE Providers for JDK 5.0.]
[Note 2: Throughout this section, the term "application" is meant to encompass both applications and applets.]

The JCE framework within JDK 5.0 includes an ability to enforce restrictions regarding the cryptographic algorithms and maximum cryptographic strengths available to applets/applications in different jurisdiction contexts (locations). Any such restrictions are specified in "jurisdiction policy files".

Due to import control restrictions by the governments of a few countries, the jurisdiction policy files shipped with the JDK 5.0 from Sun Microsystems specify that "strong" but limited cryptography may be used. An "unlimited strength" version of these files indicating no restrictions on cryptographic strengths is available for those living in eligible countries (which is most countries). But only the "strong" version can be imported into those countries whose governments mandate restrictions. The JCE framework will enforce the restrictions specified in the installed jurisdiction policy files.

It is possible that the governments of some or all such countries may allow certain applications to become exempt from some or all cryptographic restrictions. For example, they may consider certain types of applications as "special" and thus exempt. Or they may exempt any application that utilizes an "exemption mechanism," such as key recovery. Applications deemed to be exempt could get access to stronger cryptography than that allowed for non-exempt applications in such countries.

In order for an application to be recognized as "exempt" at runtime, it must meet the following conditions:

  • It must have a permission policy file bundled with it in a JAR file. The permission policy file specifies what cryptography-related permissions the application has, and under what conditions (if any).
  • The JAR file containing the application and the permission policy file must have been signed using a code-signing certificate issued after the application was accepted as exempt.

Below are sample steps required in order to make an application exempt from some or all cryptographic restrictions. This is a basic outline that includes information about what is required by JCE in order to recognize and treat applications as being exempt. You will need to know the exemption requirements of the particular country or countries in which you would like your application to be able to be run but whose governments require cryptographic restrictions. You will also need to know the requirements of a JCE framework vendor that has a process in place for handling exempt applications. Consult such a vendor for further information. (Note: The SunJCE provider does not supply an implementation of the ExemptionMechanismSpi class.)

  • Step 1: Write and Compile Your Application Code
  • Step 2: Create a Permission Policy File Granting Appropriate Cryptographic Permissions
  • Step 3: Prepare for Testing
  • Step 3a: Apply for Government Approval From the Government Mandating Restrictions.
  • Step 3b: Get a Code-Signing Certificate
  • Step 3c: Bundle the Application and Permission Policy File into a JAR file
  • Step 3d: Sign the JAR file
  • Step 3e: Set Up Your Environment Like That of a User in a Restricted Country
  • Step 3f: (only for apps using exemption mechanisms) Install a Provider Implementing the Exemption Mechanism Specified in the Permission Policy File
  • Step 4: Test Your Application
  • Step 5: Apply for U.S. Government Export Approval If Required
  • Step 6: Deploy Your Application

Special Code Requirements for Applications that Use Exemption Mechanisms

When an application has a permission policy file associated with it (in the same JAR file) and that permission policy file specifies an exemption mechanism, then when the Cipher getInstance method is called to instantiate a Cipher, the JCE code searches the installed providers for one that implements the specified exemption mechanism. If it finds such a provider, JCE instantiates an ExemptionMechanism API object associated with the provider's implementation, and then associates the ExemptionMechanism object with the Cipher returned by getInstance.

After instantiating a Cipher, and prior to initializing it (via a call to the Cipher init method), your code must call the following Cipher method:

    public ExemptionMechanism getExemptionMechanism()

This call returns the ExemptionMechanism object associated with the Cipher. You must then initialize the exemption mechanism implementation by calling the following method on the returned ExemptionMechanism:

     public final void init(Key key)

The argument you supply should be the same as the argument of the same types that you will subsequently supply to a Cipher init method.

Once you have initialized the ExemptionMechanism, you can proceed as usual to initialize and use the Cipher.

Permission Policy Files

In order for an application to be recognized at runtime as being "exempt" from some or all cryptographic restrictions, it must have a permission policy file bundled with it in a JAR file. The permission policy file specifies what cryptography-related permissions the application has, and under what conditions (if any).

Note: The permission policy file bundled with an application must be named cryptoPerms.

The format of a permission entry in a permission policy file that accompanies an exempt application is the same as the format for a jurisdiction policy file downloaded with the JDK 5.0, which is:

permission <crypto permission class name>[ <alg_name>
[[, <exemption mechanism name>][, <maxKeySize>
[, <AlgorithmParameterSpec class name>,
<parameters for constructing an AlgorithmParameterSpec object>]]]];

See Appendix D for more information about the jurisdiction policy file format.

Permission Policy Files for Exempt Applications

Some applications may be allowed to be completely unrestricted. Thus, the permission policy file that accompanies such an application usually just needs to contain the following:

grant {
// There are no restrictions to any algorithms.
permission javax.crypto.CryptoAllPermission;
};

If an application just uses a single algorithm (or several specific algorithms), then the permission policy file could simply mention that algorithm (or algorithms) explicitly, rather than granting CryptoAllPermission. For example, if an application just uses the Blowfish algorithm, the permission policy file doesn't have to grant CryptoAllPermission to all algorithms. It could just specify that there is no cryptographic restriction if the Blowfish algorithm is used. In order to do this, the permission policy file would look like the following:

grant {
permission javax.crypto.CryptoPermission "Blowfish";
};

Permission Policy Files for Applications Exempt Due to Exemption Mechanisms

If an application is considered "exempt" if an exemption mechanism is enforced, then the permission policy file that accompanies the application must specify one or more exemption mechanisms. At runtime, the application will be considered exempt if any of those exemption mechanisms is enforced. Each exemption mechanism must be specified in a permission entry that looks like the following:

    // No algorithm restrictions if specified
// exemption mechanism is enforced.
permission javax.crypto.CryptoPermission *,
"<ExemptionMechanismName>";

where <ExemptionMechanismName> specifies the name of an exemption mechanism. The list of possible exemption mechanism names includes:

  • KeyRecovery
  • KeyEscrow
  • KeyWeakening
As an example, suppose your application is exempt if either key recovery or key escrow is enforced. Then your permission policy file should contain the following:
grant {
// No algorithm restrictions if KeyRecovery is enforced.
permission javax.crypto.CryptoPermission *,
"KeyRecovery";
// No algorithm restrictions if KeyEscrow is enforced.
permission javax.crypto.CryptoPermission *,
"KeyEscrow";
};

Note: Permission entries that specify exemption mechanisms should not also specify maximum key sizes. The allowed key sizes are actually determined from the installed exempt jurisdiction policy files, as described in the next section.

How Bundled Permission Policy Files Affect Cryptographic Permissions

At runtime, when an application instantiates a Cipher (via a call to its getInstance method) and that application has an associated permission policy file, JCE checks to see whether the permission policy file has an entry that applies to the algorithm specified in the getInstance call. If it does, and the entry grants CryptoAllPermission or does not specify that an exemption mechanism must be enforced, it means there is no cryptographic restriction for this particular algorithm.

If the permission policy file has an entry that applies to the algorithm specified in the getInstance call and the entry does specify that an exemption mechanism must be enforced, then the exempt jurisdiction policy file(s) are examined. If the exempt permissions include an entry for the relevant algorithm and exemption mechanism, and that entry is implied by the permissions in the permission policy file bundled with the application, and if there is an implementation of the specified exemption mechanism available from one of the registered providers, then the maximum key size and algorithm parameter values for the Cipher are determined from the exempt permission entry.

If there is no exempt permission entry implied by the relevant entry in the permission policy file bundled with the application, or if there is no implementation of the specified exemption mechanism available from any of the registered providers, then the application is only allowed the standard default cryptographic permissions.


Installing JCE Providers for JDK 5.0

In order to be used, a cryptographic provider must be installed and registered, either statically or dynamically. Cryptographic providers for JCE in JDK 5.0 are installed and configured the same way as all other providers for the JavaTM 2 platform. More information about installing and configuring providers can be found in the Installing Providers section of the JavaTM Cryptography Architecture API Specification & Reference document.

You do not need to register the "SunJCE" provider because it is pre-registered. If you want to use other providers, read the following sections to see how to register them.

Installing a provider is done in two steps: installing the provider package classes, and configuring the provider. In some situations you will also need to set permissions for the provider prior to using it.

Installing the Provider Classes

The first thing you must do is make the provider classes available so that they can be found when requested. Provider classes are shipped as a signed JAR (Java ARchive) file.

There are two possible ways to install the provider classes:

  • Install the JAR file containing the provider classes as an "installed" or "bundled" extension.
  • Place the JAR file containing the provider classes in your class path.

The provider JAR file will be considered an installed extension if it is placed in the standard place for the JAR files of an installed extension:

<java-home>/lib/ext         [Solaris]
<java-home>\lib\ext [Windows]

Here <java-home> refers to the directory where the runtime software is installed, which is the top-level directory of the JavaTM 2 Runtime Environment (JRE) or the jre directory in the JavaTM 2 SDK (Java 2 SDK) software. For example, if you have JDK 5.0 installed on Solaris in a directory named /home/user1/JDK1.5.0, or on Microsoft Windows in a directory named C:\Java DK1.5.0, then you need to install the JAR file in the following directory:

/home/user1/JDK1.5.0/jre/lib/ext    [Solaris]
C:\JDK1.5.0\jre\lib\ext [Windows]

Similarly, if you have the JRE 5.0 installed on Solaris in a directory named /home/user1/jre1.5.0, or on Microsoft Windows in a directory named C:\jre1.5.0, you need to install the JAR file in the following directory:

/home/user1/jre1.5.0/lib/ext         [Solaris]
C:\jre1.5.0\lib\ext [Windows]

For more information, refer to these sections in the "Extension Mechanism Architecture" specification: Installed Extensions and Bundled Extensions.

Configuring the Provider

The next step is to add the provider to your list of approved providers. This is done statically by editing the security properties file

<java-home>/lib/security/java.security     [Solaris]
<java-home>\lib\security\java.security [Windows]

Here <java-home> refers to the directory where the JRE was installed. For example, if you have JDK 5.0 installed on Solaris in a directory named /home/user1/JDK1.5.0, or on Microsoft Windows in a directory named C:\JDK1.5.0, then you need to edit the following file:

/home/user1/JDK1.5.0/jre/lib/security/java.security  [Solaris]
C:\JDK1.5.0\jre\lib\security\java.security [Windows]

Similarly, if you have the Java 2 Runtime Environment, v 1.4 installed on Solaris in a directory named /home/user1/jre1.5.0, or on Microsoft Windows in a directory named C:\jre1.5.0, then you need to edit this file:

/home/user1/jre1.5.0/lib/security/java.security       [Solaris]
C:\jre1.5.0\lib\security\java.security [Windows]

For each provider, this file should have a statement of the following form:

    security.provider.n=masterClassName
    

This declares a provider, and specifies its preference order n. The preference order is the order in which providers are searched for requested algorithms when no specific provider is requested. The order is 1-based; 1 is the most preferred, followed by 2, and so on.

masterClassName must specify the fully qualified name of the provider's "master class". The provider vendor should supply you this name.

JDK 5.0 comes standard with a provider named "SUN", which is automatically configured as a static provider in the java.security properties file, as follows:

security.provider.1=sun.security.provider.Sun

(The "SUN" provider's master class is the Sun class in the sun.security.provider package.)

The JCE provider "SunJCE" and other security-related providers shipped with the Java 2 platform are also automatically configured as static providers.

To utilize another JCE provider, add a line registering the alternate provider, giving it whatever preference order you prefer (and making corresponding adjustments to the other providers' orders, if needed).

Suppose that the master class of a provider you want to register is the CryptoX class in the com.cryptox.provider package, and that you would like to make this provider the second preferred provider. To do so, add the following line to the java.security file below the line for the "SUN" provider, and increment the preference order numbers for all other providers whose numbers were greater than or equal to 2 before your addition:

    security.provider.2=com.cryptox.provider.CryptoX
Note: Providers may also be registered dynamically. To do so, a program can call either the addProvider or insertProviderAt method in the Security class. This type of registration is not persistent and can only be done by code which is granted the following permission:
java.security.SecurityPermission "insertProvider.{name}"
where {name} is replaced by the actual provider name. For example, if the provider name is "MyJCE" and if your code that dynamically registers this provider is in the MyApp.jar file in the /localWork directory, then here is a sample policy file grant statement granting that permission:
grant codeBase "file:/localWork/MyApp.jar" {
permission java.security.SecurityPermission
"insertProvider.MyJCE";
};

Setting Provider Permissions

Whenever JCE providers are not installed extensions, permissions must be granted for when applets or applications using JCE are run while a security manager is installed. There is typically a security manager installed whenever an applet is running, and a security manager may be installed for an application either via code in the application itself or via a command-line argument. Permissions do not need to be granted to installed extensions, since the default system policy configuration file grants all permissions to installed extensions.

The documentation from the vendor of each provider you will be using should include information as to which permissions it requires, and how to grant such permissions. For example, the following permissions may be needed by a provider if it is not an installed extension and a security manager is installed:

  • java.lang.RuntimePermission to get class protection domains. The provider may need to get its own protection domain in the process of doing self-integrity checking.
  • java.security.SecurityPermission "putProviderProperty.{name}" to set provider properties, where {name} is replaced by the actual provider name.

For example, a sample statement granting permissions to a provider whose name is "MyJCE" and whose code is in myjce_provider.jar appears below. Such a statement could appear in a policy file. In this example, the myjce_provider.jar file is assumed to be in the /localWork directory.

grant codeBase "file:/localWork/myjce_provider.jar" {
permission java.lang.RuntimePermission "getProtectionDomain";
permission java.security.SecurityPermission
"putProviderProperty.MyJCE";
};

JCE Keystore

The "SunJCE" provider supplies its own implementation of the java.security.KeyStore class in the JDK 5.0. Its implementation employs a much stronger protection of private keys (using password-based encryption with Triple DES) than the keystore implementation supplied by the "SUN" provider in the JDK 5.0 (Note that because the JDK 5.0 is distributed world-wide in binary and source format, it cannot employ any strong encryption mechanisms.)

In order to take advantage of the keystore implementation of the "SunJCE" provider, you specify "JCEKS" as the keystore type.

You may upgrade your keystore of type "JKS" - this is the name of the keystore type implemented by the "SUN" provider in the Java 2 SDK - to a JCE keystore of type "JCEKS" by changing the password of a private-key entry in your keystore.

To apply the cryptographically strong(er) key protection supplied by "SunJCE" to a private key named "signkey" in your default keystore, use the following command, which will prompt you for the old and new key passwords:

    keytool -keypasswd -alias signkey -storetype jceks

You may want to change the password back to its old value, using the same command.

See Security Tools for more information about keytool and about keystores and how they are managed.


Code Examples

This section is a short tutorial on how to use some of the major features of the JCE APIs in the JDK 5.0 Complete sample programs that exercise the APIs can be found in Appendix F of this document.

Using Encryption

This section takes the user through the process of generating a key, creating and initializing a cipher object, encrypting a file, and then decrypting it. Throughout this example, we use the Data Encryption Standard (DES).

Generating a Key

To create a DES key, we have to instantiate a KeyGenerator for DES. We do not specify a provider, because we do not care about a particular DES key generation implementation. Since we do not initialize the KeyGenerator, a system-provided source of randomness will be used to create the DES key:

    KeyGenerator keygen = KeyGenerator.getInstance("DES");
SecretKey desKey = keygen.generateKey();

After the key has been generated, the same KeyGenerator object can be re-used to create further keys.

Creating a Cipher

The next step is to create a Cipher instance. To do this, we use one of the getInstance factory methods of the Cipher class. We must specify the name of the requested transformation, which includes the following components, separated by slashes (/):

  • the algorithm name
  • the mode (optional)
  • the padding scheme (optional)

In this example, we create a DES (Data Encryption Standard) cipher in Electronic Codebook mode, with PKCS #5-style padding. We do not specify a provider, because we do not care about a particular implementation of the requested transformation.

The standard algorithm name for DES is "DES", the standard name for the Electronic Codebook mode is "ECB", and the standard name for PKCS #5-style padding is "PKCS5Padding":

    Cipher desCipher;

// Create the cipher
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");

We use the generated desKey from above to initialize the Cipher object for encryption:

    // Initialize the cipher for encryption
desCipher.init(Cipher.ENCRYPT_MODE, desKey);

// Our cleartext
byte[] cleartext = "This is just an example".getBytes();

// Encrypt the cleartext
byte[] ciphertext = desCipher.doFinal(cleartext);

// Initialize the same cipher for decryption
desCipher.init(Cipher.DECRYPT_MODE, desKey);

// Decrypt the ciphertext
byte[] cleartext1 = desCipher.doFinal(ciphertext);

cleartext and cleartext1 are identical.

Using Password-Based Encryption

In this example, we prompt the user for a password from which we derive an encryption key.

It would seem logical to collect and store the password in an object of type java.lang.String. However, here's the caveat: Objects of type String are immutable, i.e., there are no methods defined that allow you to change (overwrite) or zero out the contents of a String after usage. This feature makes String objects unsuitable for storing security sensitive information such as user passwords. You should always collect and store security sensitive information in a char array instead.

For that reason, the javax.crypto.spec.PBEKeySpec class takes (and returns) a password as a char array.

The following method is an example of how to collect a user password as a char array:

    /**
* Reads user password from given input stream.
*/
public char[] readPasswd(InputStream in) throws IOException {
char[] lineBuffer;
char[] buf;
int i;

buf = lineBuffer = new char[128];

int room = buf.length;
int offset = 0;
int c;

loop: while (true) {
switch (c = in.read()) {
case -1:
case '\n':
break loop;

case '\r':
int c2 = in.read();
if ((c2 != '\n') && (c2 != -1)) {
if (!(in instanceof PushbackInputStream)) {
in = new PushbackInputStream(in);
}
((PushbackInputStream)in).unread(c2);
} else
break loop;

default:
if (--room < 0) {
buf = new char[offset + 128];
room = buf.length - offset - 1;
System.arraycopy(lineBuffer, 0, buf, 0, offset);
Arrays.fill(lineBuffer, ' ');
lineBuffer = buf;
}
buf[offset++] = (char) c;
break;
}
}

if (offset == 0) {
return null;
}

char[] ret = new char[offset];
System.arraycopy(buf, 0, ret, 0, offset);
Arrays.fill(buf, ' ');

return ret;
}

In order to use Password-Based Encryption (PBE) as defined in PKCS #5, we have to specify a salt and an iteration count. The same salt and iteration count that are used for encryption must be used for decryption:

    PBEKeySpec pbeKeySpec;
PBEParameterSpec pbeParamSpec;
SecretKeyFactory keyFac;

// Salt
byte[] salt = {
(byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
(byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
};

// Iteration count
int count = 20;

// Create PBE parameter set
pbeParamSpec = new PBEParameterSpec(salt, count);

// Prompt user for encryption password.
// Collect user password as char array (using the
// "readPasswd" method from above), and convert
// it into a SecretKey object, using a PBE key
// factory.
System.out.print("Enter encryption password: ");
System.out.flush();
pbeKeySpec = new PBEKeySpec(readPasswd(System.in));
keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

// Create PBE Cipher
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");

// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

// Our cleartext
byte[] cleartext = "This is another example".getBytes();

// Encrypt the cleartext
byte[] ciphertext = pbeCipher.doFinal(cleartext);

Using Key Agreement

Please refer to Appendix F for sample programs exercising the Diffie-Hellman key exchange between 2 and 3 parties, respectively.


Appendix A: Standard Names

The JCE API requires and utilizes a set of standard names for algorithms, algorithm modes, and padding schemes. This specification establishes the following names as standard names. It supplements the list of standard names defined in Appendix A in the JavaTM Cryptography Architecture API Specification & Reference. Note that algorithm names are treated case-insensitively.

In some cases naming conventions are suggested for forming names that are not explicitly listed, to facilitate name consistency across provider implementations. Such suggestions use items in angle brackets (such as <digest> and <encryption>) as placeholders to be replaced by specific message digest, encryption algorithm, and other names.

Cipher

Algorithm

The following names can be specified as the algorithm component in a transformation when requesting an instance of Cipher:

  • AES: Advanced Encryption Standard as specified by NIST in a draft FIPS. Based on the Rijndael algorithm by Joan Daemen and Vincent Rijmen, AES is a 128-bit block cipher supporting keys of 128, 192, and 256 bits.
  • ARCFOUR/RC4: A stream cipher developed by Ron Rivest. For more information, see K. Kaukonen and R. Thayer, "A Stream Cipher Encryption Algorithm 'Arcfour'", Internet Draft (expired), draft-kaukonen-cipher-arcfour-03.txt.
  • Blowfish: The block cipher designed by Bruce Schneier.
  • DES: The Digital Encryption Standard as described in FIPS PUB 46-2.
  • DESede: Triple DES Encryption (DES-EDE).
  • ECIES (Elliptic Curve Integrated Encryption Scheme)
  • PBEWith<digest>And<encryption> or PBEWith<prf>And<encryption>: The password-based encryption algorithm (PKCS #5), using the specified message digest (<digest>) or pseudo-random function (<prf>) and encryption algorithm (<encryption>). Examples:
    • PBEWithMD5AndDES: The password-based encryption algorithm as defined in: RSA Laboratories, "PKCS #5: Password-Based Encryption Standard," version 1.5, Nov 1993. Note that this algorithm implies CBC as the cipher mode and PKCS5Padding as the padding scheme and cannot be used with any other cipher modes or padding schemes.
    • PBEWithHmacSHA1AndDESede: The password-based encryption algorithm as defined in: RSA Laboratories, "PKCS #5: Password-Based Cryptography Standard," version 2.0, March 1999.
  • RC2, RC4, and RC5: Variable-key-size encryption algorithms developed by Ron Rivest for RSA Data Security, Inc.
  • RSA: The RSA encryption algorithm as defined in PKCS #1.

Mode

The following names can be specified as the mode component in a transformation when requesting an instance of Cipher:

  • NONE: No mode.
  • CBC: Cipher Block Chaining Mode, as defined in FIPS PUB 81.
  • CFB: Cipher Feedback Mode, as defined in FIPS PUB 81.
  • ECB: Electronic Codebook Mode, as defined in: The National Institute of Standards and Technology (NIST) Federal Information Processing Standard (FIPS) PUB 81, "DES Modes of Operation," U.S. Department of Commerce, Dec 1980.
  • OFB: Output Feedback Mode, as defined in FIPS PUB 81.
  • PCBC: Propagating Cipher Block Chaining, as defined by Kerberos V4.

Padding

The following names can be specified as the padding component in a transformation when requesting an instance of Cipher:

  • ISO10126Padding. This padding for block ciphers is described in 5.2 Block Encryption Algorithms in the W3C's "XML Encryption Syntax and Processing" document.
  • NoPadding: No padding.
  • OAEPWith<digest>And<mgf>Padding: Optimal Asymmetric Encryption Padding scheme defined in PKCS #1, where <digest> should be replaced by the message digest and <mgf> by the mask generation function. Example: OAEPWithMD5AndMGF1Padding.
  • PKCS5Padding: The padding scheme described in: RSA Laboratories, "PKCS #5: Password-Based Encryption Standard," version 1.5, November 1993.
  • SSL3Padding: The padding scheme defined in the SSL Protocol Version 3.0, November 18, 1996, section 5.2.3.2 (CBC block cipher):
        block-ciphered struct {
    opaque content[SSLCompressed.length];
    opaque MAC[CipherSpec.hash_size];
    uint8 padding[GenericBlockCipher.padding_length];
    uint8 padding_length;
    } GenericBlockCipher;

    The size of an instance of a GenericBlockCipher must be a multiple of the block cipher's block length.

    The padding length, which is always present, contributes to the padding, which implies that if:

        sizeof(content) + sizeof(MAC) % block_length = 0,
    padding has to be (block_length - 1) bytes long, because of the existence of padding_length.

    This make the padding scheme similar (but not quite) to PKCS5Padding, where the padding length is encoded in the padding (and ranges from 1 to block_length). With the SSL scheme, the sizeof(padding) is encoded in the always present padding_length and therefore ranges from 0 to block_length-1.

    Note that this padding mechanism is not supported by the "SunJCE" provider.

KeyAgreement

The following algorithm names can be specified when requesting an instance of KeyAgreement:

  • DiffieHellman: Diffie-Hellman Key Agreement as defined in PKCS #3: Diffie-Hellman Key-Agreement Standard, RSA Laboratories, version 1.4, November 1993.
  • ECDH (Elliptic Curve Diffie-Hellman) as described in RFC 3278: "Use of Elliptic Curve Cryptography (ECC) Algorithms in Cryptographic Message Syntax (CMS)."
  • ECMQV (Elliptic Curve Menezes-Qu-Vanstone) as described in ECC Cipher Suites For TLS (January 2004 draft).

KeyGenerator

The following algorithm names can be specified when requesting an instance of KeyGenerator:

  • AES
  • ARCFOUR/RC4
  • Blowfish
  • DES
  • DESede
  • HmacMD5
  • HmacSHA1
  • HmacSHA256
  • HmacSHA384
  • HmacSHA512
  • RC2

KeyPairGenerator

The following algorithm names can be specified when requesting an instance of KeyPairGenerator:

  • DiffieHellman

SecretKeyFactory

The following algorithm names can be specified when requesting an instance of SecretKeyFactory:

  • DES
  • DESede
  • PBEWith<digest>And<encryption> or PBEWith<prf>And<encryption>: Secret-key factory for use with PKCS #5 password-based encryption, where <digest> is a message digest, <prf> is a pseudo-random function, and <encryption> is an encryption algorithm. Examples: PBEWithMD5AndDES (PKCS #5, v 1.5) and PBEWithHmacSHA1AndDESede (PKCS #5, v 2.0). Note: These both use only the low order 8 bits of each password character.

KeyFactory

The following algorithm names can be specified when requesting an instance of KeyFactory:

  • DiffieHellman

AlgorithmParameterGenerator

The following algorithm names can be specified when requesting an instance of AlgorithmParameterGenerator:

  • DiffieHellman

AlgorithmParameters

The following algorithm names can be specified when requesting an instance of AlgorithmParameters:

  • AES
  • Blowfish
  • DES
  • DESede
  • DiffieHellman
  • OAEP
  • PBE
  • PBEWith<digest>And<encryption>
  • RC2

MAC

The following algorithm names can be specified when requesting an instance of Mac:

  • HmacMD5: The HMAC-MD5 keyed-hashing algorithm as defined in RFC 2104: "HMAC: Keyed-Hashing for Message Authentication" (February 1997).
  • HmacSHA1: The HMAC-SHA1 keyed-hashing algorithm as defined in RFC 2104: "HMAC: Keyed-Hashing for Message Authentication" (February 1997).
  • HmacSHA256: The HmacSHA256 algorithm as defined in RFC 2104 "HMAC: Keyed-Hashing for Message Authentication" (February 1997) with SHA-256 as the message digest algorithm.
  • HmacSHA384: The HmacSHA384 algorithm as defined in RFC 2104 "HMAC: Keyed-Hashing for Message Authentication" (February 1997) with SHA-384 as the message digest algorithm.
  • HmacSHA512: The HmacSHA512 algorithm as defined in RFC 2104 "HMAC: Keyed-Hashing for Message Authentication" (February 1997) with SHA-512 as the message digest algorithm.
  • PBEWith<mac>: MAC for use with PKCS #5 v 2.0 password-based message authentication standard, where <mac> is a Message Authentication Code algorithm name. Example: PBEWithHmacSHA1.

Keystore Types

The following types can be specified when requesting an instance of KeyStore:

Exemption Mechanisms

The following exemption mechanism names can be specified in the permission policy file that accompanies an application considered "exempt" from cryptographic restrictions:

  • KeyEscrow: An encryption system with a backup decryption capability that allows authorized persons (users, officers of an organization, and government officials), under certain prescribed conditions, to decrypt ciphertext with the help of information supplied by one or more trusted parties who hold special data recovery keys.
  • KeyRecovery: A method of obtaining the secret key used to lock encrypted data. One use is as a means of providing fail-safe access to a corporation's own encrypted information in times of disaster.
  • KeyWeakening: A method in which a part of the key can be escrowed or recovered.

Appendix B: SunJCE Default Keysizes

The SunJCE provider uses the following default keysizes:

  • KeyGenerator
    • DES: 56 bits
    • Triple DES: 112 bits
    • Blowfish: 56 bits
    • HmacMD5: 64 bytes
    • HmacSHA1: 64 bytes
  • KeyPairGenerator
    • Diffie-Hellman: 1024 bits
  • AlgorithmParameterGenerator
    • Diffie-Hellman: 1024 bits

Appendix C: SunJCE Keysize Restrictions

The SunJCE provider enforces the following restrictions on the keysize passed to the initialization methods of the following classes:

  • KeyGenerator

    Restrictions (by algorithm):

    • DES: keysize must be equal to 56
    • Triple DES: keysize must be equal to 112 or 168

      Note: A keysize of 112 will generate a Triple DES key with 2 intermediate keys, and a keysize of 168 will generate a Triple DES key with 3 intermediate keys.

    • Blowfish: keysize must be a multiple of 8, and can only range from 32 to 448, inclusive
  • KeyPairGenerator

    Restrictions (by algorithm):

    • Diffie-Hellman: keysize must be a multiple of 64, and can only range from 512 to 1024, inclusive
  • AlgorithmParameterGenerator

    Restrictions (by algorithm):

    • Diffie-Hellman: keysize must be a multiple of 64, and can only range from 512 to 1024, inclusive

Appendix D: Jurisdiction Policy File Format

JCE represents its jurisdiction policy files as J2SE-style policy files with corresponding permission statements. As described in Default Policy Implementation and Policy File Syntax, a J2SE policy file specifies what permissions are allowed for code from specified code sources. A permission represents access to a system resource. In the case of JCE, the "resources" are cryptography algorithms, and code sources do not need to be specified, because the cryptographic restrictions apply to all code.

A jurisdiction policy file consists of a very basic "grant entry" containing one or more "permission entries."

grant {
<permission entries>;
};

The format of a permission entry in a jurisdiction policy file is:

permission <crypto permission class name>[ <alg_name>
[[, <exemption mechanism name>][, <maxKeySize>
[, <AlgorithmParameterSpec class name>,
<parameters for constructing an
AlgorithmParameterSpec object>]]]];

A sample jurisdiction policy file that includes restricting the "Blowfish" algorithm to maximum key sizes of 64 bits is:

grant {
permission javax.crypto.CryptoPermission "Blowfish", 64;
. . .;
};

A permission entry must begin with the word permission. The <crypto permission class name> in the template above would actually be a specific permission class name, such as javax.crypto.CryptoPermission. A crypto permission class reflects the ability of an application/applet to use certain algorithms with certain key sizes in certain environments. There are two crypto permission classes: CryptoPermission and CryptoAllPermission. The special CryptoAllPermission class implies all cryptography-related permissions, that is, it specifies that there are no cryptography-related restrictions.

The <alg_name>, when utilized, is a quoted string specifying the standard name (see Appendix A) of a cryptography algorithm, such as "DES" or "RSA".

The <exemption mechanism name>, when specified, is a quoted string indicating an exemption mechanism which, if enforced, enables a reduction in cryptographic restrictions. Exemption mechanism names that can be used include "KeyRecovery" "KeyEscrow", and "KeyWeakening".

<maxKeySize> is an integer specifying the maximum key size (in bits) allowed for the specified algorithm.

For some algorithms it may not be sufficient to specify the algorithm strength in terms of just a key size. For example, in the case of the "RC5" algorithm, the number of rounds must also be considered. For algorithms whose strength needs to be expressed as more than a key size, the permission entry should also specify an AlgorithmParameterSpec class name (such as javax.crypto.spec.RC5ParameterSpec) and a list of parameters for constructing the specified AlgorithmParameterSpec object.

Items that appear in a permission entry must appear in the specified order. An entry is terminated with a semicolon.

Case is unimportant for the identifiers (grant, permission) but is significant for the <crypto permission class name> or for any string that is passed in as a value.

Note: An "*" can be used as a wildcard for any permission entry option. For example, an "*" (without the quotes) for an <alg_name> option means "all algorithms."


Appendix E: Maximum Key Sizes Allowed by "Strong" Jurisdiction Policy Files

Due to import control restrictions, the jurisdiction policy files shipped with JDK 5.0 allow "strong" but limited cryptography to be used. Here are the maximum key sizes allowed by this "strong" version of the jurisdiction policy files:

Algorithm

Maximum Key Size

DES

64

DESede

*

RC2

128

RC4

128

RC5

128

RSA

2048

* (all others)

128


Appendix F: Sample Programs

  • Diffie-Hellman Key Exchange between 2 Parties

    /*
    * Copyright 1997-2001 by Sun Microsystems, Inc.,
    * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
    * All rights reserved.
    *
    * This software is the confidential and proprietary information
    * of Sun Microsystems, Inc. ("Confidential Information"). You
    * shall not disclose such Confidential Information and shall use
    * it only in accordance with the terms of the license agreement
    * you entered into with Sun.
    */

    import java.io.*;
    import java.math.BigInteger;
    import java.security.*;
    import java.security.spec.*;
    import java.security.interfaces.*;
    import javax.crypto.*;
    import javax.crypto.spec.*;
    import javax.crypto.interfaces.*;
    import com.sun.crypto.provider.SunJCE;

    /**
    * This program executes the Diffie-Hellman key agreement protocol
    * between 2 parties: Alice and Bob.
    *
    * By default, preconfigured parameters (1024-bit prime modulus and base
    * generator used by SKIP) are used.
    * If this program is called with the "-gen" option, a new set of
    * parameters is created.
    */

    public class DHKeyAgreement2 {

    private DHKeyAgreement2() {}

    public static void main(String argv[]) {
    try {
    String mode = "USE_SKIP_DH_PARAMS";

    DHKeyAgreement2 keyAgree = new DHKeyAgreement2();

    if (argv.length > 1) {
    keyAgree.usage();
    throw new Exception("Wrong number of command options");
    } else if (argv.length == 1) {
    if (!(argv[0].equals("-gen"))) {
    keyAgree.usage();
    throw new Exception("Unrecognized flag: " + argv[0]);
    }
    mode = "GENERATE_DH_PARAMS";
    }

    keyAgree.run(mode);
    } catch (Exception e) {
    System.err.println("Error: " + e);
    System.exit(1);
    }
    }

    private void run(String mode) throws Exception {

    DHParameterSpec dhSkipParamSpec;

    if (mode.equals("GENERATE_DH_PARAMS")) {
    // Some central authority creates new DH parameters
    System.out.println
    ("Creating Diffie-Hellman parameters (takes VERY long) ...");
    AlgorithmParameterGenerator paramGen
    = AlgorithmParameterGenerator.getInstance("DH");
    paramGen.init(512);
    AlgorithmParameters params = paramGen.generateParameters();
    dhSkipParamSpec = (DHParameterSpec)params.getParameterSpec
    (DHParameterSpec.class);
    } else {
    // use some pre-generated, default DH parameters
    System.out.println("Using SKIP Diffie-Hellman parameters");
    dhSkipParamSpec = new DHParameterSpec(skip1024Modulus,
    skip1024Base);
    }

    /*
    * Alice creates her own DH key pair, using the DH parameters from
    * above
    */
    System.out.println("ALICE: Generate DH keypair ...");
    KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
    aliceKpairGen.initialize(dhSkipParamSpec);
    KeyPair aliceKpair = aliceKpairGen.generateKeyPair();

    // Alice creates and initializes her DH KeyAgreement object
    System.out.println("ALICE: Initialization ...");
    KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
    aliceKeyAgree.init(aliceKpair.getPrivate());

    // Alice encodes her public key, and sends it over to Bob.
    byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();

    /*
    * Let's turn over to Bob. Bob has received Alice's public key
    * in encoded format.
    * He instantiates a DH public key from the encoded key material.
    */
    KeyFactory bobKeyFac = KeyFactory.getInstance("DH");
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec
    (alicePubKeyEnc);
    PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);

    /*
    * Bob gets the DH parameters associated with Alice's public key.
    * He must use the same parameters when he generates his own key
    * pair.
    */
    DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();

    // Bob creates his own DH key pair
    System.out.println("BOB: Generate DH keypair ...");
    KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
    bobKpairGen.initialize(dhParamSpec);
    KeyPair bobKpair = bobKpairGen.generateKeyPair();

    // Bob creates and initializes his DH KeyAgreement object
    System.out.println("BOB: Initialization ...");
    KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
    bobKeyAgree.init(bobKpair.getPrivate());

    // Bob encodes his public key, and sends it over to Alice.
    byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();

    /*
    * Alice uses Bob's public key for the first (and only) phase
    * of her version of the DH
    * protocol.
    * Before she can do so, she has to instanticate a DH public key
    * from Bob's encoded key material.
    */
    KeyFactory aliceKeyFac = KeyFactory.getInstance("DH");
    x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
    PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
    System.out.println("ALICE: Execute PHASE1 ...");
    aliceKeyAgree.doPhase(bobPubKey, true);

    /*
    * Bob uses Alice's public key for the first (and only) phase
    * of his version of the DH
    * protocol.
    */
    System.out.println("BOB: Execute PHASE1 ...");
    bobKeyAgree.doPhase(alicePubKey, true);

    /*
    * At this stage, both Alice and Bob have completed the DH key
    * agreement protocol.
    * Both generate the (same) shared secret.
    */
    byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
    int aliceLen = aliceSharedSecret.length;

    byte[] bobSharedSecret = new byte[aliceLen];
    int bobLen;
    try {
    // show example of what happens if you
    // provide an output buffer that is too short
    bobLen = bobKeyAgree.generateSecret(bobSharedSecret, 1);
    } catch (ShortBufferException e) {
    System.out.println(e.getMessage());
    }
    // provide output buffer of required size
    bobLen = bobKeyAgree.generateSecret(bobSharedSecret, 0);

    System.out.println("Alice secret: " +
    toHexString(aliceSharedSecret));
    System.out.println("Bob secret: " +
    toHexString(bobSharedSecret));

    if (!java.util.Arrays.equals(aliceSharedSecret, bobSharedSecret))
    throw new Exception("Shared secrets differ");
    System.out.println("Shared secrets are the same");

    /*
    * Now let's return the shared secret as a SecretKey object
    * and use it for encryption. First, we generate SecretKeys for the
    * "DES" algorithm (based on the raw shared secret data) and
    * then we use DES in ECB mode
    * as the encryption algorithm. DES in ECB mode does not require any
    * parameters.
    *
    * Then we use DES in CBC mode, which requires an initialization
    * vector (IV) parameter. In CBC mode, you need to initialize the
    * Cipher object with an IV, which can be supplied using the
    * javax.crypto.spec.IvParameterSpec class. Note that you have to use
    * the same IV for encryption and decryption: If you use a different
    * IV for decryption than you used for encryption, decryption will
    * fail.
    *
    * Note: If you do not specify an IV when you initialize the
    * Cipher object for encryption, the underlying implementation
    * will generate a random one, which you have to retrieve using the
    * javax.crypto.Cipher.getParameters() method, which returns an
    * instance of java.security.AlgorithmParameters. You need to transfer
    * the contents of that object (e.g., in encoded format, obtained via
    * the AlgorithmParameters.getEncoded() method) to the party who will
    * do the decryption. When initializing the Cipher for decryption,
    * the (reinstantiated) AlgorithmParameters object must be passed to
    * the Cipher.init() method.
    */
    System.out.println("Return shared secret as SecretKey object ...");
    // Bob
    // Note: The call to bobKeyAgree.generateSecret above reset the key
    // agreement object, so we call doPhase again prior to another
    // generateSecret call
    bobKeyAgree.doPhase(alicePubKey, true);
    SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");

    // Alice
    // Note: The call to aliceKeyAgree.generateSecret above reset the key
    // agreement object, so we call doPhase again prior to another
    // generateSecret call
    aliceKeyAgree.doPhase(bobPubKey, true);
    SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");

    /*
    * Bob encrypts, using DES in ECB mode
    */
    Cipher bobCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
    bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);

    byte[] cleartext = "This is just an example".getBytes();
    byte[] ciphertext = bobCipher.doFinal(cleartext);

    /*
    * Alice decrypts, using DES in ECB mode
    */
    Cipher aliceCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
    aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey);
    byte[] recovered = aliceCipher.doFinal(ciphertext);

    if (!java.util.Arrays.equals(cleartext, recovered))
    throw new Exception("DES in CBC mode recovered text is " +
    "different from cleartext");
    System.out.println("DES in ECB mode recovered text is " +
    "same as cleartext");

    /*
    * Bob encrypts, using DES in CBC mode
    */
    bobCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);

    cleartext = "This is just an example".getBytes();
    ciphertext = bobCipher.doFinal(cleartext);
    // Retrieve the parameter that was used, and transfer it to Alice in
    // encoded format
    byte[] encodedParams = bobCipher.getParameters().getEncoded();

    /*
    * Alice decrypts, using DES in CBC mode
    */
    // Instantiate AlgorithmParameters object from parameter encoding
    // obtained from Bob
    AlgorithmParameters params = AlgorithmParameters.getInstance("DES");
    params.init(encodedParams);
    aliceCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey, params);
    recovered = aliceCipher.doFinal(ciphertext);

    if (!java.util.Arrays.equals(cleartext, recovered))
    throw new Exception("DES in CBC mode recovered text is " +
    "different from cleartext");
    System.out.println("DES in CBC mode recovered text is " +
    "same as cleartext");
    }

    /*
    * Converts a byte to hex digit and writes to the supplied buffer
    */
    private void byte2hex(byte b, StringBuffer buf) {
    char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
    '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    int high = ((b & 0xf0) >> 4);
    int low = (b & 0x0f);
    buf.append(hexChars[high]);
    buf.append(hexChars[low]);
    }

    /*
    * Converts a byte array to hex string
    */
    private String toHexString(byte[] block) {
    StringBuffer buf = new StringBuffer();

    int len = block.length;

    for (int i = 0; i < len; i++) {
    byte2hex(block[i], buf);
    if (i < len-1) {
    buf.append(":");
    }
    }
    return buf.toString();
    }

    /*
    * Prints the usage of this test.
    */
    private void usage() {
    System.err.print("DHKeyAgreement usage: ");
    System.err.println("[-gen]");
    }

    // The 1024 bit Diffie-Hellman modulus values used by SKIP
    private static final byte skip1024ModulusBytes[] = {
    (byte)0xF4, (byte)0x88, (byte)0xFD, (byte)0x58,
    (byte)0x4E, (byte)0x49, (byte)0xDB, (byte)0xCD,
    (byte)0x20, (byte)0xB4, (byte)0x9D, (byte)0xE4,
    (byte)0x91, (byte)0x07, (byte)0x36, (byte)0x6B,
    (byte)0x33, (byte)0x6C, (byte)0x38, (byte)0x0D,
    (byte)0x45, (byte)0x1D, (byte)0x0F, (byte)0x7C,
    (byte)0x88, (byte)0xB3, (byte)0x1C, (byte)0x7C,
    (byte)0x5B, (byte)0x2D, (byte)0x8E, (byte)0xF6,
    (byte)0xF3, (byte)0xC9, (byte)0x23, (byte)0xC0,
    (byte)0x43, (byte)0xF0, (byte)0xA5, (byte)0x5B,
    (byte)0x18, (byte)0x8D, (byte)0x8E, (byte)0xBB,
    (byte)0x55, (byte)0x8C, (byte)0xB8, (byte)0x5D,
    (byte)0x38, (byte)0xD3, (byte)0x34, (byte)0xFD,
    (byte)0x7C, (byte)0x17, (byte)0x57, (byte)0x43,
    (byte)0xA3, (byte)0x1D, (byte)0x18, (byte)0x6C,
    (byte)0xDE, (byte)0x33, (byte)0x21, (byte)0x2C,
    (byte)0xB5, (byte)0x2A, (byte)0xFF, (byte)0x3C,
    (byte)0xE1, (byte)0xB1, (byte)0x29, (byte)0x40,
    (byte)0x18, (byte)0x11, (byte)0x8D, (byte)0x7C,
    (byte)0x84, (byte)0xA7, (byte)0x0A, (byte)0x72,
    (byte)0xD6, (byte)0x86, (byte)0xC4, (byte)0x03,
    (byte)0x19, (byte)0xC8, (byte)0x07, (byte)0x29,
    (byte)0x7A, (byte)0xCA, (byte)0x95, (byte)0x0C,
    (byte)0xD9, (byte)0x96, (byte)0x9F, (byte)0xAB,
    (byte)0xD0, (byte)0x0A, (byte)0x50, (byte)0x9B,
    (byte)0x02, (byte)0x46, (byte)0xD3, (byte)0x08,
    (byte)0x3D, (byte)0x66, (byte)0xA4, (byte)0x5D,
    (byte)0x41, (byte)0x9F, (byte)0x9C, (byte)0x7C,
    (byte)0xBD, (byte)0x89, (byte)0x4B, (byte)0x22,
    (byte)0x19, (byte)0x26, (byte)0xBA, (byte)0xAB,
    (byte)0xA2, (byte)0x5E, (byte)0xC3, (byte)0x55,
    (byte)0xE9, (byte)0x2F, (byte)0x78, (byte)0xC7
    };

    // The SKIP 1024 bit modulus
    private static final BigInteger skip1024Modulus
    = new BigInteger(1, skip1024ModulusBytes);

    // The base used with the SKIP 1024 bit modulus
    private static final BigInteger skip1024Base = BigInteger.valueOf(2);
    }

  • Diffie-Hellman Key Exchange between 3 Parties

    /*
    * Copyright 1997-2001 by Sun Microsystems, Inc.,
    * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
    * All rights reserved.
    *
    * This software is the confidential and proprietary information
    * of Sun Microsystems, Inc. ("Confidential Information"). You
    * shall not disclose such Confidential Information and shall use
    * it only in accordance with the terms of the license agreement
    * you entered into with Sun.
    */

    import java.io.*;
    import java.math.BigInteger;
    import java.security.*;
    import java.security.spec.*;
    import java.security.interfaces.*;
    import javax.crypto.*;
    import javax.crypto.spec.*;
    import javax.crypto.interfaces.*;
    import com.sun.crypto.provider.SunJCE;

    /**
    * This program executes the Diffie-Hellman key agreement protocol
    * between 3 parties: Alice, Bob, and Carol.
    *
    * We use the same 1024-bit prime modulus and base generator that are
    * used by SKIP.
    */

    public class DHKeyAgreement3 {

    private DHKeyAgreement3() {}

    public static void main(String argv[]) {
    try {
    DHKeyAgreement3 keyAgree = new DHKeyAgreement3();
    keyAgree.run();
    } catch (Exception e) {
    System.err.println("Error: " + e);
    System.exit(1);
    }
    }

    private void run() throws Exception {

    DHParameterSpec dhSkipParamSpec;

    System.out.println("Using SKIP Diffie-Hellman parameters");
    dhSkipParamSpec = new DHParameterSpec(skip1024Modulus, skip1024Base);

    // Alice creates her own DH key pair
    System.out.println("ALICE: Generate DH keypair ...");
    KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
    aliceKpairGen.initialize(dhSkipParamSpec);
    KeyPair aliceKpair = aliceKpairGen.generateKeyPair();

    // Bob creates his own DH key pair
    System.out.println("BOB: Generate DH keypair ...");
    KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
    bobKpairGen.initialize(dhSkipParamSpec);
    KeyPair bobKpair = bobKpairGen.generateKeyPair();

    // Carol creates her own DH key pair
    System.out.println("CAROL: Generate DH keypair ...");
    KeyPairGenerator carolKpairGen = KeyPairGenerator.getInstance("DH");
    carolKpairGen.initialize(dhSkipParamSpec);
    KeyPair carolKpair = carolKpairGen.generateKeyPair();


    // Alice initialize
    System.out.println("ALICE: Initialize ...");
    KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
    aliceKeyAgree.init(aliceKpair.getPrivate());

    // Bob initialize
    System.out.println("BOB: Initialize ...");
    KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
    bobKeyAgree.init(bobKpair.getPrivate());

    // Carol initialize
    System.out.println("CAROL: Initialize ...");
    KeyAgreement carolKeyAgree = KeyAgreement.getInstance("DH");
    carolKeyAgree.init(carolKpair.getPrivate());


    // Alice uses Carol's public key
    Key ac = aliceKeyAgree.doPhase(carolKpair.getPublic(), false);

    // Bob uses Alice's public key
    Key ba = bobKeyAgree.doPhase(aliceKpair.getPublic(), false);

    // Carol uses Bob's public key
    Key cb = carolKeyAgree.doPhase(bobKpair.getPublic(), false);


    // Alice uses Carol's result from above
    aliceKeyAgree.doPhase(cb, true);

    // Bob uses Alice's result from above
    bobKeyAgree.doPhase(ac, true);

    // Carol uses Bob's result from above
    carolKeyAgree.doPhase(ba, true);


    // Alice, Bob and Carol compute their secrets
    byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
    System.out.println("Alice secret: " + toHexString(aliceSharedSecret));

    byte[] bobSharedSecret = bobKeyAgree.generateSecret();
    System.out.println("Bob secret: " + toHexString(bobSharedSecret));

    byte[] carolSharedSecret = carolKeyAgree.generateSecret();
    System.out.println("Carol secret: " + toHexString(carolSharedSecret));


    // Compare Alice and Bob
    if (!java.util.Arrays.equals(aliceSharedSecret, bobSharedSecret))
    throw new Exception("Alice and Bob differ");
    System.out.println("Alice and Bob are the same");

    // Compare Bob and Carol
    if (!java.util.Arrays.equals(bobSharedSecret, carolSharedSecret))
    throw new Exception("Bob and Carol differ");
    System.out.println("Bob and Carol are the same");
    }


    /*
    * Converts a byte to hex digit and writes to the supplied buffer
    */
    private void byte2hex(byte b, StringBuffer buf) {
    char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
    '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    int high = ((b & 0xf0) >> 4);
    int low = (b & 0x0f);
    buf.append(hexChars[high]);
    buf.append(hexChars[low]);
    }

    /*
    * Converts a byte array to hex string
    */
    private String toHexString(byte[] block) {
    StringBuffer buf = new StringBuffer();

    int len = block.length;

    for (int i = 0; i < len; i++) {
    byte2hex(block[i], buf);
    if (i < len-1) {
    buf.append(":");
    }
    }
    return buf.toString();
    }

    /*
    * Prints the usage of this test.
    */
    private void usage() {
    System.err.print("DHKeyAgreement usage: ");
    System.err.println("[-gen]");
    }

    // The 1024 bit Diffie-Hellman modulus values used by SKIP
    private static final byte skip1024ModulusBytes[] = {
    (byte)0xF4, (byte)0x88, (byte)0xFD, (byte)0x58,
    (byte)0x4E, (byte)0x49, (byte)0xDB, (byte)0xCD,
    (byte)0x20, (byte)0xB4, (byte)0x9D, (byte)0xE4,
    (byte)0x91, (byte)0x07, (byte)0x36, (byte)0x6B,
    (byte)0x33, (byte)0x6C, (byte)0x38, (byte)0x0D,
    (byte)0x45, (byte)0x1D, (byte)0x0F, (byte)0x7C,
    (byte)0x88, (byte)0xB3, (byte)0x1C, (byte)0x7C,
    (byte)0x5B, (byte)0x2D, (byte)0x8E, (byte)0xF6,
    (byte)0xF3, (byte)0xC9, (byte)0x23, (byte)0xC0,
    (byte)0x43, (byte)0xF0, (byte)0xA5, (byte)0x5B,
    (byte)0x18, (byte)0x8D, (byte)0x8E, (byte)0xBB,
    (byte)0x55, (byte)0x8C, (byte)0xB8, (byte)0x5D,
    (byte)0x38, (byte)0xD3, (byte)0x34, (byte)0xFD,
    (byte)0x7C, (byte)0x17, (byte)0x57, (byte)0x43,
    (byte)0xA3, (byte)0x1D, (byte)0x18, (byte)0x6C,
    (byte)0xDE, (byte)0x33, (byte)0x21, (byte)0x2C,
    (byte)0xB5, (byte)0x2A, (byte)0xFF, (byte)0x3C,
    (byte)0xE1, (byte)0xB1, (byte)0x29, (byte)0x40,
    (byte)0x18, (byte)0x11, (byte)0x8D, (byte)0x7C,
    (byte)0x84, (byte)0xA7, (byte)0x0A, (byte)0x72,
    (byte)0xD6, (byte)0x86, (byte)0xC4, (byte)0x03,
    (byte)0x19, (byte)0xC8, (byte)0x07, (byte)0x29,
    (byte)0x7A, (byte)0xCA, (byte)0x95, (byte)0x0C,
    (byte)0xD9, (byte)0x96, (byte)0x9F, (byte)0xAB,
    (byte)0xD0, (byte)0x0A, (byte)0x50, (byte)0x9B,
    (byte)0x02, (byte)0x46, (byte)0xD3, (byte)0x08,
    (byte)0x3D, (byte)0x66, (byte)0xA4, (byte)0x5D,
    (byte)0x41, (byte)0x9F, (byte)0x9C, (byte)0x7C,
    (byte)0xBD, (byte)0x89, (byte)0x4B, (byte)0x22,
    (byte)0x19, (byte)0x26, (byte)0xBA, (byte)0xAB,
    (byte)0xA2, (byte)0x5E, (byte)0xC3, (byte)0x55,
    (byte)0xE9, (byte)0x2F, (byte)0x78, (byte)0xC7
    };

    // The SKIP 1024 bit modulus
    private static final BigInteger skip1024Modulus
    = new BigInteger(1, skip1024ModulusBytes);

    // The base used with the SKIP 1024 bit modulus
    private static final BigInteger skip1024Base = BigInteger.valueOf(2);
    }

  • Blowfish Example

    /*
    * Copyright 1997-2001 by Sun Microsystems, Inc.,
    * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
    * All rights reserved.
    *
    * This software is the confidential and proprietary information
    * of Sun Microsystems, Inc. ("Confidential Information"). You
    * shall not disclose such Confidential Information and shall use
    * it only in accordance with the terms of the license agreement
    * you entered into with Sun.
    */

    import java.security.*;
    import javax.crypto.*;
    import javax.crypto.spec.*;

    /**
    * This program generates a Blowfish key, retrieves its raw bytes, and
    * then reinstantiates a Blowfish key from the key bytes.
    * The reinstantiated key is used to initialize a Blowfish cipher for
    * encryption.
    */

    public class BlowfishKey {

    public static void main(String[] args) throws Exception {

    KeyGenerator kgen = KeyGenerator.getInstance("Blowfish");
    SecretKey skey = kgen.generateKey();
    byte[] raw = skey.getEncoded();
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "Blowfish");

    Cipher cipher = Cipher.getInstance("Blowfish");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted =
    cipher.doFinal("This is just an example".getBytes());
    }
    }

  • HMAC-MD5 Example

    /*
    * Copyright 1997-2001 by Sun Microsystems, Inc.,
    * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
    * All rights reserved.
    *
    * This software is the confidential and proprietary information
    * of Sun Microsystems, Inc. ("Confidential Information"). You
    * shall not disclose such Confidential Information and shall use
    * it only in accordance with the terms of the license agreement
    * you entered into with Sun.
    */

    import java.security.*;
    import javax.crypto.*;

    /**
    * This program demonstrates how to generate a secret-key object for
    * HMAC-MD5, and initialize an HMAC-MD5 object with it.
    */

    public class initMac {

    public static void main(String[] args) throws Exception {

    // Generate secret key for HMAC-MD5
    KeyGenerator kg = KeyGenerator.getInstance("HmacMD5");
    SecretKey sk = kg.generateKey();

    // Get instance of Mac object implementing HMAC-MD5, and
    // initialize it with the above secret key
    Mac mac = Mac.getInstance("HmacMD5");
    mac.init(sk);
    byte[] result = mac.doFinal("Hi There".getBytes());
    }
    }

Copyright ¨ 1996-2004 Sun Microsystems, Inc. All Rights Reserved.

Please send comments to: java-security@java.sun.com.


@http://java.sun.com/j2se/1.5.0/docs/guide/security/jce/JCERefGuide.html
2007/03/06 18:59 2007/03/06 18:59
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

아파치 재단에 제로니모가 있다면.. 썬사에는 글래스피쉬가 있습니다.





JAVA EE 5 의 오픈소스 구현체인 글래스 피쉬..

http://java.sun.com/javaee/community/glassfish/
https://glassfish.dev.java.net/public/devindex.html

오픈소스 엔터프라이즈 어플리케이션 서버, 제로니모와 글래스피쉬의 선전을 기대 합니다.



2006/06/12 03:13 2006/06/12 03:13
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다