Last active
July 10, 2025 18:37
-
-
Save masakielastic/b7a13d6316409a671c9cef13b677a44d to your computer and use it in GitHub Desktop.
stream_socket_server で TLS 対応の HTTP/1 サーバー
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// 1. 秘密鍵と CSR(証明書署名要求)のオプションを設定 | |
$config = [ | |
"config" => "/etc/ssl/openssl.cnf", // 必要に応じて openssl.cnf へのパスを調整 | |
"private_key_bits" => 2048, // 鍵長 | |
"private_key_type" => OPENSSL_KEYTYPE_RSA, // 鍵タイプ | |
]; | |
// 2. 秘密鍵の生成 | |
$privateKey = openssl_pkey_new($config); | |
if ($privateKey === false) { | |
die("秘密鍵の生成に失敗: " . openssl_error_string()); | |
} | |
// 3. CSR の生成(ここでは DN 情報を簡易に指定) | |
$dn = [ | |
"countryName" => "JP", | |
"stateOrProvinceName" => "Tokyo", | |
"localityName" => "Chiyoda-ku", | |
"organizationName" => "Example Co., Ltd.", | |
"organizationalUnitName" => "IT", | |
"commonName" => "example.local", | |
"emailAddress" => "[email protected]", | |
]; | |
$csr = openssl_csr_new($dn, $privateKey, $config); | |
if ($csr === false) { | |
die("CSR の生成に失敗: " . openssl_error_string()); | |
} | |
// 4. 自己署名証明書の生成(有効期間365日) | |
$serial = random_int(1, PHP_INT_MAX); | |
$validDays = 365; | |
$x509 = openssl_csr_sign($csr, null, $privateKey, $validDays, $config, $serial); | |
if ($x509 === false) { | |
die("証明書の署名に失敗: " . openssl_error_string()); | |
} | |
// 5. 生成した秘密鍵・証明書を PEM 形式で文字列に出力 | |
openssl_pkey_export($privateKey, $privateKeyPem); | |
openssl_x509_export($x509, $certPem); | |
// 6. ファイルへの保存 | |
file_put_contents('key.pem', $privateKeyPem); | |
file_put_contents('cert.pem', $certPem); | |
echo "自己署名証明書と秘密鍵を生成しました。\n"; | |
echo " - key.pem\n"; | |
echo " - cert.pem\n"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// SSL コンテキストの設定 | |
$context = stream_context_create([ | |
'ssl' => [ | |
'local_cert' => 'cert.pem', // サーバ証明書+中間証明書 | |
'local_pk' => 'vkey.pem', // サーバ秘密鍵 | |
'allow_self_signed' => true, // テスト用に自己署名証明書を許可 | |
'verify_peer' => false, // クライアント証明書検証なし | |
'verify_peer_name' => false, | |
], | |
]); | |
// TLS:// を使ってポート 8443 で待ち受け | |
$server = stream_socket_server( | |
'tls://0.0.0.0:8443', | |
$errno, | |
$errstr, | |
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, | |
$context | |
); | |
if (!$server) { | |
die("サーバの起動に失敗: $errstr ($errno)\n"); | |
} | |
echo "Listening on https://0.0.0.0:8443\n"; | |
while ($client = @stream_socket_accept($server, -1)) { | |
// リクエストを読み込む | |
$request = ''; | |
while (!feof($client) && strpos($request, "\r\n\r\n") === false) { | |
$request .= fgets($client, 1024); | |
} | |
// 簡易にリクエスト行だけを取得 | |
if (preg_match('#^(GET|POST)\s+([^\s]+)\s+HTTP/1\.[01]#', $request, $m)) { | |
list(, $method, $path) = $m; | |
// ログ表示 | |
echo "[" . date('Y-m-d H:i:s') . "] $method $path\n"; | |
} | |
// レスポンスを送信 | |
$body = "<html><body><h1>こんにちは、世界!</h1><p>あなたは {$method} {$path} をリクエストしました。</p></body></html>"; | |
$response = "HTTP/1.1 200 OK\r\n"; | |
$response .= "Content-Type: text/html; charset=UTF-8\r\n"; | |
$response .= "Content-Length: " . strlen($body) . "\r\n"; | |
$response .= "Connection: close\r\n"; | |
$response .= "\r\n"; | |
$response .= $body; | |
fwrite($client, $response); | |
fclose($client); | |
} | |
fclose($server); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment