Httpsサーバとクライアントの作成

1.実行環境
・Windows10にVirtualBoxの仮想マシンがインストールされている
・仮想マシンのOSはCentOS8で、Apatch(HTTPサーバ)がインストールされている
(1)HTTPサーバのバージョンの確認
# httpd -v

(2)httpサーバの起動
# systemctl start httpd
# systemctl is-active httpd

(3)ブラウザから接続
/var/www/html
のフォルダにあるindex.html
が表示される

2.HttpサーバのSSL化
(1)SSLモジュールのインストール
ApacheでSSLを利用ため必要なソフトウェア
・OpenSSL:SSLを利用するために必要なソフトウェア
・mod_ssl:OpenSSLを使ってApacheをSSLに対応させるモジュール
OpenSSLはCentOSをデフォルト設定でインストールされる。OpenSSLがインストールされているかどうか確認する。
# rpm -qa | grep openssl

インストールされていないときは、下記のコマンドでインストールする。
# yum install openssl
バージョンの確認
# openssl version

CentOS8が2021年末にサポート切れでミラーサイトが停止され、パッケージのインストールに失敗した。

(対策)
 下記のファイルで、参照先のリポジトリをミラーサイトからhttp://vault.centos.orgに変更する。
/etc/yum.repos.d/CentOS*repo
①CentOS-Linux-AppStream.repo
②CentOS-Linux-BaseOS.repo

変更前:
[appstream]
name=CentOS Linux $releasever – AppStream
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
#baseurl=http://mirror.centos.org/$contentdir/$releasever/AppStream/$basearch/os/


mirrorlist=・・・をコメント化し、#baseurl=http://mirror.centos.org/・・・のコメントを外し、”mirror”を”vault”に変更
変更後:
[appstream]
name=CentOS Linux $releasever – AppStream
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
baseurl=http://vault.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
参照先のリポジトリを変更したあと再度、下記コマンドでインストールする
# yum install mod_ssl

下記コマンドでインストールされたことを確認する
# rpm -qa | grep mod_ssl

※rpmコマンド
オプション-q:問い合わせ(パッケージ情報の表示と検索)
   -a:情報問い合わせ関連(-q、-Vと一緒に使用するオプション)でall

(4)秘密鍵とサーバ証明書の作成
 ・秘密鍵の作成
 # openssl genrsa > server.key

・サーバ証明書の作成
CSRファイル(認証局にサーバ証明書を発行してもらう時に使用するファイル)を作成
# openssl req -new -key server.key > server.csr
下記はEnterでテストでは入力無しとした

Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server’s hostname) []:
Email Address []:
Please enter the following ‘extra’ attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
・サーバ証明書への署名
 本来はこのCSRファイルを認証局に渡して署名してもらう必要があるが、ここではテストのため、自分で署名するため、以下のコマンドを実行。
# openssl x509 -req -signkey server.key < server.csr > server.crt

(5)秘密鍵とサーバ証明書の配置
作成した鍵と証明書を/etc/httpd/以下に配置する。

ここでは
 /etc/httpd/conf/ssl.keyおよび/etc/httpd/conf/ssl.crt配下に置く
# mkdir /etc/httpd/conf/ssl.key
# mkdir /etc/httpd/conf/ssl.crt
# mv server.key /etc/httpd/conf/ssl.key/
# mv server.crt /etc/httpd/conf/ssl.crt/

(6)SSLモジュールの設定
/etc/httpd/conf.d/ssl.confの中に、配置した鍵と証明書のパスを設定する。
# vi /etc/httpd/conf.d/ssl.conf

#SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateFile /etc/httpd/conf/ssl.crt/server.crt

#SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
SSLCertificateKeyFile /etc/httpd/conf/ssl.key/server.key

(7)Apacheを再起動して接続する
# systemctl restart httpd
# systemctl is-active httpd
ssl.confを修正したあと、起動に失敗する

SELinuxが影響していると考えられるため、テストではSELinuxを無効とする
現在
[root@localhost conf.d]#  getenforce
Enforcing
一時的にpermissiveモードに変更
[root@localhost conf.d]#  getenforce
Permissive
上記の変更をしてhttpサーバが起動できた
[root@localhost conf.d]# systemctl restart httpd
[root@localhost conf.d]# systemctl is-active httpd
active

