2015年10月14日水曜日

Keystore と秘密鍵と公開鍵の関係について

Java では、Keytool という鍵と証明書を管理するためのユーティリティを利用して、SSL やTLS などの暗号化通信を実現します。

SSL/TLS 通信は、以下のステップで実現されます。(Server Hello Done までを記載)

Client Hello
Server Hello
Server Certificate
Server Hello Done
Client Hello で、どの暗号化スイートを利用して通信したいかを問い合わせます。


Server Hello で、暗号化スイートを決定し、Server Certificate でサーバ証明書を渡します。



このサーバ証明書を作成するために、Keytool を使います。

サーバ証明書は、認証局の署名が必要です。 企業は正規の業者から署名をしてもらいますが、テスト時やインターナルな環境では、必ずしも正規の業者からの署名は必要ないかもしれません。

そのような場合、通称「オレオレ認証局」という、自作認証局を作成して、サーバ証明書に署名をすることになります。

オレオレ認証局の作成の仕方は他のサイトを見て頂くことにしますが、大きく2通りのやり方が公開されています。 1つは、OpenSSL とPerl を利用して認証局を構築するやり方。
もう1つは、OpenSSL のみで構築するやり方。

どちらでも構築はできますので、調べてみてください。
で、Keytool を利用し、Keystore を生成する場合、以下の2種類のエントリが存在します。
PrivateKeyEntry
TrustedCertEntry
暗号化通信をする場合に必要となるエントリは、PrivateKeyEntry になります。 これは、公開鍵と秘密鍵のペアのエントリになります。

以下では、Keystore ファイルの生成方法の概要を記載します。
サーバ管理者が秘密鍵を生成
サーバ管理者が秘密鍵から証明書署名要求ファイルを生成
オレオレ認証局は自分の秘密鍵で証明書署名要求ファイルに署名してサーバ証明書を生成
サーバ管理者はサーバ証明書とサーバ管理者側の秘密鍵を利用して、公開鍵と秘密鍵のペアファイルを生成
生成するKeystore ファイルにペアファイルをインポート

具体的なコマンドは以下の通り。
openssl genrsa -des3 -out private.key
openssl req -new -days 3650 -key private.key -out server.csr
openssl req -keyout ca/cakey.pem -out ca/cacert.pem -new -x509
openssl ca -in server.csr -keyfile ca\cakey.pem -cert ca\cacert.pem –out server.crt
openssl pkcs12 -inkey private.key -in server.crt -export -out pair.pkcs12
keytool -importkeystore -srckeystore pair.pkcs12 -srcstoretype PKCS12 -destkeystore keystore
※下記の修正は事前にしておくこと。
修正1:openssl.cnf を開いて、ca のディレクトリを指定する。(実行フォルダにca フォルダを新規で作成しておく)
[ CA_default ]
dir  = ./ca  # Where everything is kept
修正2:空のindex.txt ファイルを./ca に配置しておく。
修正3:4桁以上の数字が記入されたserial というファイル名(拡張子無し)のファイルを./ca に配置しておく。

2015年5月26日火曜日

Java1.7 Compile で検知できないキャストエラーについて

Java1.6 で動作しているJenkins で、ビルドがコケた。
理由は、キャストエラー。これをJava1.7 のデフォルトではコンパイルエラーとして表示してくれないため、開発環境ではエラーがでないが、Java1.6 でBuild したらコケる。


以下、テストコード
package test;

import java.util.Map;
import java.util.HashMap;

public class Test {
    public static void main(String args[]) {
     Map result = new HashMap();
     result.put("key",new Integer(2));
     int errorCode = (int)result.get( "key" );
     System.err.println("errorCode: " + errorCode);
    }
}

で、問題は以下の部分。
int errorCode = (int)result.get( "key" );

以下のようにキャストすべき。
int errorCode = (Integer)result.get( "key" );

-source option に1.6 を指定すると、キャスト失敗。
javac -source 1.6 -bootclasspath "C:\Program Files\Java\jdk1.7.0_67\jre\lib\rt.jar" Test.java
Test.java:11: エラー: 変換できない型
        int errorCode = (int)result.get( "key" );
                                       ^
  期待値: int
  検出値:    Object
