はじめに
2年ほど前にSSL証明書をシェルコマンドで手作りしていてそれを変えたいと思っていました。残念ながらその時は力及ばず挫折しました。なんとなく昨日ふと思い立って続きをやってみたところなんかそれっぽくなったので、記録を残しておきたいと思います。
概要
本記事は以下の内容について書きます
- rubyで秘密鍵の作成
- rubyでCSRの作成
- sinatra + hamlで入力フォームの作成
- sinatra + hamlで表示(views)の作成
完成形としては、ブラウザでアクセスできる簡単なページを作成し、
そこでCSRに必要な情報を送信すると秘密鍵の文字列とCSRの文字列が表示される。
というものです。
注意点
本記事で作成された秘密鍵とCSRについては、データの確認中です。SSLの申請用途として使用し得るものなのか検証ができていません(ので誰か詳しい人教えて下さい)。
テストやエラー処理は記述していません。
環境
rubyがちょっと古い点に関しては後述します
- ruby 2.1.5
- sinatra 1.4.6
- MAC OS 10.11.1
- haml 4.0.7
Rubyで秘密鍵の作成
参考URL: http://docs.ruby-lang.org/ja/1.8.7/class/OpenSSL=3a=3aPKey=3a=3aRSA.html
のちほど全体のssl.rbを記載しますが、秘密鍵の部分のクラスは以下のとおりです。
かなりの部分を参考URLを参考にしています
シェルコマンドで言うと
$ openssl genrsa -rand (擬似乱数ファイル名) -des3 (キー長) > (秘密鍵ファイル名)
に当たる部分です。
class PrivateKey
SEED_MACHINE_PATH = '/dev/random' # ランダム生成器の指定
ENTROPY = 100
BITSIZE = 2048 # 暗号化強度
# fqdnだけ指定します。
def initialize(fqdn)
@fqdn = fqdn
@rsa = ""
end
# 秘密鍵のデータ作成部分
def generate
# シードの登録
OpenSSL::Random.seed(File.read(SSL::PrivateKey::SEED_MACHINE_PATH, SSL::PrivateKey::ENTROPY))
@rsa = OpenSSL::PKey::RSA.generate(SSL::PrivateKey::BITSIZE)
end
end
参考URLのところに書いてあったとおり、ランダムなシードを設定して指定したbitsizeの秘密鍵を作成します。
rubyでCSRの作成
参考URL: http://docs.ruby-lang.org/ja/2.0.0/class/OpenSSL=3a=3aX509=3a=3aRequest.html
こちらも秘密鍵同様、該当部分を一部抜粋します
いわゆる
$ openssl req -new -key (秘密鍵ファイル名) -out (CSRファイル名)
に当たる部分です。
class CSR
# いわゆるsubjectデータと秘密鍵を指定
def initialize(fqdn, country, state, locality, organization, organization_unit, key)
@fqdn = fqdn
@country = country
@state = state
@locality = locality
@organization = organization
@organization_unit = organization_unit
@key = key
end
# 作成のところ
def generate
@rsa = OpenSSL::PKey::RSA.new(@key)
@csr = OpenSSL::X509::Request.new
@csr.subject = add_subject()
@csr.version = 0
@csr.public_key = @rsa.public_key
@csr.sign(@rsa, "sha256")
@csr
end
private
# subjectの追加
def add_subject
name = OpenSSL::X509::Name.new
name.add_entry('C', @country)
name.add_entry('ST', @state)
name.add_entry('L', @locality)
name.add_entry('O', @organization)
name.add_entry('OU', @organization_unit)
name.add_entry('CN', @fqdn)
name
end
end
気をつけたポイントは、参考サイトだとattributeを追加して代替アドレスを指定している部分があるんですが、CSRを普通にshのコマンドで作成したものとそこだけ差異がでるので思い切って削ってみました。
あとは、基本去年からsha2がデフォルトとなっているのでsha256の設定も入れました。
ssl.rbの全体
全体のクラスのコードは以下の様な感じです。
module SSL
require 'openssl'
class PrivateKey
SEED_MACHINE_PATH = '/dev/random'
ENTROPY = 100
BITSIZE = 2048
def initialize(fqdn)
@fqdn = fqdn
@rsa = ""
end
def generate
OpenSSL::Random.seed(File.read(SSL::PrivateKey::SEED_MACHINE_PATH, SSL::PrivateKey::ENTROPY))
@rsa = OpenSSL::PKey::RSA.generate(SSL::PrivateKey::BITSIZE)
end
class CSR
def initialize(fqdn, country, state, locality, organization, organization_unit, key)
@fqdn = fqdn
@country = country
@state = state
@locality = locality
@organization = organization
@organization_unit = organization_unit
@key = key
end
def generate
@rsa = OpenSSL::PKey::RSA.new(@key)
@csr = OpenSSL::X509::Request.new
@csr.subject = add_subject()
@csr.version = 0
@csr.public_key = @rsa.public_key
@csr.sign(@rsa, "sha256")
@csr
end
private
def add_subject
name = OpenSSL::X509::Name.new
name.add_entry('C', @country)
name.add_entry('ST', @state)
name.add_entry('L', @locality)
name.add_entry('O', @organization)
name.add_entry('OU', @organization_unit)
name.add_entry('CN', @fqdn)
name
end
end
end
sinatra + hamlで入力フォームの作成
ssl.rbでクラスを作成したので、フォームから入力した値を利用してsslクラスのライブラリを呼びだそうとしてみます。
はまった点
私の環境だと ruby2.2.3からsinatraを起動すると、起動のコメントはでますが、localhost:4567につながらないという事象が起こりました。Ctrl+Cで停止させてもコンソールが止まってしまって戻ってこず、結局ruby2.1.5で起動させました。
回避に過ぎないので、解決法があったら誰かおしえて下さい。
利用しているgemのインストール
Gemをインストールします。bundle installで一気に入れてしまおうと思いますので
$ bundle init
して、Gemfileを作成します。
source "https://rubygems.org"
gem 'sinatra', '1.4.6'
gem 'sinatra-contrib', '1.4.6'
gem 'haml', '4.0.7'
こちらを作成したら $ bundle installしてください。
sinatraの準備&app.rbの作成
sinatraを書きます。基本は下記の公式サイトを参考に。
参考URL: http://www.sinatrarb.com/intro-ja.html
require 'sinatra'
require 'sinatra/reloader'
# ruby2.2.3で動かなかった時の試行錯誤(効果なしのところ)
set :environment, :development
# set :port, 5678
set :bind, '127.0.0.1'
# http://localhost:4567/にアクセスした時に表示する内容
get '/' do
haml :index
end
# http://localhost:4567/からフォームを入力し
# 遷移した先のcompleteが表示する内容
get '/complete' do
@fqdn = params[:fqdn]
@country = params[:country]
@state = params[:state]
@locality = params[:locality]
@organization = params[:organization]
@organization_unit = params[:organization_unit]
require './lib/ssl.rb'
rsa = SSL::PrivateKey.new(@fqdn)
@key = rsa.generate()
csr = SSL::CSR.new(
@fqdn,
@country,
@state,
@locality,
@organization,
@organization_unit,
@key)
@csr = csr.generate()
haml :complete
end
sinatra + hamlで表示(views)の作成
トップにアクセスした際のhamlは以下のとおりです。
念のためですが、hamlファイルはviewsディレクトリに置くといい感じです
<!DOCTYPE html>
%html{:lang => "ja"}
%head
%meta{:charset => "UTF-8"}/
%body{:style => 'text-align:center;'}
%h1
CSR TEST
%div
%form{ :action => 'complete', :methods => 'get'}
%p
%strong
FQDN
%input{:type => 'text', :name => 'fqdn'}
%p
%strong
COUNTRY
%input{:type => 'text', :name => 'country'}
%p
%strong
STATE
%input{:type => 'text', :name => 'state'}
%p
%strong
LOCALITY
%input{:type => 'text', :name => 'locality'}
%p
%strong
ORGANIZATION
%input{:type => 'text', :name => 'organization'}
%p
%strong
ORGANIZATION-UNIT
%input{:type => 'text', :name => 'organization_unit'}
%p
%input{:type => "submit", :value => '作成する'}
つづいてコンプリートページです。
<!DOCTYPE html>
%html{:lang => "ja"}
%head
%meta{:charset => "UTF-8"}/
%body{:style => 'text-align:center;'}
%h1
CSR TEST
%p
COMPLETE
%h2
INPUT
%div
%p
%strong
FQDN:
= @fqdn
%p
%strong
COUNTRY:
= @country
%p
%strong
STATE:
= @state
%p
%strong
LOCALITY:
= @locality
%p
%strong
ORGANIZATION:
= @organization
%p
%strong
ORGANIZATION-UNIT:
= @organization_unit
%hr
%h2
DATA
%h3
PrivateKey
%div
%p
= find_and_preserve("<textarea cols='100' rows='30'>#{@key}</textarea>")
%h3
CertificateSigningRequest
%div
%p
= find_and_preserve("<textarea cols='100' rows='30'>#{@csr}</textarea>")
textareaタグを本来であれば使いたいところなのですが、これを使ってしまうとhamlが展開された先のHTMLでhamlのインデントが秘密鍵とCSRの内容にしっかりと付いてしまいます。
それをふせぐために find_and_preserveメソッドがhamlに用意されています。この中で内容を展開することで、左側にインデント分の余白が無い文字列データを出すことができます。
実際の画面と操作イメージ
超簡素ですみません。
$ ruby app.rb するとサーバが起動します。
画像の様にいわゆるCSRの情報を書いていきます。
作成するボタンを押すと下記の画像のようなページが出ます。
- input => 入力内容の確認
- DATA>PrivateKey => 秘密鍵のテキスト
- DATA>CertificateSigningRequest => CSRのテキスト
一応ここで作成されたものについてopensslのコマンドでmodulusとtextを確認したところそんなに悪いデータにはなっていなさそうでした。しかし要検証だと思います。
最後に
SSLのデータは秘密鍵の取扱いなども有りますので、慎重さが求められます。
しかしながら今までshで手作業でやってた部分もGUIでできるようになると、手間の削減や自分がやらなくて良い作業になったりしますからやらんとあかんのですが、なかなか。
ともあれOpensslの仕様の部分が全然わかっていないのでココらへんの勉強もしっかりしないといけないですね。