ラベル SSL の投稿を表示しています。 すべての投稿を表示
ラベル SSL の投稿を表示しています。 すべての投稿を表示

2013/08/12

golang certificate ssl handshake

2013/08/14 追記
Go界のジェフ・ベゾスが勝手にウンコードを添削してくれたよ!! みんな、こっち見てね!

ヒャッハー、証明書好きのオレ様が帰ってきたぜ!!!というわけで遅延証明書よりも在留証明書よりも公開鍵証明書を愛する私が Go を使ってサーバから証明書をゲットするその日がやってきました。Go には crypto/tls という素敵パッケージもあるので何の心配もありません。Config としてサーバ認証を真面目にしない InsecureSkipVerify を true としてあげればどんな IP address, port のペアに対してだって証明書を取ってくることが可能なのです。実行するとこんな:
$ go run get_cert.go www.google.com
i=0
[[{[2 5 4 6] US}] [{[2 5 4 10] Google Inc}] [{[2 5 4 3] Google Internet Authority}]]
2013-07-31 11:40:49 +0000 UTC
2013-10-31 23:59:59 +0000 UTC
[[{[2 5 4 6] US}] [{[2 5 4 10] Google Inc}] [{[2 5 4 7] Mountain View}] [{[2 5 4 8] California}] [{[2 5 4 3] www.google.com}]]
i=1
[[{[2 5 4 6] US}] [{[2 5 4 10] Equifax}] [{[2 5 4 11] Equifax Secure Certificate Authority}]]
2012-12-12 15:58:50 +0000 UTC
2013-12-31 15:58:50 +0000 UTC
[[{[2 5 4 6] US}] [{[2 5 4 10] Google Inc}] [{[2 5 4 3] Google Internet Authority}]]
OpenSSL みたいな pretty な感じに出してくれるのはとりあえず見つからなかったけど頑張れば自分で書けるぜ!!
package main

import (
        "crypto/tls"
        "crypto/x509"
        "fmt"
        "log"
        "os"
)

func main() {
        if len(os.Args) < 1 {
                log.Fatal("You must input a hostname")
        }
        peerCertificates, err := GetCert(os.Args[1])
        if err == nil {
                for i, Cert := range peerCertificates {
                        fmt.Printf("i=%d\n", i)
                        fmt.Println(Cert.Issuer.ToRDNSequence())
                        fmt.Println(Cert.NotBefore)
                        fmt.Println(Cert.NotAfter)
                        fmt.Println(Cert.Subject.ToRDNSequence())
                }
        } else {
                log.Fatal(err)
        }
}

func GetCert(host string) ([]*x509.Certificate, error) {
        config := &tls.Config{InsecureSkipVerify: true}
        conn, err := tls.Dial("tcp", host+":443", config)
        var peerCertificates []*x509.Certificate
        if err == nil {
                connectionState := conn.ConnectionState()
                peerCertificates = connectionState.PeerCertificates
        }
        return peerCertificates, err
}
ポートは決め打ちだぜ!!

2010/09/23

diffie-hellman dhe

なんか、質問受けてたんですよね、きっと
DH と DHE って何が違うの? って話になったんで検索しました

夜でもアッサム: SSL(TLS)のDHとDHEの違い
なんかもぉ、しっかり書いてありましたよ
  • DHEのEは、EphemeralのE。
  • DHEは、DHパラメータを通信時に動作に作成する。
  • DHは、DHパラメータは証明書に書かれている物を使う。よって、static DHとも呼ばれる。
  • DH-RSAは、証明書にCAがRSAで署名したという事。証明書にRSA鍵が入っているわけではない。
  • DHE-RSAは、証明書にRSA鍵が入っている。


でね、気になるので原典をあたるわけですよ、
英語だと辛いから和訳ですけど
The TLS Protocol Version 1.0
DH は、認証局(CA)によって署名されたサーバー証明書に含まれているDiffie-Hellman パラメータを使用した暗号スイートを示すものである。DHEは、CA によって署名された DSS またはRSA証明書により署名されたDiffie-Hellmanパラメータを使用した一時的 Diffie-Hellman を示すものである。使用される署名アルゴリズムは、DHまたは DHEパラメータの後に指定される。
おぉ、なるほど、これだ

この前後に、大量の CipherSuite が記載されています。
抜粋すると
CipherSuite TLS_RSA_WITH_RC4_128_MD5 = { 0x00,0x04 };
CipherSuite TLS_RSA_WITH_RC4_128_SHA = { 0x00,0x05 };
CipherSuite TLS_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x0A };
CipherSuite TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x0D };
CipherSuite TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x10 };
CipherSuite TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x13 };
CipherSuite TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x16 };


SSL/TLS って何をしてるかってーと
  • サーバ(とクライアント)の認証
  • 通信の暗号化
  • 通信の改竄検知
でした。で、それぞれ
  • 証明書(=身元情報+公開鍵+それらの認証局による署名)と公開鍵に対応する秘密鍵を持ってること
  • 外部とは秘密に交換した情報を鍵とした暗号化
  • 外部とは秘密に交換した情報を鍵とした HMAC
で実現してました。

何が大切かっていうと「信頼できる相手」と「鍵交換」できるってことね

TLS_RSA_WITH
鍵交換に使える RSA 証明書があるんで、秘密鍵持ってることを確認して認証して、証明書の鍵で鍵交換
TLS_DH_???_WITH
DH 証明書があるんで、鍵交換で同じもの共有できると秘密鍵の所有も確認できるんで認証もできる。??? ってのは証明書の署名。
TLS_DHE_???_WITH
鍵交換には使えないかもしれない? RSA/DSS 証明書があるんで、秘密鍵持ってることを確認して認証して、公開鍵で署名した DH の鍵なら信頼できるんでそれを使って鍵交換。??? は、何だ? 証明書の公開鍵か? 署名か?


かな、ちょっと想像入ってるけど。
分からない部分もあるけど。

っていうか鍵交換に使える RSA 証明書しか見たことないし
TLS_RSA_WITH だけで許してもらえるよね、きっと。

2010/07/24

ssl-v3

今まで OpenSSL/GnuTLS/Java のライブラリを使って証明書取ってきました
そろそろ SSL のこと詳しく知ってもいいんじゃない?
でも暗号とか難しそうだしどうしようかなぁ

手元に「マスタリングTCP/IP SSL/TLS編」はあったので
つらつらと眺めたり wireshark のログを見て分かってきたので
更に原本にあたろうと思い SSLv3 の仕様を眺めることに
The SSL Protocol Version 3.0
SSLv2 はもぉ使わない方がいいらしいですしね
TLSv1 でもいいんですが、何となく SSL の方を選択

すげー簡単に SSL の手順書くと
  • client hello で使える暗号スイートリストと client random 送信
  • server hello で使う暗号スイートと server random 送信
  • server certificate でサーバの証明書送信
  • client key exchange で pre-master secret 送信
  • 両者で鍵作って finish