エラー1個

しかし、source を1.7 に指定すれば、コンパイルに問題は起きない。
javac -source 1.7 -bootclasspath "C:\Program Files\Java\jdk1.7.0_67\jre\lib\rt.jar" Test.java

で、開発中にどうやってこれを検知するか(コンパイルエラーとして表示させるか)という点ですが、 Eclipse の以下の設定で、実行環境をJava1.7 から、1.6 に変更すれば検知可能です。
Right click on the project > Build Path > Java Build Path > Libraries

2015年5月18日月曜日

XMLStreamReader が持つXML をファイル出力する場合

[ 解決課題 ]
XML に問題があり、ファイル退避、かつ、問題箇所以降のXML を読み込みたい。

[ 検討結果 ]
ファイル退避、もしくはXML の読み込みのどちらかを選択する必要がある。 両方実施したい場合は、自前パーサーを作成する必要がある。

[ 処理内容 ]
前提として、reader は元から存在するものとし、XML 情報を持つとする。
 StringWriter sw と、XMLStreamWriter writer を準備して、Transformer class を利用して、 transform method をCall するとsw にXML 情報が入力される。
これをtoString() してあげれば、XML の出力が可能。

 しかし、問題点があり、transform() をCall すると、XMLStreamReader reader の情報がすべて失われてしまう。
そのため、XML のある1箇所に問題があり、すべてのXML を出力、かつ、問題の箇所以降のXML を再読み込みすることができない。
 DeepCopy もできないため(*1)、2回XML の読み込みをしたい場合は、StringWriter にすべて書きだした後に、 自前のパーサーを作って処理を実行しないといけない。

*1) http://stackoverflow.com/questions/9837861/how-to-remember-xmlstreamreader-position-in-xml-with-stax

public void parse(XMLStreamReader reader){
 StringWriter sw = new StringWriter();
 StAXSource source = new StAXSource( reader );
 XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
 XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
 XMLStreamWriter writer;
 String rawxml = null;
 try {
     XMLStreamReader re = xmlInputFactory.( reader );
     writer = xmlOutputFactory.createXMLStreamWriter( sw );
     TransformerFactory tf = TransformerFactory.newInstance();
     Transformer t = tf.newTransformer();
     StAXResult result = new StAXResult( writer );
     t.transform( source, result );
     rawxml = sw.toString();
    } catch ( XMLStreamException e ) {
     e.printStackTrace();
    } catch ( TransformerConfigurationException e ) {
     e.printStackTrace();
    } catch ( TransformerException e ) {
     e.printStackTrace();
    }
} 

2015年5月8日金曜日

Java でClose したPort にアクセスした場合について

閉じているポートにアクセスすると、以下のException がでる。
java.net.ConnectException: Connection refused: connect
 at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
 at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
 at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
 at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
 at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
 at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
 at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
 at java.net.Socket.connect(Socket.java:579)

では、これはなぜおこるのか、というと以下の記載がありました。
http://docs.oracle.com/cd/E19455-01/806-2720/msgs-175/index.html

[ 抜粋 ]
[ 原因 ]
対象のマシンが拒否したため、接続できません。
アクティブでないサービスに接続しようとした場合、または要求したアドレスにサービスプロセスが存在しなかった場合に起こります。

[ 対処方法 ]
対象のマシン上のサービスをアクティブにするか、またはサービスがなくなっていた場合は再度起動します。
セキュリティ上の理由からこのサービスを提供したくない場合は、ユーザーグループにそのことを伝え、できれば代替サービスを提供します。

では、なぜConnection refused になるのでしょうか?
その答えは以下に有りました。
http://www5d.biglobe.ne.jp/stssk/rfc/rfc793j.html

[ 抜粋 ]
1.  If the connection does not exist (CLOSED) then a reset is sent
    in response to any incoming segment except another reset.  In
    particular, SYNs addressed to a non-existent connection are rejected
    by this means.

つまり、CLOSED の状態のポートにアクセスすると、リセットがレスポンスとしてアクセス元に送られるそうです。

