Last-modified: 2016-06-21 (火) 00:47:55
Azure/App Serviceでクライアント認証がしたい

概要

Azure App Serviceにてクライアント認証(自己証明書)をします。
なおこの機能(clientCertEnabled)はBasicプラン以上でなければ使えません。
ここではnode.jsを使います。クライアント証明書要求などはiisが行うため、node側はリクエストヘッダーのx-arr-clientcertを検証します。
App Serviceのnodeはiisのモジュール(iisnode)として動作するため、
node.js/クライアント認証がしたい
のように、nodeにクライアント認証を任せることはできません。
今回はサーバー証明書も解説目的で自己証明書を使います。

サーバー証明書、クライアント証明書を作成

  1. 自己証明書を作成
    opensslが入っていない場合は、gitなど同梱されているものを入れるか、chocolateyでopensslを入れるか、公式サイトからいれておく
    Everything is expanded.Everything is shortened.
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
    
    -
    !
    -
    |
    !
    -
    !
     
    -
    !
     
     
     
    -
    !
     
     
    
    REM private key作成
    openssl genrsa 4096 > <private key出力先>
    REM 証明書署名要求(CSR)作成
    REM -configはつけなくてもいける環境と[Unable to load config info from /usr/local/ssl/openssl.cnf]などとでる環境があった
    openssl req -batch -new -sha256 -key <private keyパス> -out <CSR出力パス> -subj <証明書名(C:国名, ST:都道府県, L:都市名, O:組織名, OU:部署, CN:発行元)> -config <openssl.cnfのパス>
    REM 証明書作成
    openssl x509 -req -days 3650 -signkey <private keyパス> -in <CSRパス> -out <証明書出力パス>
     
    REM 例. SSLサーバー証明書作成(証明書内の公開鍵と対をなすprivate key:server.key, 証明書:server.crt)
    openssl genrsa 4096 > "D:\server.key"
    openssl req -batch -new -sha256 -key "D:\server.key" -out "D:\server.csr" -subj "/C=JP/ST=Tokyo/L=hoge/O=foo/OU=bar/CN=foo.bar.com" -config "..\ssl\openssl.cnf"
    openssl x509 -req -days 3650 -signkey "D:\server.key" -in "D:\server.csr" -out "D:\server.crt"
     
    REM 例. SSLクライアント証明書作成(証明書内の公開鍵と対をなすprivate key:client.key, 証明書:client.crt)
    openssl genrsa 4096 > "D:\client.key"
    openssl req -batch -new -sha256 -key "D:\client.key" -out "D:\client.csr" -subj "/C=JP/ST=Tokyo/L=hoge/O=foo/OU=bar/CN=foo.bar.com" -config "..\ssl\openssl.cnf"
    openssl x509 -req -days 3650 -signkey "D:\client.key" -in "D:\client.csr" -out "D:\client.crt"
  2. クライアント証明書(X509形式)をPKCS12形式に変換
    PKCS12形式に変換した証明書をダブルクリックしてインストールすると、IEなどでCertificateRequestを受信すると、証明書選択ダイアログが出るようになる
    Everything is expanded.Everything is shortened.
      1
      2
      3
      4
    
     
     
    -
    !
    
    openssl pkcs12 -export -in <証明書(X509)パス> -inkey <private keyパス> -out <証明書(PKCS12)出力パス>
     
    REM 例.
    openssl pkcs12 -export -in "D:\client.crt" -inkey "D:\client.key" -out "D:\client.pfx"
  3. 必要ならばクライアント証明書(PKCS12)をダブルクリックしてWindowsにインストール
    000013.png

server.js

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 
 
 
 
 
 
 
-
-
|
|
|
|
!
|
-
|
|
-
|
!
!
|
|
|
-
|
|
!
|
-
|
|
|
!
!
 
-
|
|
!
 
-
|
|
!
 
-
|
!
var http = require('http');
var fs = require('fs');
var express = require('express');
var app = express();
var x509 = require('x509.js');
var ca = JSON.stringify(x509.parseCert(fs.readFileSync('client.crt')));
 
app.use(function (req, res, next) {
    // iisnodeでTLS handshakeのCertificateVerify値を使う検証方法がわからなかったので
    // とりあえずクライアント証明書の内容が全て一致していることを確認している
    // 
    // x509.jsでパースする際にエラーとなるので、テキストを整形してみたが
    // そもそもが全うな検証方法でははないのだから、[var ca]をx-arr-clientcert値と同じフォーマットにして、単純に比較したのとかわらない
    var clientcert = "-----BEGIN CERTIFICATE-----\n";
    for (var i = 0; i < req.headers["x-arr-clientcert"].length; ++i)
    {
        clientcert += req.headers["x-arr-clientcert"][i];
        if ((i !== 0) && (i % 64 === 0))
        {
            clientcert += "\n";
        }
    }
    clientcert += "\n-----END CERTIFICATE-----";
    
    if (ca === JSON.stringify(x509.parseCert(clientcert)))
    {
        console.log("success!");
        next();
    }
    else
    {
        console.log("ng...");
        res.statusCode = 406;
        res.send("");
    }
});
 
app.get('/', function (req, res) {
    console.log("/");        
    res.send("/");
});
 
app.get('/foo', function(req, res){
    console.log("/foo");
    res.send("foo");
})
 
var server = http.createServer(app).listen(process.env.port, function () {
    console.log("server listening on port %d", server.address().port);
});

App Serviceの作成と設定

  1. Azure Portalを開く
  2. 必要ならば新しいApp Serviceを作る(プランはBasic以上にする)
  3. Azure Resources Explorerを開く
  4. 検索ボックスにデプロイ先のアプリ名を入れて検索する(Microsoft.Web/sites)
  5. [Read Only]になっていたら[Read/Write]にする
  6. [Edit]ボタンを押す
  7. [clientCertEnabled]をtrueにして、[PUT]ボタンを押す
    000015.png

デプロイ(Visual Studio)

  1. プロジェクトのコンテキストメニューを開き、[公開]からデプロイする
    client.crtもデプロイ対象に含める

確認

補足

Azure上で動作しているnodeのconsole.logを見るには、

  1. Azureクラシックポータルからアプリの[構成]で[Visual Studio Online での編集]を[オン]にする
  2. [ダッシュボード]にある[Visual Studio Online での編集]から、Visual Studio Onlineに移動する
  3. Outputウインドウとソースを表示してconsole.logの確認、およびソースの編集ができる

検証時の環境