home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

日記/2012/04/22/JavaでSSLSocketの勉強メモ1

日記/2012/04/22/JavaでSSLSocketの勉強メモ1

日記 / 2012 / 04 / 22 / JavaでSSLSocketの勉強メモ1
id: 1075 所有者: msakamoto-sf    作成日: 2012-04-22 18:49:24
カテゴリ: Java セキュリティ 

お仕事でBurpSuiteを使っているのだけど、暫く前から気になってしょうがない点がある。HTTPSの時に、なんでBurpは適切なCNでPortSwiggerによる証明書を生成できるのだろう?

というわけで、その辺のもやもやを解決するためにはまずSSLを学ばないと駄目かなと思い、資料漁ったり入り口としてJavaでSSLを使うにはどうするのか、とか手をつけてみました。
今回はそのお勉強メモで、SSLでechoサーバをJavaで作ってみようという、少しぐぐればサンプルがゴロゴロ転がってるやつです。ただ、そのサンプルが「なぜそれで正常に動作するのか?」を理解するだけで実に丸一日使い果たした・・・。


まず資料。後でまとめ直しますが・・・

仕組みと仕様:

実装サンプル:

Java特化リファレンス:

まずKeyStoreを作ってみる。

SSLサーバ側が提示する証明書は、SSLContextをinitするときにKeyManagerとして渡される。SSLContextのinitにはKeyManagerとTrustManagerの二種類があるけど、接続を受け付けて先に提示するサーバ側が使うのがKeyManager, 提示された証明書を検証するときに信頼された証明書がストアされるのがTrustManager。かなり混乱してたのだけれど、単純にサーバ側は秘密鍵が必要なので、公開鍵・秘密鍵のペアを保存しているKeyManagerに。クライアントが証明書の検証を行うにはルートCAからの公開鍵のチェインがあればよいので、信頼された証明書を管理するTrustManager、ということになる。

結果だけ取り急ぎメモ。こんなシェルスクリプト作りました。
makeks.sh:

#!/bin/sh -x

KEYSTORE=testks01
PASSWORD=changeit
ALIAS=testkey
DNAME="cn=TestName, ou=TestUnit, o=TestOrg, l=Japan, st=Tokyo, c=JP"
CERT_PEM=testkey.pem

rm -i $KEYSTORE

keytool -J-Dfile.encoding=UTF-8 \
  -keystore $KEYSTORE \
  -storepass $PASSWORD \
  -storetype jks \
  -genkeypair \
  -alias $ALIAS \
  -validity 3650 \
  -dname "$DNAME" \
  -keypass $PASSWORD

keytool -J-Dfile.encoding=UTF-8 \
  -keystore $KEYSTORE \
  -storepass $PASSWORD \
  -storetype jks \
  -list -v

keytool -J-Dfile.encoding=UTF-8 \
  -keystore $KEYSTORE \
  -storepass $PASSWORD \
  -storetype jks \
  -exportcert \
  -alias $ALIAS \
  -rfc \
  -file $CERT_PEM

最後にexportしてるのは、後でopenssl s_clientで確認する時用に"-rfc"付き=PEM形式で出力された証明書が欲しいから。

で、keytoolが操作するデフォルトのKeyStoreは"JKS"という形式です。これはkeytoolのドキュメントに書かれています。

キーストアには、Sun が提供する組み込みのデフォルトの実装があります。これは、JKS という名前の独自のキーストアタイプ (形式) を利用するもので、キーストアをファイルとして実装しています。この実装では、個々の非公開鍵は個別のパスワードによって保護され、キーストア全体の整合性も (非公開鍵とは別の) パスワードによって保護されます。

では、どこに「デフォルトはJKSである」という指定があるのか?これはJavaのセキュリティプロファイルの設定に書かれているようです。同じくkeytoolのドキュメントにあります。