細かいことは解説してるサイトを見るということで

よーし、とーさん Python で実装しちゃうぞー、と思いますよね
データのやりとりは TCP などの上で SSL Record とかいう
version と type と length だけ頭に付ける単純な入れ物に入れて行われて
中身もまぁ大体似たようなもんでした、type と length くらい

client hello は random とか送るんだけど決め打ちで
時間を入れるとこがあるんでそこだけ真面目に入れてみました

server hello done まではまだ暗号とか使ってないんで
パースして証明書を取り出してみました

ここまでで力尽きました
標準ライブラリだけでガンバりましたヨ!!
def bin2int(b):
return sum(256**e*x for (e, x) in enumerate(reversed([ord(x) for x in b])))

def recv_server_hello_from(s):
messages = []

received = ''
while True:
while len(received) &lt; 5:
bytes = s.recv(1024)
if len(bytes) == 0:
raise socket.error(&quot;0 bytes received&quot;)
received += bytes

header, received = received[:5], received[5:]
content_type = ord(header[0])
version_major = ord(header[1])
version_minor = ord(header[2])
if not (content_type == 22
and version_major == 3 and version_minor in range(3)):
raise socket.error(&quot;not SSLv3/TLS&quot;)
content_length = bin2int(header[3:5])

while len(received) &lt; content_length:
bytes = s.recv(1024)
if len(bytes) == 0:
raise socket.error(&quot;0 bytes received&quot;)
received += bytes

content, received = received[:content_length], received[content_length:]

class ServerHelloDone(Exception):
pass
try:
while content:
msg_type = ord(content[0])
if msg_type == 14:
raise ServerHelloDone
if msg_type not in (2, 11, 12, 13, 14, 15, 16, 20):
raise socket.error(&quot;not SSLv3/TLS msg_type = %d&quot; % msg_type)
msg_length = bin2int(content[1:4])
msg = content[4:4+msg_length]
messages.append((msg_type, msg_length, msg))
content = content[4+msg_length:]
except ServerHelloDone:
break

return messages

def parse_server_certificate(data):
certs = []

length, data = data[:3], data[3:]

while data:
length = bin2int(data[:3])
certs.append(data[3:length+3])
data = data[length+3:]

return certs

if __name__ == '__main__':
import socket
import struct
import time

client_hello = (b'\x16\x03\x00\x00`\x01\x00\x00\\\x03\x00'
+ struct.pack('&gt;I', int(time.time()))
+ 'L?\xac\xc8\xc5lw?\xa8\x1a\xce\xdf\x8d\xc0\x96'
+ '\t\x97\xab\xa1dv\xc1\xec\x9aj\xc8\x0f(\x00\x004\x00'
+ ':\x009\x008\x005\x004\x003\x002\x00/\x00\x1b\x00\x1a'
+ '\x00\x19\x00\x18\x00\x17\x00\x16\x00\x15\x00\x14\x00'
+ '\x13\x00\x12\x00\x11\x00\n\x00\t\x00\x08\x00\x06\x00'
+ '\x05\x00\x04\x00\x03\x02\x01\x00')

s = socket.socket()
s.connect(('www.google.com', 443))
s.send(client_hello)

for (msg_type, msg_length, msg) in recv_server_hello_from(s):
if msg_type == 11:
for cert in parse_server_certificate(msg):
print '-----BEGIN CERTIFICATE-----'
print cert.encode('base64'),
print '-----END CERTIFICATE-----'
これ動かすと
$ python sslv3.py 
-----BEGIN CERTIFICATE-----
MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBMMQswCQYDVQQG
EwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMNVGhh
d3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0xMTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVT
MRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApH
b29nbGUgSW5jMRcwFQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jNgtXj9xVo
RaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L05vuuWciKh0R73mkszeK
9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAMBgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0w
K6ApoCeGJWh0dHA6Ly9jcmwudGhhd3RlLmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYI
KwYBBQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzAB
hhZodHRwOi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0ZS5j
b20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUFAAOBgQCfQ89bxFAp
sb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5u2ONgJd8IyAPkU0Wueru9G2Jysa9
zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqA
ibAxWEEHXw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIzCCAoygAwIBAgIEMAAAAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJVUzEXMBUGA1UE
ChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNTEzMDAwMDAwWhcNMTQwNTEyMjM1OTU5WjBMMQswCQYD
VQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMN
VGhhd3RlIFNHQyBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1NNn0I0Vf67NMf59HZGh
PwtxPKzMyGT7Y/wySweUvW+Aui/hBJPAM/wJMyPpC3QrccQDxtLN4i/1CWPN/0ilAL/g5/OIty0y
3pg25gqtAHvEZEo7hHUD8nCSfQ5i9SGraTaEMXWQ+L/HbIgbBpV8yeWo3nWhLHpo39XKHIdYYBkC
AwEAAaOB/jCB+zASBgNVHRMBAf8ECDAGAQH/AgEAMAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEE
BAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFByaXZhdGVMYWJlbDMtMTUwMQYDVR0fBCow
KDAmoCSgIoYgaHR0cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwMgYIKwYBBQUHAQEEJjAk
MCIGCCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMDQGA1UdJQQtMCsGCCsGAQUFBwMB
BggrBgEFBQcDAgYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEBBQUAA4GBAFWsY+re
od3SkF+fC852vhNRj5PZBSvIG3dLrWlQoe7e3P3bB+noOZTcq3J5Lwa/q4FwxKjt6lM07e8eU9kG
x1Yr0Vz00YqOtCuxN5BICEIlxT6Ky3/rbwTRbcV0oveifHtgPHfNDs5IAn8BL7abN+AqKjbc1YXW
rOU/VG+WHgWv
-----END CERTIFICATE-----
証明書が出てきます

つづく (のか!?)

2010/05/13

sni

ことの始まりは
http://twitter.com/k4403/status/13880245576
このつぶやき

Google Reader で証明書に関する警告が出るということで
画像を見てみると FQDN が www.google.co.jp になっていました
ほぉ、www.google.co.jp でもアクセスできるのか、なんて思いつつ
Firefox でアクセスしたら www.google.com にリダイレクトされてしまった
あらら

とりあえず引いてみると
$ host www.google.co.jp
www.google.co.jp is an alias for www.google.com.
www.google.com is an alias for www.l.google.com.
www.l.google.com has address 66.249.89.99
www.l.google.com has address 66.249.89.104
まぁこの辺は色々な仕組みで IP address 渡されてるんだろうけど
でもまぁ、www.google.com の CNAME ってことになってるようで

で、他のブラウザでも、と思って w3m で繋いでみると
Bad cert ident www.google.com from www.google.co.jp: accept? (y/n)
むむむ?
何故 Firefox と結果が違うのだ?
ちなみに https://www.google.com/reader はちゃんと見れるの

