前ポストで、暗号化された SOAP メッセージを、サービス側が期待して待つようにした。今度はクライアントに暗号機能を与えてみる。
■ コード
クライアントのリクエスト送信コードは以下のようになる。見ての通りいかにもセキュリティっぽいコードは無い。ただし ConfigurationContext というものを使って、実行時の振る舞いに設定情報を反映させることになる。
import …略…
public class Client {
private static final String URL
= "http://localhost:8888/axis2/services/StockQuoteService";
private static final String AXIS2_REPO = "resources/axis-repo";
public static void main(String[] args) throws Exception {
ConfigurationContext ctx = ConfigurationContextFactory
.createConfigurationContextFromFileSystem(
AXIS2_REPO, AXIS2_REPO + "/conf/axis2.xml");
Options options = new Options();
options.setAction("urn:getPrice");
options.setTo(new EndpointReference(URL));
ServiceClient client = new ServiceClient(ctx, null);
client.setOptions(options);
OMElement response = client.sendReceive(getPayload("test"));
System.out.println(response);
}
private static OMElement getPayload(String value) {
OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace ns = factory.createOMNamespace(
"http://quickstart.samples/xsd","ns1");
OMElement elem = factory.createOMElement("getPrice", ns);
OMElement childElem = factory.createOMElement("symbol", null);
childElem.setText(value);
elem.addChild(childElem);
return elem;
}
}
main() 冒頭で ConfigurationContext を初期化している AXIS2_REPO はこんな感じ。
axis-repo/
conf/
axis2.xml
modules/
addressing-1.5.1.mar
rampart-1.4.mar
modules 下の *.mar は手近などこかから持ってきて配置する。axis2.xml は、サンプルなんかに含まれるものを加工すると楽。ここでは rampart サンプル 05番 の client.axis2.xml を使う事にした。このサンプルには IN と OUT 両方の暗号化設定が記述されているけど、今やりたいのはリクエストだけなので InflowSecurity の記述は削除する。
残った要点はこの部分。
<module ref="rampart" />
<parameter name="OutflowSecurity">
<action>
<items>Encrypt</items>
<encryptionUser>service</encryptionUser>
<encryptionPropFile>client.properties</encryptionPropFile>
</action>
</parameter>
rampart モジュールを使う事の宣言と、OutflowSecurity のところで、クライアントからサービス方向のセキュリティ設定が記述されているのがわかる。
上の XMLで <encryptionPropFile>から参照されている client.properties は、以下のような内容で resources 直下に作る。
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=rampart
org.apache.ws.security.crypto.merlin.file=ourkeys.jks
~crypto.merlin.file で指定しているキーストア・ファイル ourkeys.jks は、ここでは手を抜いてサーバと同じものを使う事にした(なまじ真面目にやると、思い通りに動かないときキーの作り方が悪いのか設定の仕方が悪いのか切り分けしにくくて面倒くさい)。この ourkeys.jks も resouces 下に置く。resouces には、お好みで log4j.properties なども置いておく。
ここまでまとめるとこんな感じになる
{作業ディレクトリ}
resources/
axis-repo/ … 前述
client.properties
log4j.properties
ourkeys.jks
src/
Client.java
PWCBHandler.java
■ ビルド → 実行
この作業ディレクトリで、こんな要点の build.xml を書く。
- axis2 の lib ディレクトリ内の *.jar を java タスク と javac タスクのクラスパスに含める。
- build フォルダを作って、javac で コンパイルした2つのクラスファイルをそこに入れる。
- resources 下の client.properties、log4j.properties、ourkeys.jks を build 下にコピーする。
- java タスクでは build/ もクラスパスに含める。
実行する前に、TCPMon (他の何かでもOK)をポート:8888で待たせておく。
で、ant から実行。
上手くいくと、こんな感じのリクエストが送られる。
POST /axis2/services/StockQuoteService HTTP/1.1
Content-Type: text/xml; charset=UTF-8
SOAPAction: "urn:getPrice"
User-Agent: Axis2
Host: 127.0.0.1:8888
Transfer-Encoding: chunked
83c
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<xenc:EncryptedKey Id="EncKeyId-urn:uuid:7934811BD783B11FAA12585771335152">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference>
<ds:X509Data>
<ds:X509IssuerSerial>
<ds:X509IssuerName>CN=service,OU=a,O=a,L=a,ST=a,C=aa</ds:X509IssuerName>
<ds:X509SerialNumber>1258572637</ds:X509SerialNumber>
</ds:X509IssuerSerial>
</ds:X509Data>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>KWbsTXPE5TB4KzH9VRe2TJXT51tPDQ3z0UilymTUu165MR/hGB7uHyscSeYB1TEkA+23jrx3S9uo+nFahrBOuMOUsUvTckYvmi66K/0GLP8QAjg0gubiFmxF6d3JjI5mNuaGHWWspplKxEAFS99Xp4MQTXO52Gn/qo99d+vibzg=</xenc:CipherValue>
</xenc:CipherData>
<xenc:ReferenceList>
<xenc:DataReference URI="#EncDataId-6393126" />
</xenc:ReferenceList>
</xenc:EncryptedKey>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<xenc:EncryptedData Id="EncDataId-6393126" Type="http://www.w3.org/2001/04/xmlenc#Content">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:Reference URI="#EncKeyId-urn:uuid:7934811BD783B11FAA12585771335152" />
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>9l9w23N4cauBPWarHAMrPI2bjzR5bfZNsHKAC5NTkMNzitW/3JM74mWh83LuhH5e/RAV3mxaDcYJczmLD0tiVqJJGGuF85p+AocHCnjWz1we0/dYaMoDBSYn2kINp+6//70wwGBxHByT53OP7SpLfkJD9Fnnd4TVBg3iwamszR49oCYv64Z3wYLV/Ie6OMVy+kFbcXf/xFG/MpfmrJdkaEwq+uheOrdT3yYneoNoUbvvhmVHirnEXHryGfKu+36pgIpKDkrvbS9EFZ3WmIo+pQwJ3wq2BzFLWK/ackH0LV8=</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</soapenv:Body>
</soapenv:Envelope>0
(ちなみにレスポンス方向は暗号化してないので、プレーンなSOAP メッセージのままなのが TCPMon を見るとわかる。)
この見所は、以下のようなところ
- Header に EncryptedKey、Body に EncryptedData があること
- EncryptedKey の暗号方式はRSA、EncryptedData の暗号方式はAES
- EncryptedKey の DataReference から、EncryptedData を参照している事
- EncryptedData の SecurityTokenReference から、EncryptedKey を参照している事
- X509IssuerSerial に鍵を作ったときに入力した内容が現れている事
■ 補足
Rampart のsample を見ると、タイムスタンプ、署名、署名+暗号化、暗号化部分を別のMIMEパートに分けて出す方法など、必要な情報がほぼ十分に載っていてお勧め。