(8)ブラウザからHttpsサーバに接続
 CentOSのディスクトップのブラウザから接続
https://localhost

サーバから取得したサーバ証明書では、ルート証明書との正当性が確認できないため、エラーになったと考えられる。
「詳細情報」を選択して、次に進み、強制的に許可する。

また、ホストPC(Windows10)のブラウザからアクセスしたとき
http://localhostでは接続できるが、https://localhostでは接続できない

コマンドプロンプトからcurlコマンドで、SSL接続で証明書エラーをスキップして接続することで、httpsサーバからのレスポンスが正常に処理される。
# curl -k https://localhost

3.Httpsクライアントの作成
(1)openssl-devel パッケージのインストール
 openssl-devel は各種の暗号化アルゴリズムとプロトコルをサポートしたアプリケーションを開発するために必要な静的ライブラリとヘッダーファイルが含まれている。
# yum install openssl-devel

(2)クライアントのソースファイル
client.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

int main(void) {
    int mysocket;
    struct sockaddr_in server;// ホストのIPアドレスなど情報を保持する構造体
    struct addrinfo hints, *res;

    SSL *ssl;
    SSL_CTX *ctx;

    char msg[100];

    char *host = "localhost";
    char *path = "/";
    int   port = 443;

    // IPアドレスの解決
    memset(&hints, 0, sizeof(hints));
    hints.ai_family   = AF_INET;//インターネットプロトコル
    hints.ai_socktype = SOCK_STREAM;
    char *service = "https";

    int err = 0;
    if ((err = getaddrinfo(host, service, &hints, &res)) != 0) {
        fprintf(stderr, "Fail to resolve ip address - %d\n", err);
        return EXIT_FAILURE;
    }

 //ソケット
    if ((mysocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
        fprintf(stderr, "Fail to create a socket.\n");
        return EXIT_FAILURE;
    }

  //コネクト
    if (connect(mysocket, res->ai_addr, res->ai_addrlen) != 0) {
        printf("Connection error.\n");
        return EXIT_FAILURE;
    }

 //SSLセットアップ
    SSL_load_error_strings();/OpenSSLのエラー文の読み込み

    SSL_library_init();//SSLの初期化

 //SSL_CTX構造体生成
    ctx = SSL_CTX_new(SSLv23_client_method()); //引数は使用するプロトコルの種類

//SSLの構造体生成
    ssl = SSL_new(ctx);
    err = SSL_set_fd(ssl, mysocket);//SSL構造体とソケットの関連付け
    SSL_connect(ssl);/サーバとハンドシェイクを行う
    printf("Conntect to %s\n", host);

 //HTTPリクエストの送信
    sprintf(msg, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", path, host);

    SSL_write(ssl, msg, strlen(msg));

    int buf_size = 256;
    char buf[buf_size];
    int read_size;
//レスポンスの受信
    do {
        read_size = SSL_read(ssl, buf, buf_size);
        write(1, buf, read_size);
    } while(read_size > 0);
//SSLとソケットの終了処理
    SSL_shutdown(ssl);
    SSL_free(ssl);
    SSL_CTX_free(ctx);
    ERR_free_strings();

    close(mysocket);

    return EXIT_SUCCESS;
}

(3)クライアントソースファイルのコンパイル
 # gcc client.c -o client -lssl -lcrypto

(4)httpsサーバの起動
 # setenforce 0
 # systemctl start httpd

(5)httpsクライアントの起動
 # ./client

Conntect to localhost
HTTP/1.1 200 OK
Date: Sun, 22 May 2022 00:50:46 GMT
Server: Apache/2.4.37 (centos) OpenSSL/1.1.1k
Last-Modified: Sun, 08 May 2022 01:59:00 GMT
ETag: "c3-5de766f65c100"
Accept-Ranges: bytes
Content-Length: 195
Connection: close
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>画像テスト</title>
  </head>
  <body>
    <p>画像のテスト</p>
    <img src="test.jpg">
  </body>
</html>

httpサーバにsslで接続できて、レスポンスのhtmlファイルが返却された

The end