で、どうやら 1 IP address で複数の証明書を利用しようとしてるみたいで
それには
  • マルチドメインな証明書 (CN とか SubjectAlternativeName に沢山書く)
  • ワイルドカードな証明書 (* 使う、ホスト名部分だけウマー)
  • Server Name Indication (SNI)
で、google はこの SNI っぽい

SNI?
そもそも何でそんな小細工しないといけなかったかというと
SSL/TLS の handshake の際に IP address だけ使ってたんですって
でも、Virtual Host 的なことは色々なサービスで使いたいのでこれは不便
マルチドメインとかワイルドカードもあったけど根本的に解決しようということなのか
登場したのが SNI だと思うの多分
Server Name Indication - Wikipedia, the free encyclopedia

でまぁ、どうしてるかというと、client hello に繋ぐ先の FQDN 入れるの
そうよね、伝えちゃえばいいのよね
で、Firefox で www.google.co.jp に繋いだところを wireshark で見てみたら
しっかり www.google.co.jp って名前を伝えているようでした
Google は SNI を使っている!!

SNI には 1 つだけ悲しいところがありまして
Windows XP がサポートしていません。2003 もかな。
Windows は CryptoAPI とかいう API で暗号回りを処理してるんだけど
多分それが古いのかな?
で、それを使ってる IE とか chrome が SNI 使えないみたいです。
(chrome はそのうち TLS まわりを自分でもってくるかもしれないけど)

例えば Google App Engine の Roadmap に
App Engine For Business Roadmap - Google App Engine - Google Code
独自ドメインで SSL 使えるようにって書いてあったりしますけど
ワイルドカードはダメだからマルチドメインか SNI かと思い
(IP address をしこたま用意してあるってのもあると思うんですが)
SNI だったら XP 切るのかなぁ〜、と思いました

でも Google だったら証明書も用意してくれるかなって思ったりもしたけど
審査があるんだよなぁ、やっぱ、証明書は自前で用意かなぁ
秘密鍵と CSR を作成してくれて、証明書と中間が用意できるようになるのかなぁ
や、アプリ部分と同様に upload するのかなぁ

ちょっと楽しみ

2010/04/26

python ssl

何か私、勘違いをしていたみたいでして
Python 2.6 だと ssl って module がありました
17.3. ssl — SSL wrapper for socket objects — Python v2.6.5 documentation
ssl.SSLSocket.getpeercert とかあるの

というわけで簡単にいじってみました
import socket
import ssl

sock = socket.socket()
ssl_sock = ssl.wrap_socket(sock)
ssl_sock.connect(('www.google.com', 443))
der = ssl_sock.getpeercert(binary_form=True)
print ssl.DER_cert_to_PEM_cert(der)
として実行してみると
$ python get_cert.py 
-----BEGIN CERTIFICATE-----
MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM
MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x
MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw
FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN
gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L
05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM
BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl
LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF
BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw
Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0
ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF
AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5
u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6
z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==-----END CERTIFICATE-----

$
何だこの変な改行は
まぁ、動いた

あ、や、こんなことしなくても
ssl.get_server_certificate(("www.google.com", 443))
でいいのか

Python 2.6 だったらサーバー証明書取るだけならまぁいける
裏で動いてるのは OpenSSL だから OpenSSL は要るけど
うん、覚えておこう、っていうかもぉ、忘れられないわ

2010/04/23

gnutls python

悪ノリも大概にしなさいという気がしないでもないですが
どうせならと思って gnutls も使ってみようと
Python であれば python-gnutls というのがあります
easy_install でも pip でも使えば入ってしまいます

example とかも揃っているので良く分かってなくても使えてしまいましたが
from gnutls import crypto

x509 = crypto.X509Certificate(open("google.crt").read())

print "version: %d" % x509.version
print "serial number: %s" % x509.serial_number
print "issuer: %s" % x509.issuer.title()
print "not before: %s" % time.ctime(x509.activation_time)
print "not after: %s" % time.ctime(x509.expiration_time)
print "subject: %s" % x509.subject.title()
とか
import socket
import time

from gnutls import connection

cred = connection.X509Credentials()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
session = connection.ClientSession(sock, cred)

session.connect(('www.google.com', 443))
session.handshake()
x509 = session.peer_certificate
session.bye()
session.close()

print "version: %d" % x509.version
print "serial number: %s" % x509.serial_number
print "issuer: %s" % x509.issuer.title()
print "not before: %s" % time.ctime(x509.activation_time)
print "not after: %s" % time.ctime(x509.expiration_time)
print "subject: %s" % x509.subject.title()
とかすると
version: 3
serial number: 205024093581220934167709006090448199471
issuer: C=Za,O=Thawte Consulting (Pty) Ltd.,Cn=Thawte Sgc Ca
not before: Fri Dec 18 09:00:00 2009
not after: Mon Dec 19 08:59:59 2011
subject: C=Us,St=California,L=Mountain View,O=Google Inc,Cn=Www.Google.Com
こんなのが出ました

gnutls.constants.X509_FMT_DER とかあるので DER も読めたり
gnutls.library.functions ってのがあるんでガリガリ書いたり
できるのかな? 試していません

とりあえず証明書は読めました

2010/04/21

scala cast

Jython でできたら Scala でもできるべ JK、と思ったら失敗しやんの
というお話

同じようにしようと思ったんですね、Scala で
scala> import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.SSLSocketFactory

scala> val fc = SSLSocketFactory.getDefault
fc: javax.net.SocketFactory = com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl@10c6406

scala> val sock = fc.createSocket("www.google.com", 443)
sock: java.net.Socket = 17aa961[SSL_NULL_WITH_NULL_NULL: Socket[addr=www.google.com/66.249.89.99,port=443,localport=57185]]

scala> sock.startHandshake
<console>:8: error: value startHandshake is not a member of java.net.Socket
sock.startHandshake
^

scala>
java.net.Socket には startHandshake なんて無いと

あれー、と思って調べてみたら
Java の場合は Socket 作るときにキャストしてたんです
なんで調べてみましたところ
型キャスト - うなの日記
.asInstanceOf でキャストするらしい

とわけで仕切り直し
scala> import javax.net.ssl.SSLSocket                                 
import javax.net.ssl.SSLSocket

scala> val ssl_sock = fc.createSocket("www.google.com", 443).asInstanceOf[javax.net.ssl.SSLSocket]
ssl_sock: javax.net.ssl.SSLSocket = 5b55a9[SSL_NULL_WITH_NULL_NULL: Socket[addr=www.google.com/66.249.89.104,port=443,localport=51395]]

scala> ssl_sock.startHandshake

scala> val session = ssl_sock.getSession
session: javax.net.ssl.SSLSession = [Session-1, SSL_RSA_WITH_RC4_128_SHA]