デフォルトのキーストアタイプは JKS (Sun が提供する独自のタイプのキーストアの実装) です。これは、セキュリティープロパティーファイル内の次の行によって指定されています。

 keystore.type=jks

これにより、Javaのコードからkeytool用のKeyStoreを操作するには以下のようなコードを書けば良いことが導かれます。

KeyStore ks1 = KeyStore.getInstance("JKS");

MacOSX 10.7.2 のJDKでは以下の場所に記述されていました。
/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/security/java.security:

keystore.type=jks

SSLServerSocketを使ってみる

こんなクラスでなんとか動かせました。

package test;
 
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
 
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
 
public class KeyStore1 {
    public static void main(String[] args) throws Exception {
        char[] commonPassword = "changeit".toCharArray();
        KeyStore ks1 = KeyStore.getInstance("JKS");
        ks1.load(new FileInputStream("testks01"), commonPassword);
        System.out.println("KeyStore's size = " + ks1.size());
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks1, commonPassword);
 
        // 明示的に空のTrustManagerを使ってみます。
        KeyStore emptyTrustStore = KeyStore.getInstance("JKS");
        emptyTrustStore.load(null, "".toCharArray()); // nullで空のKeyStoreを指定したことになります。
        TrustManagerFactory emptyTMF = TrustManagerFactory.getInstance("PKIX");
        emptyTMF.init(emptyTrustStore);
 
        SSLContext sslContext = SSLContext.getInstance("TLSv1");
        sslContext.init(kmf.getKeyManagers(), emptyTMF.getTrustManagers(), null);
        ServerSocket serverSocket = sslContext.getServerSocketFactory().createServerSocket(18080);
        BufferedReader br = null;
        BufferedWriter bw = null;
        while (true) {
            Socket clientSocket = serverSocket.accept();
            br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            bw = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
            String recv = br.readLine();
            System.out.println("Received Message = [" + recv + "]");
            bw.write("Hello, your message = [" + recv + "]");
            bw.flush();
            try { br.close(); } catch (Exception e){}
            try { bw.close(); } catch (Exception e){}
        }
 
    }
 
}

"openssl s_client"で接続してみる。

"-CAfile" 無し:validation errorになるものの一応動作。

$ openssl s_client -connect localhost:18080
CONNECTED(00000003)
depth=0 /C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
verify error:num=18:self signed certificate
verify return:1
depth=0 /C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
verify return:1
---
Certificate chain
 0 s:/C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
   i:/C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDBDCCAsKgAwIBAgIET5PQxjALBgcqhkjOOAQDBQAwZTELMAkGA1UEBhMCSlAx
DjAMBgNVBAgTBVRva3lvMQ4wDAYDVQQHEwVKYXBhbjEQMA4GA1UEChMHVGVzdE9y
ZzERMA8GA1UECxMIVGVzdFVuaXQxETAPBgNVBAMTCFRlc3ROYW1lMB4XDTEyMDQy
MjA5MzUwMloXDTIyMDQyMDA5MzUwMlowZTELMAkGA1UEBhMCSlAxDjAMBgNVBAgT
BVRva3lvMQ4wDAYDVQQHEwVKYXBhbjEQMA4GA1UEChMHVGVzdE9yZzERMA8GA1UE
CxMIVGVzdFVuaXQxETAPBgNVBAMTCFRlc3ROYW1lMIIBuDCCASwGByqGSM44BAEw
ggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2N
WPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fn
xqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUj
C8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0H
gmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuz
pnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7P
SSoDgYUAAoGBAOb3/ZEzhB9iwQ771XuMeppRoSgeYxa1XsWgpWsem7+RqEZnF77s
EucxVA4eHtZylnipv7bGuwUAhrq8iCXIwUMlijrLk4jqiShdLBmZxJowR8VUhnm9
KyPyBeGQLrlLDXARwsM63drvWmTeMLaksocS2UhfeSS/t5MVS5oVr454MAsGByqG
SM44BAMFAAMvADAsAhQbqbi4HhYtG8LopueO/noca1OhIAIUFCYPCOaNPQcSfvOU
b/x7qdXQiuU=
-----END CERTIFICATE-----
subject=/C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
issuer=/C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
---
No client certificate CA names sent
---
SSL handshake has read 1282 bytes and written 296 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-DSS-AES256-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-DSS-AES256-SHA
    Session-ID: 4F93DB16D389C93A4D45034E1EB62BC8D9E7D7766997AF5F6C64A77DDA61E405
    Session-ID-ctx: 
    Master-Key: F7A0B6B5CF9A13BFBC7EBF7DBB51468251CCF64A2E5206649F45CF16E393BD5F03BADDA29A9AFF8F83496EDC8613C52A
    Key-Arg   : None
    Start Time: 1335089942
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)
---
Hello
Hello, your message = [Hello]closed