で、リセットって何?とググると、以下が出てきました。
http://e-words.jp/w/RST%E3%83%91%E3%82%B1%E3%83%83%E3%83%88.html

[ 抜粋 ]
TCPで接続を中断・拒否する際に送られるパケット。
TCPヘッダの制御フラグでRSTフィールドがセットされたパケットのこと。
接続要求を拒絶したり、確立された接続を一方的に切断する際に送られるもの。

最後に、ポートを閉じている、とはどういう状態か、という点に関しては、RFC793 ではCLOSED の状態である、ということでした。

2015年4月23日木曜日

OSレベルで、SSLv3.0 を無効にして、Webサーバにアクセス

[ 目的 ]
POODLE 対策として、SSLv3.0 をWebサーバがあるPCのOSで制御して、サーバアプリケーションには修正しないようにすることを目的としている。


[ やり方 ]
OSレベルでSSLv3.0 を無効にするやり方
http://news.mynavi.jp/column/windows/314/

サーバ証明書の生成方法(CentOS6)
http://qiita.com/kgbu/items/44535b31c0f1a062693d


[ 環境 ]
○サーバ環境
Windows Server 2012
IE10
SSL/TLS の設定は、IEから実施

○クライアント環境
Windows7
IE8
SSL/TLS の設定は、IEから実施

[ 結果 ]
サーバ内からlocalhost 接続した場合は、SSLv3.0 は無効化されていたが、
クライアントPC からサーバへ接続は可能であった。

2015年4月21日火曜日

[ Sublime Text 2 ] Package Control インストール

インストールの方法は、色々な人が書いているので、ハマりどころだけ。

色々な人が書いているのは、その時点で最新のPackage Control をインストールするPython コードを、そのまま記載しているようです。

これをそのままコピペして、プロキシ設定しても失敗します。

添付した画像には、2014年と2015年現時点での差分があります。
引数や、接続先URL が変更になっているようです。


これを考慮して、かつ、プロキシ通るように、ProxyHandler の引数に、Proxy サーバのURL なり、ユーザ名、パスワードなりを入力して、Package Control をインストールします。
#黄色い行の2行目の最後に、ProxyHandler の引数設定をしています。

Sublime Text 2
http://www.sublimetext.com/2

現時点での、Package Control のサイトはこちら。
https://packagecontrol.io/installation#st2

2015年1月27日火曜日

SQL Server 2012 のDB Export とAttach

[ 目的 ]
DB の一部をExport する方法を実施する。

[ やり方 ]
まず、取得したいDB のコピーを作成する。

Export Data... を選択する



Server name は、DB が存在するサーバ名を選択する。

Server name は、DB のコピーを出力するサーバを指定する。

上の画面で、New を押すと、コピーするDB の名前が決められる。
今回は、exportdb とした。
また、名前を入力すると、DB のファイル(MDF ファイル)がどこに作られるかが確認できる。



コピーしたいテーブルを選択する。



ここまでで、コピーは完了。
今度は、出力されたmdf ファイルの中身を見る方法。

Databases 上で右クリックして、Attach... を選択。



Add.. を押す。

先ほどExport した、exportdb.mdf ファイルを選択する。

以下の画面で、mdf ファイルが追加されるのを確認して、OK を押すと、MS SQL Server Management Studio で、Export したDB を参照することができる。

2015年1月16日金曜日

Jetty, Axis2 でlog4j のログを出すための設定パラメータ

[ 目的 ]
サーブレット周りのOSS に、外部から入力されたデータを確認したい

[ 解決策 ]
Jetty とAxis2 を利用している場合、以下のパラメータを設定すると、 データ入力されているかどうかが、なんとなく確認可能。
log4j.logger.httpclient.wire.header=DEBUG
log4j.logger.httpclient.wire.content=DEBUG
log4j.logger.org.apache.commons.httpclient=DEBUG
log4j.logger.org.apache.axis2.util=DEBUG
log4j.logger.org.eclipse.jetty=DEBUG

ちなみに、log4j.properties に設定する上記のパラメータは、
log4j.logger.<package name>=<log level>
とすれば、大体ログ出力できそうです。