scala> session.getPeerCertificates
res3: Array[java.security.cert.Certificate] =
Array([
[
Version: V3
Subject: CN=www.google.com, O=Google Inc, L=Mountain View, ST=California, C=US
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

Key: Sun RSA public key, 1024 bits
modulus: 16360039926653679142684174795487342200966220249542631283272411000230225514996552443305086682233800494025854567816692732625...
scala>
取れた!!

Scala の方が Java に対して素直なんですかね
何故 Jython でキャストが不要なのか良く分からず
どのくらい手を加えているのかっつーことなんでしょうか

2010/04/20

sslsocketfactory session getdefault

Jython で Java を使って SSL 張って証明書を取ろうとしました

色々ググったりして SSLSocketFactory ってのがあるということで
java.net パッケージの仕組み
を見てふんふんとする
で、socket を作って getSession したらそこに getPeerCertificates という method が!
In [63]: soc = factory.createSocket('www.google.com', 443)

In [1]: from javax.net.ssl import *

In [2]: sock = factory.createSocket('www.google.com', 443)

In [3]: session = sock.getSession()

In [4]: certs = session.getPeerCertificates()
---------------------------------------------------------------------------
SSLPeerUnverifiedException Traceback (most recent call last)

/tmp/<ipython console> in <module>()

SSLPeerUnverifiedException: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated

In [5]:
うぎゃぁ!!

何か足りないのみたいなのでググり直すと
HTTPS通信にはまる - TNET日々のメモ
そうですよね、handshake してませんでした

ということで
import sys
from javax.net.ssl import SSLSocketFactory

host = "www.google.com"
port = 443

factory = SSLSocketFactory.getDefault()
sock = factory.createSocket(host, port)
sock.startHandshake()
session = sock.getSession()
certs = session.getPeerCertificates()

for c in certs:
print '-----BEGIN CERTIFICATE-----'
print c.getEncoded().tostring().encode('base64').rstrip()
print '-----END CERTIFICATE-----'
Certificate を PEM で出したかったんですが
DER を array で取り出す方法しか分からなかったので
Jython str にして encode してしまいました
てゆーか array なんてあったんだー使ったことないよー

でどうなるかというと
$ jython get_certs.jy www.google.com
-----BEGIN CERTIFICATE-----
MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBMMQswCQYDVQQG
EwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMNVGhh
d3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0xMTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVT
MRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApH
b29nbGUgSW5jMRcwFQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jNgtXj9xVo
RaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L05vuuWciKh0R73mkszeK
9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAMBgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0w
K6ApoCeGJWh0dHA6Ly9jcmwudGhhd3RlLmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYI
KwYBBQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzAB
hhZodHRwOi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0ZS5j
b20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUFAAOBgQCfQ89bxFAp
sb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5u2ONgJd8IyAPkU0Wueru9G2Jysa9
zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqA
ibAxWEEHXw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIzCCAoygAwIBAgIEMAAAAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJVUzEXMBUGA1UE
ChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNTEzMDAwMDAwWhcNMTQwNTEyMjM1OTU5WjBMMQswCQYD
VQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMN
VGhhd3RlIFNHQyBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1NNn0I0Vf67NMf59HZGh
PwtxPKzMyGT7Y/wySweUvW+Aui/hBJPAM/wJMyPpC3QrccQDxtLN4i/1CWPN/0ilAL/g5/OIty0y
3pg25gqtAHvEZEo7hHUD8nCSfQ5i9SGraTaEMXWQ+L/HbIgbBpV8yeWo3nWhLHpo39XKHIdYYBkC
AwEAAaOB/jCB+zASBgNVHRMBAf8ECDAGAQH/AgEAMAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEE
BAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFByaXZhdGVMYWJlbDMtMTUwMQYDVR0fBCow
KDAmoCSgIoYgaHR0cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwMgYIKwYBBQUHAQEEJjAk
MCIGCCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMDQGA1UdJQQtMCsGCCsGAQUFBwMB
BggrBgEFBQcDAgYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEBBQUAA4GBAFWsY+re
od3SkF+fC852vhNRj5PZBSvIG3dLrWlQoe7e3P3bB+noOZTcq3J5Lwa/q4FwxKjt6lM07e8eU9kG
x1Yr0Vz00YqOtCuxN5BICEIlxT6Ky3/rbwTRbcV0oveifHtgPHfNDs5IAn8BL7abN+AqKjbc1YXW
rOU/VG+WHgWv
-----END CERTIFICATE-----
$
ありゃ、1行が76文字に...

まぁいいや、Java で証明書が取れました

2010/04/05

openssl 接続 bio

SSL で証明書を取ってくるシリーズ
今回は何と C with OpenSSL
C なんて学部の頃に「独習 C」を友達に薦められ
読んだというか眺めた憶えはありますがそれっきりで
一生のうちで使うことは無かろうかと思っていたのですが

で、OpenSSL の各種 binding は散々いじってるので
  1. socket 用意する
  2. SSL Centext 用意する
  3. socket と Context で SSL 作る
  4. 繋ぎにいって証明書もらう
というのは分かっちゃいるんですが
そもそも C で socket 叩いたことないよボク!!

というわけでググるわけです
良い sample code でも無いものかと探してみました

OpenSSLライブラリを使ってプログラミング(1) - 再帰の反復
このページは OpenSSL や SSL に関する詳しいまとめが
Geekなぺーじ : getaddrinfo(単純な例)
getaddrinfo の使い方とか socket の張り方とか

で、ついでなんで getopt とか構造体のこともググりながら
どうにかとりあえず動くものは作れました。
でもあれだからね、素人が作ったからどこに穴があるか分からないからね。
#include <stdio.h>
#include <getopt.h>
#include <sys/socket.h>
#include <netdb.h>
#include <openssl/ssl.h>

typedef struct {
unsigned char *data;
int length;
} der_data;

char* x509_as_pem(X509 *x509) {
BIO *bio;
BUF_MEM *bptr;

bio = BIO_new(BIO_s_mem());
PEM_write_bio_X509(bio, x509);
bptr = BUF_MEM_new();
BIO_get_mem_ptr(bio, &bptr);

return bptr->data;
}

der_data* x509_as_der(X509 *x509) {
int len;
int i;
unsigned char *buf, *p;
der_data *data;

len = i2d_X509(x509, NULL);
buf = OPENSSL_malloc(len);
if (buf == NULL) {
printf("error\n");
exit(1);
}
p = buf;
i2d_X509(x509, &p);

data = (der_data *)malloc( sizeof(der_data) );
data->data = buf;
data->length = len;
return data;
}

int main(int argc, char *argv[]) {

int der = 0;
int c, s;
char *cmd = argv[0];

struct hostent *host;
char *service = "443";
struct addrinfo hints;
struct addrinfo *result;
int fd;

SSL_CTX *ctx;
SSL *ssl;
int ret;
X509 *x509;
BIO *bio;

void help() {
fprintf(stderr, "usage: %s [-d] hostname\n", cmd);
exit(1);
}

while ((c = getopt(argc, argv, ":d")) != -1) {
switch (c) {
case 'd':
der = 1;
break;
case '?':
help();
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
help();
}

memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((s = getaddrinfo(argv[0], service, &hints, &result)) != 0) {
printf("error %d : %s\n", s, gai_strerror(s));
exit(1);
}

fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (fd < 0) {
perror("open socket error");
exit(1);
}

if (connect(fd, result->ai_addr, result->ai_addrlen) != 0) {
perror("connection error");
return 1;
}

free(result);

SSL_load_error_strings();
SSL_library_init();

ctx = SSL_CTX_new(SSLv23_method());
if ( ctx == NULL ){
ERR_print_errors_fp(stderr);
exit(1);
}

ssl = SSL_new(ctx);
if ( ssl == NULL ){
ERR_print_errors_fp(stderr);
exit(1);
}

ret = SSL_set_fd(ssl, fd);
if ( ret == 0 ){
ERR_print_errors_fp(stderr);
exit(1);
}

ret = SSL_connect(ssl);
if ( ret != 1 ){
ERR_print_errors_fp(stderr);
exit(1);
}

x509 = SSL_get_peer_certificate(ssl);
if (x509 == NULL) {
ERR_print_errors_fp(stderr);
exit(1);
}

if (der == 0) {
char *data;
data = x509_as_pem(x509);
printf("%s", data);
} else {
der_data *data;
int i;

data = x509_as_der(x509);
for (i=0; i < data->length; i++) {
printf("%c", (data->data)[i]);
}
}

return 0;
}
セキュリティーとか何も考えてないというか
セキュリティーの考え方を知らないので
それは誰か教えてくれると嬉しいヨ!!

で、
$ gcc -lssl -o get_cert get_cert.c 
$ ./get_cert www.google.com
-----BEGIN CERTIFICATE-----
MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM
MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x
MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw
FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN
gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L
05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM
BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl
LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF
BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw
Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0
ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF
AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5
u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6
z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==
-----END CERTIFICATE-----
$ ./get_cert -d www.google.com | od -t x1
0000000 30 82 03 21 30 82 02 8a a0 03 02 01 02 02 10 2f
0000020 df bc f6 ae 91 52 6d 0f 9a a3 df 40 34 3e 9a 30
0000040 0d 06 09 2a 86 48 86 f7 0d 01 01 05 05 00 30 4c
0000060 31 0b 30 09 06 03 55 04 06 13 02 5a 41 31 25 30
0000100 23 06 03 55 04 0a 13 1c 54 68 61 77 74 65 20 43
0000120 6f 6e 73 75 6c 74 69 6e 67 20 28 50 74 79 29 20
0000140 4c 74 64 2e 31 16 30 14 06 03 55 04 03 13 0d 54
0000160 68 61 77 74 65 20 53 47 43 20 43 41 30 1e 17 0d
0000200 30 39 31 32 31 38 30 30 30 30 30 30 5a 17 0d 31
0000220 31 31 32 31 38 32 33 35 39 35 39 5a 30 68 31 0b
0000240 30 09 06 03 55 04 06 13 02 55 53 31 13 30 11 06
0000260 03 55 04 08 13 0a 43 61 6c 69 66 6f 72 6e 69 61
0000300 31 16 30 14 06 03 55 04 07 14 0d 4d 6f 75 6e 74
0000320 61 69 6e 20 56 69 65 77 31 13 30 11 06 03 55 04
0000340 0a 14 0a 47 6f 6f 67 6c 65 20 49 6e 63 31 17 30
0000360 15 06 03 55 04 03 14 0e 77 77 77 2e 67 6f 6f 67
0000400 6c 65 2e 63 6f 6d 30 81 9f 30 0d 06 09 2a 86 48
0000420 86 f7 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02
0000440 81 81 00 e8 f9 86 0f 90 fa 86 d7 df bd 72 26 b6
0000460 d7 44 02 83 78 73 d9 02 28 ef 88 45 39 fb 10 e8
0000500 7c ae a9 38 d5 75 c6 38 eb 0a 15 07 9b 83 e8 cd
0000520 82 d5 e3 f7 15 68 45 a1 0b 19 85 bc e2 ef 84 e7
0000540 dd f2 d7 b8 98 c2 a1 bb b5 c1 51 df d4 83 02 a7
0000560 3d 06 42 5b e1 22 c3 de 6b 85 5f 1c d6 da 4e 8b
0000600 d3 9b ee b9 67 22 2a 1d 11 ef 79 a4 b3 37 8a f4
0000620 fe 18 fd bc f9 46 23 50 97 f3 ac fc 24 46 2b 5c
0000640 3b b7 45 02 03 01 00 01 a3 81 e7 30 81 e4 30 0c
0000660 06 03 55 1d 13 01 01 ff 04 02 30 00 30 36 06 03
0000700 55 1d 1f 04 2f 30 2d 30 2b a0 29 a0 27 86 25 68
0000720 74 74 70 3a 2f 2f 63 72 6c 2e 74 68 61 77 74 65
0000740 2e 63 6f 6d 2f 54 68 61 77 74 65 53 47 43 43 41
0000760 2e 63 72 6c 30 28 06 03 55 1d 25 04 21 30 1f 06
0001000 08 2b 06 01 05 05 07 03 01 06 08 2b 06 01 05 05
0001020 07 03 02 06 09 60 86 48 01 86 f8 42 04 01 30 72
0001040 06 08 2b 06 01 05 05 07 01 01 04 66 30 64 30 22
0001060 06 08 2b 06 01 05 05 07 30 01 86 16 68 74 74 70
0001100 3a 2f 2f 6f 63 73 70 2e 74 68 61 77 74 65 2e 63
0001120 6f 6d 30 3e 06 08 2b 06 01 05 05 07 30 02 86 32
0001140 68 74 74 70 3a 2f 2f 77 77 77 2e 74 68 61 77 74
0001160 65 2e 63 6f 6d 2f 72 65 70 6f 73 69 74 6f 72 79
0001200 2f 54 68 61 77 74 65 5f 53 47 43 5f 43 41 2e 63
0001220 72 74 30 0d 06 09 2a 86 48 86 f7 0d 01 01 05 05
0001240 00 03 81 81 00 9f 43 cf 5b c4 50 29 b1 bf e2 b0
0001260 9a ff 6a 21 1d 2d 12 c3 2c 4e 5a f9 12 e2 ce b9
0001300 82 52 2d e7 1d 7e 1a 76 96 90 79 d1 24 52 38 79
0001320 bb 63 8d 80 97 7c 23 20 0f 91 4d 16 b9 ea ee f4
0001340 6d 89 ca c6 bd cc 24 68 d6 43 5b ce 2a 58 bf 3c
0001360 18 e0 e0 3c 62 cf 96 02 2d 28 47 50 34 e1 27 ba
0001400 cf 99 d1 50 ff 29 25 c0 36 36 15 33 52 70 be 31
0001420 8f 9f e8 7f e7 11 0c 8d bf 84 a0 42 1a 80 89 b0
0001440 31 58 41 07 5f
0001445
$
という感じに der とか pem とかで取れるようになりました。

openssl s_client の劣化版じゃん!!
や、そうなんですけどね。

ocaml ssl

SSL で繋いで証明書を取ってこようシリーズ
今回は OCaml でいってみようと思います
OCaml に関してはプログラミング in OCaml など見ると良いのかも

とりあえずググりますと
http://sourceforge.net/projects/savonet/files/ocaml-ssl/
こちらに OpenSSL の binding があったのでこれを使おうかと思いまして
っていうかね、
OCamlで簡易SSLクライアント - komamitsu.log
こんな素敵なまとめページが見つかってしまいました

OCaml-SSL をインストールしようとすると
「findlib が無いよ」
って言われたので findlib から入れました
で、使い方は上記ページを参考にしつつ試して動いた感じです

OCaml-SSL のソースをザザっと眺めてみたんですが
証明書をいじる関数があまり用意されていないらしく
というか subject と issuer を抜くものしかありませんでした
あらら

とりあえず minimal かと思われる証明書取ってくるのがこれ
open Unix;;

Ssl.init ();;
let hostent = gethostbyname "www.google.com";;
let addr = hostent.h_addr_list.(0);;
let sockaddr = ADDR_INET(addr, 443);;

let sock = Ssl.open_connection Ssl.SSLv23 (ADDR_INET(addr, 443));;
let cert = Ssl.get_certificate sock;;
Printf.printf "%s%!" (Ssl.get_subject cert)
っていうか何というコード
変数に欲しいもの列記して実行してるだけです
って OpenSSL をいじってるわけだから仕方ないのかな?

でまぁこんなのを show_subject.ml として用意して
ocamlfind c -package ssl -thread -linkpkg -o show_subject show_subject.ml
と打つとコマンドができました

findlib っていうのがあるのだというのを知ったのが
今回の最大の成果となっております

2010/02/13

python epoll

「イベント〜?」「ドリブ〜ン!」

なんか、すっかり放置していました
私は GUI でチャットなどを作るはずだったのです
しかし全く着手せぬまま現在に至っています、ごめんなさい

そこで、少しだけ epoll 触っとこうと思うことにして調べました
How To Use Linux epoll with Python
何とも丁寧な説明がありました

とりあえず
  • select.epoll.register で file descriptor と何を監視するかを登録
  • select.epoll.poll で active なやつをとってくる
ってのだけ把握

というわけで、少し試してみることに
import socket
import select
from OpenSSL.SSL import Connection, Context, SSLv23_METHOD

fqdn = 'www.google.com'

s = socket.socket()
s.settimeout(0)

epoll = select.epoll()
epoll.register(s.fileno(), 2**11-1)

print epoll.poll(1)
s.connect_ex((fqdn, 443))
print "connect"
print epoll.poll(1)
ctx = Context(SSLv23_METHOD)
print "make Context"
print epoll.poll(1)
c = Connection(ctx, s)
print "make Connection"
print epoll.poll(1)
c.set_connect_state()
print "set connect state"
print epoll.poll(1)
c.do_handshake()
print "do handshake"
print epoll.poll(1)
x509 = c.get_peer_certificate()
print x509.get_subject()

で、結果がこちら
$ python test1.py 
[(3, 276)]
connect
[(3, 260)]
make Context
[(3, 260)]
make Connection
[(3, 260)]
set connect state
[(3, 260)]
Traceback (most recent call last):
File "test1.py", line 26, in <module>
c.do_handshake()
OpenSSL.SSL.WantReadError
non-blocking のまま handshake しようとして怒られました

select.EPOLLOUT = 4, select.EPOLLHUP = 16, select.EPOLLWRNORM = 256
みたいなんですけど、まぁ、OUT だけ見りゃいいのかな、この場合

教訓:「OpenSSL の handshake を呼ぶんなら blocking にしよう」
え、そこなの?

2010/02/05

erlang ssl_esock

HTTPS サーバーから証明書を取ってこようシリーズ
今回は Erlang です

Erlang の SSL な module はこちら
Erlang -- SSL Reference Manual
ざっと見ると getpeercert という関数があるのでこれっぽいです

左のコラムに ssl と ssl_new というのがありますが、これは
以前は SSL 関連でも全て OpenSSL を利用していたようで
connection を張ろうとしたりすると ssl_esock という subprocess が立ち
そいつに色々と仕事を任せていたようです
でも Erlang 的にはそこに C が入ってしまうので嬉しくありません

そこで cryptographic primitive な部分だけ OpenSSL を使って
connection の部分なんかを Erlang で実装したのが ssl_new のようです
cryptographic primitive は crypto って application を使います

で、application とか良く分かってないんですが
とりあえず start とかしといたらいいんでしょ、というのが次
-module(get_cert).
-compile(export_all).

get_cert(Host) ->
application:start(crypto),
application:start(ssl),
{ok, Sock} = ssl:connect(Host, 443, [{ssl_imp, new}]),
{ok, Der} = ssl:peercert(Sock),
{ok, Cert} = public_key:pkix_decode_cert(Der, otp),
ssl:close(Sock),
application:stop(ssl),
application:stop(crypto),
Cert.
ssl:connect の option に ssl_new を使うと伝え
ssl:peercert で証明書を DER で取り
public_key:pkix_decode_cert でそれを parse しています
otp ってあるのは parse の形式で、あと plain があります
それぞれ
-include_lib("public_key/include/public_key.hrl").
-include_lib("public_key/include/OTP-PUB-KEY.hrl").
的なことをしておけば読み易いです
かんたんなことしかしていませんね

で、動かしてみると
$ erl
Erlang R13B03 (erts-5.7.4) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.4 (abort with ^G)
1> get_cert:get_cert("blogger.com").

=INFO REPORT==== 15-Feb-2010::01:34:14 ===
application: crypto
exited: stopped
type: temporary

=INFO REPORT==== 15-Feb-2010::01:34:14 ===
application: ssl
exited: stopped
type: temporary
{'OTPCertificate',
{'OTPTBSCertificate',v3,134060097170805708820381,
{'SignatureAlgorithm',{1,2,840,113549,1,1,5},'NULL'},
{rdnSequence,
[[{'AttributeTypeAndValue',{2,5,4,6},"US"}],
[{'AttributeTypeAndValue',
{2,5,4,10},
{printableString,"Google Inc"}}],
[{'AttributeTypeAndValue',
{2,5,4,3},
{printableString,"Google Internet Authority"}}]]},
{'Validity',
{utcTime,"091222015844Z"},
{utcTime,"101222020844Z"}},
{rdnSequence,
[[{'AttributeTypeAndValue',{2,5,4,6},"US"}],
[{'AttributeTypeAndValue',
{2,5,4,8},
{printableString,"California"}}],
[{'AttributeTypeAndValue',
{2,5,4,7},
{printableString,"Mountain View"}}],
[{'AttributeTypeAndValue',
{2,5,4,10},
{printableString,"Google Inc"}}],
[{'AttributeTypeAndValue',
{2,5,4,3},
{teletexString,"*.blogger.com"}}]]},
(あと長いんで略)
こんな感じで X.509 を parse してくれてます

ちなみに public_key の Reference は
Erlang -- public_key
にあって
例えば pem_to_der ってのを使えば PEM の PATH 与えて
DER で返してくれるなんてのも分かります

2009/08/21

"Required SSLCacheServerPath missing"

Apache-SSL を忘れてました

apache 1.3 のソースと、それに合わせて apache-ssl のソースを取ってきます
バージョンを合わせましょう
で、apache のソースを展開して移動して apache-ssl をその場で展開します
中に FixPatch ってスクリプトがあるんで ./FixPatch ってして実行
patch あてるか聞かれるんで y って答えてあげます
で、あとは configure して make して make install
src に移動して make certificate ってするとオレオレ証明書も作ってくれます
apache_1.3.?/SSLconf/conf/httpsd.pem に証明書と鍵が入ってます

で、apache_1.3.?/SSLconf/conf/httpd.conf が設定サンプル
ココに SSLCacheServerPath とか設定してあるんですが
インストールした方の conf/httpsd.conf には記述が無いので
まんまだと起動できなくって "Required SSLCacheServerPath missing" って出ます
で、httpd.conf に SSLCacheServerPath はあるんですが
path が変なとこ向いてるんで bin/gcache を指してあげ
更に証明書と秘密鍵のパスも設定してあげると起動します

tar xfz apache_1.3.41.tar.gz 
cd apache_1.3.41
tar xfz ../apache_1.3.41+ssl_1.60.tar.gz
./FixPatch
./configure
make
make install
cd src/
make certificate
cd ../SSLconf/conf/
cp http* /usr/local/apache/conf
cd /usr/local/apache
(conf の中身をいろいろいじる)
./bin/httpsdctl start
SSL 部分の設定 include したら動くようになってると素敵だな
って思いました
User とか Group とか再定義できるんですかね、Log も
上書き??

apache-1.3 mod_ssl

復習大切ですよね、というわけで apache のこと復習してました

apache-1.3 の場合 mod_ssl ってのは本体とは別で配布されてます
本体も mod_ssl も展開して
./configure --with-apache=../apache-1.3.41
的なことをして設定
更に本体の方に移動して
./configure && make && make certificate && make install
的なインストールをします
設定ファイルは conf/httpd.conf に入ってまして
でも Listen するのが 8080 とか 8443 とかになってました
make certificate するときにオレオレ作ったんでそれが使われます
で、startssl

apache-2.0 の場合は本体に入っているので
./configure --enable-ssl
的なことをして設定
設定ファイルには 443 ポートでの設定が入ってる
設定ファイルは conf/ssl.conf が conf/httpd.conf から Include されてるので編集
証明書は作ってくれないのかな??

apache-2.3 の場合も本体に入ってる
設定ファイルは conf/extra/httpd-ssl.conf になってて
conf/httpd.conf からの Include は comment out されてます

にしても、apache 大きかったです

2009/08/07

openssl rsa "public_key"

また Ruby で CSR ですか、と思われても仕方無いのですが
今回は CSR を作りたいなというお話でして
色々ググったりして
RubyOpenSSLDigitalSignatureSample - Hironobu's Tips List
ここで秘密鍵のこと習ったり
Ruby のソース眺めたりして
結果こんな感じになりました

require 'openssl'
include OpenSSL

# RSA key pair を作る、引数は鍵長と exponent
pkey = PKey::RSA.new(1024, 65537)

# Subject 用にお名前を用意する
# oid と名前の pair を沢山渡す
# oid はどれ? という場合は http://www.alvestrand.no/objectid/2.5.4.html
# new でも渡せるし追加もできる
subject = X509::Name.new([['2.5.4.6', 'JP'], ['2.5.4.8', 'Tokyo']])
subject.add_entry('2.5.4.7', 'Chiyoda-ku')
subject.add_entry('2.5.4.10', 'example')
subject.add_entry('2.5.4.3', "www.example.com")
# subject = "/C=JP/ST=Tokyo/L=Chiyoda-ku/O=example/CN=www.example.com"

# 空の CSR を作って秘密鍵と Subject を登録
req = X509::Request.new()
req.public_key = pkey
req.subject = subject

# 最後に署名して終わり、鍵と hash 関数を与える
req.sign(pkey, OpenSSL::Digest::SHA1.new)

puts req.to_text
#Certificate Request:
# Data:
# Version: 0 (0x0)
# Subject: C=JP, ST=Tokyo, L=Chiyoda-ku, O=example, CN=www.example.com
# Subject Public Key Info:
# Public Key Algorithm: rsaEncryption
# RSA Public Key: (1024 bit)
# Modulus (1024 bit):
# 00:c3:a5:c1:63:63:0d:79:0e:94:e2:82:4f:97:97:
# 07:9a:26:86:8c:27:14:ef:99:ed:9e:9e:46:03:10:
# 9e:e9:6a:af:69:8a:11:47:29:55:c5:68:59:14:70:
# ce:6a:91:34:9a:59:98:1e:59:09:a3:f5:30:69:b6:
# bf:09:c2:24:5c:96:16:81:ef:4e:ae:51:70:0f:14:
# 03:9e:17:18:9c:c1:2c:c1:cb:5d:7f:9e:1b:4a:0d:
# 92:11:47:42:cc:71:23:54:0d:60:ec:79:4d:27:20:
# dd:f0:1f:31:06:c6:88:86:31:d5:a1:ba:91:5a:bb:
# 08:a5:29:4e:89:99:35:58:cd
# Exponent: 65537 (0x10001)
# Attributes:
# a0:00
# Signature Algorithm: sha1WithRSAEncryption
# 8b:df:fb:0f:5c:33:56:22:06:c7:1d:8d:df:a0:d3:fc:ea:ea:
# ff:bd:69:12:b5:f5:56:b2:9a:e0:38:2c:57:0e:99:33:97:93:
# 66:50:b4:68:d6:a4:9d:dc:71:77:48:1a:4c:a5:41:6a:33:29:
# 98:5a:ae:75:61:1f:0e:38:42:ab:29:e3:41:dd:bb:88:bf:d1:
# 27:2e:8e:91:c7:a8:a8:4a:f8:7c:dd:86:d5:48:94:19:a1:c0:
# 8a:bb:94:c2:c3:7b:50:7c:a2:9d:d3:45:45:59:cd:5c:d9:b6:
# a0:a6:db:13:32:75:27:3d:b6:84:a1:bb:30:6c:9b:8a:13:f7:
# ac:43


もうちょっとがんばって
  • Extension 書く
  • 日本語入れてみる
などあるかとは思いますが
まぁそういうのは追い追い

2009/07/26

nginx msie

別件で検索したりしてて
MSIE ってのは脳味噌腐ってるって話を思い出しました
apache の ssl.conf には
SetEnvIf User-Agent ".*MSIE.*" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
って設定がされてたりしますが
その前のところのコメントなんかに理由が説明されています

で、そうだよな、IE が悪いよなきっと、と
nginx ssl の悩みを解決すべく
気をとりなおして検索したら
nginx ssl_prefer_server_ciphers and MSIE 7.x core dump
って bug report を見付けました
あらら
設定を色々試していたはずなんだけどなぁ
何で見付けられなかったんでしょう

ssl_prefer_server_ciphers の on にしなければ
nginx で SSL してても IE と正常に接続できるようになりました
よかったよかった

2009/07/24

wildcard subjectaltname virtualhost

SSL と VirtualHost 関連で「どうなの?」的なことを問われたので
確か素敵まとめサイトがあったと思って検索したら
SSL/TLS で Namebase のバーチャルホスト
が出てきました
素敵!!
VhostTaskForce - CAcert Wiki
ここも素敵かな??

多分 Apache 用語ですけど
VirtualHost
1 つのウェブサーバで複数のウェブサイトを構築
IP address ベースと Name ベースのものがある
IP ベース VirtualHost
アクセスがあった IP address 毎に
サイト・コンテンツを指定して複数サイトがあるように見せる
IP address が複数必要なので場合によってはコストが高い
Name ベース VirtualHost
サーバーの名前毎にコンテンツを振り分ける VirtualHost
IP address が 1 つしかなくても複数のサイトが構築できる
HTTP 1.1 とかで Host というヘッダをクライアントは送るので
そこで判断してるんですよね?? きっと

で、こっから少し古い話

一方 SSL は TCP の接続の後にネゴシエーションするわけですが
ここにはまだ Apache 出てこないんで Host とかの内容見ません
SSL のネゴシエーションのときに分かるのは TCP までの情報なので
IP address とか port 番号とかです
なので、HTTPS でサーバが証明書を提示する際にどの証明書を提示するか
それを決める判断の基準も IP address とか port 番号しかありません

IP ベースの VirtualHost であれば IP address と port 番号毎に
1 つサイトが決まっているので証明書を提示してあげれば良いのですが
1 つの IP アドレスの下で Name ベースの VirtualHost をしてる場合
それら VirtualHost 達で 1 枚しか証明書が提示できません
なのでワイルドカードや SubjectAlternativeName などを利用して
1 枚で複数の FQDN に対応できる証明書が利用されています

古い話終わり

で、RFC 3546 で TLS 拡張で Server Name Indication ってのが定義されてるそうで
TLS レベルで接続したいサーバの名前を指定する仕組みが決められて
それ使えば Name ベースの VirtualHost でも問題無しに
1 Host 毎に 1 枚証明書を対応付けれて IP address もぉ要らないよ!
ということになっているみたいです

IE7 以降とか Fx 2 以降とか Opera 8 以上は対応してるみたいなんですが
携帯とかどうなんだろう??

更に、RFC 2817 では Upgrading to TLS という仕組みも定義されてるとか
HTTP 1.1 から TLS な世界に切り替わるみたいですが
ちょっと、ググっただけでは対応状況までは分かりませんでした

Server Name Indication が普及してくれると分かり易くていいですね

2009/07/13

lighttpd ssl

nginx で上手くいかないので lighttpd に逃げることにしました
nginx のことは後で考えます

(2009/07/26 削除)

ググったらやっぱり色々出てきます
Webサーバ「lighttpd」でSSLを使うには - @IT
も出るし、
Lighttpd - Docs:SSL - lighty labs
も出るし。

compile は configure に --with-openssl って付けてあげれば OK
openssl の dev っぽい package があれば OK

ssl.pemfile には base64 してある証明書と秘密鍵を繋げたものを入れときます
$ cat ssl.crt ssl.key > ssl.pem
とかして ssl.pem を指定してやれば OK、絶対パスとかで
crt と key の順番はどっちでもいいらしい

ssl.ca-file には中間証明書を指定してやります
こっちも同様で複数枚にも対応してるけど
apache や nginx と違ってサーバ証明書から階層が構築できるかどうかチェックするみたい
ただ垂れ流すだけでなく、ひと手間かけてるみたいでした

redirect したいときには 設定ファイルの頭の方で mod_redirect とか呼んどけば OK
source の中に doc/lighttpd.conf って雛形があるんでそれを使いましょう

で、多分こんなんで nginx ssl と同じなはず
$SERVER["socket"] == "192.168.0.1:80" {
url.redirect = ( "^/(.*)" => "https://sample1/" )

accesslog.filename = "/www/sample1/logs/http_access.log"
}

$SERVER["socket"] == "192.168.0.1:443" {
ssl.engine = "enable"
ssl.pemfile = "/www/sample1/etc/sample1.pem"
ssl.ca-file = "/www/sample1/etc/sample1.ca-bundle"
ssl.cipher-list = "ALL:!EXP:!ADH:!LOW:!SSLv2:!MD5"
server.document-root = "/www/sample1/httpsdocs"

accesslog.filename = "/www/sample1/logs/https_access.log"
}

$SERVER["socket"] == "192.168.0.2:443" {
ssl.engine = "enable"
ssl.pemfile = "/www/sample2/etc/sample2.pem"
ssl.ca-file = "/www/sample2/etc/sample2.ca-bundle"
ssl.cipher-list = "ALL:!EXP:!ADH:!LOW:!SSLv2:!MD5"
server.document-root = "/www/sample2/httpsdocs"

accesslog.filename = "/www/sample2/logs/https_access.log"

url.redirect = ( "^/(.*)" => "https://sample3/$1" )
}

$SERVER["socket"] == "192.168.0.2:80" {
accesslog.filename = "/www/sample2/logs/http_access.log"

url.redirect = ( "^/(.*)" => "https://sample2/$1" )
}
でもなー、nginx で上手くいかないんだよなー
なんでかなー

2009/04/25

client-hello ie firefox RSA SHA1 MD5

SSL の暗号スイートは誰が決めてるのよ? って話になって
この話するの 2 度目なはずなんだけど曖昧な記憶
なので SSL について検索したらサーバが決めてました
client hello でクライアントが使えるリストを提示して
server hello で「じゃぁこれね」ってサーバが選んでるそうです

「じゃぁ、ブラウザは何を提出してるわけ?」って知りませんよねぇ
まぁパケットキャプチャーすれば分かるしサーバいじってもいいのかと思ったら
wireshark で全て解決ということが分かってはいおしまい

というわけにもいかなかったのですが
まぁこんだけ道具揃ってれば絶対誰か調べてるはずなので
ググったらちゃんといらっしゃいました
Tatsuyoshi tech diary
やぁ、すばらしい

ブラウザによってバラバラですね
勉強になりました