"-CAfile" 有り

$ openssl s_client -connect localhost:18080 -CAfile testkey.pem 
CONNECTED(00000003)
depth=0 /C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
verify return:1
---
Certificate chain
 0 s:/C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
   i:/C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDBDCCAsKgAwIBAgIET5PQxjALBgcqhkjOOAQDBQAwZTELMAkGA1UEBhMCSlAx
DjAMBgNVBAgTBVRva3lvMQ4wDAYDVQQHEwVKYXBhbjEQMA4GA1UEChMHVGVzdE9y
ZzERMA8GA1UECxMIVGVzdFVuaXQxETAPBgNVBAMTCFRlc3ROYW1lMB4XDTEyMDQy
MjA5MzUwMloXDTIyMDQyMDA5MzUwMlowZTELMAkGA1UEBhMCSlAxDjAMBgNVBAgT
BVRva3lvMQ4wDAYDVQQHEwVKYXBhbjEQMA4GA1UEChMHVGVzdE9yZzERMA8GA1UE
CxMIVGVzdFVuaXQxETAPBgNVBAMTCFRlc3ROYW1lMIIBuDCCASwGByqGSM44BAEw
ggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2N
WPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fn
xqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUj
C8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0H
gmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuz
pnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7P
SSoDgYUAAoGBAOb3/ZEzhB9iwQ771XuMeppRoSgeYxa1XsWgpWsem7+RqEZnF77s
EucxVA4eHtZylnipv7bGuwUAhrq8iCXIwUMlijrLk4jqiShdLBmZxJowR8VUhnm9
KyPyBeGQLrlLDXARwsM63drvWmTeMLaksocS2UhfeSS/t5MVS5oVr454MAsGByqG
SM44BAMFAAMvADAsAhQbqbi4HhYtG8LopueO/noca1OhIAIUFCYPCOaNPQcSfvOU
b/x7qdXQiuU=
-----END CERTIFICATE-----
subject=/C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
issuer=/C=JP/ST=Tokyo/L=Japan/O=TestOrg/OU=TestUnit/CN=TestName
---
No client certificate CA names sent
---
SSL handshake has read 1282 bytes and written 296 bytes
---
---
New, TLSv1/SSLv3, Cipher is DHE-DSS-AES256-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-DSS-AES256-SHA
    Session-ID: 4F93DB59D0CF63BF0AD9DA123066DE22AFF5781398C7F17B07BD07E4DF0E94C1
    Session-ID-ctx: 
    Master-Key: 617EB17722AEDBF398245F4C65F910DD36C261E4283516329F7B11F689C4A0661E64960E5FEB967551592B40706F84B2
    Key-Arg   : None
    Start Time: 1335090009
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
Hello
Hello, your message = [Hello]closed

取り急ぎ以上。


プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2012-04-22 19:20:38
md5:55a4d1f485048bdefbf0495b7c9f1914
sha1:069bad75931a96bbe7076f7eddc6c5648c16dc8c
コメント
コメントを投稿するにはログインして下さい。