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

2025年10月9日木曜日

Ruby OpenSSL::SSL::SSLError - SSL_connect returned=1 errno=0 対策

Ruby OpenSSL::SSL::SSLError - SSL_connect returned=1 errno=0 対策

概要

macOS + openssl@3.6.0 で発生します
回避策を紹介します

エラー全文

2025-10-09 10:48:58 - OpenSSL::SSL::SSLError - SSL_connect returned=1 errno=0 peeraddr=[2606:4700::6810:190e]:443 state=error: certificate verify failed (unable to get certificate CRL) (OpenSSL::SSL::SSLError):

環境

  • macOS 15.7.1
  • Ruby 3.4.4
  • openssl 3.6.0

回避策

  • vim libs/ssl_patch.rb
# frozen_string_literal: true

require 'openssl'

# 開発環境限定で SSL 検証をスキップする
if ENV['MODE'] == 'dev'
  s = OpenSSL::X509::Store.new.tap(&:set_default_paths)
  OpenSSL::SSL::SSLContext.send(:remove_const, :DEFAULT_CERT_STORE)
  OpenSSL::SSL::SSLContext.const_set(:DEFAULT_CERT_STORE, s.freeze)
end

あとはメインなどの先頭で

require './libs/ssl_patch'

パッチなので可能な限り先に読み込むようにしてください

最後に

SSL 関係はいろいろとハックしなければいけないケースが多いので辛いです

参考サイト

2024年2月21日水曜日

Python の requests で sslv3 alert handshake failure

Python の requests で sslv3 alert handshake failure

概要

クライアント側の Python や OpenSSL は最新でサーバ側の SSL の設定が古い場合の対処方法を紹介します

環境

  • Python 3.11.3
    • requests 2.31.0
  • OpenSSL 3.0.2 15

エラー詳細

以下のような感じで requests で接続時にハンドシェイクエラーが発生します

requests.exceptions.SSLError: HTTPSConnectionPool(host='xxx.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:997)')))

おそらく原因は requests が最新の ciphers を使用して接続しに行っておりサーバ側がその ciphers に対応しておらずエラーとなっています

対処方法

requests で chipers を指定することができるのでそれを使います
Adapter という機能を使います

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.ssl_ import create_urllib3_context  # type: ignore

# ここはサーバ側が対応している ciphers を指定すること
CIPHERS = "AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA"


class RsaAesShaAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        context = create_urllib3_context(ciphers=CIPHERS)
        kwargs["ssl_context"] = context
        return super(RsaAesShaAdapter, self).init_poolmanager(*args, **kwargs)

    def proxy_manager_for(self, *args, **kwargs):
        context = create_urllib3_context(ciphers=CIPHERS)
        kwargs["ssl_context"] = context
        return super(RsaAesShaAdapter, self).proxy_manager_for(*args, **kwargs)


# セッションを作成
s = requests.Session()
# セッションに対して https 通信の場合に指定のアダプタを使用するように設定
s.mount("https://", RsaAesShaAdapter())
r = s.get("https://xxx.com")
print(r.text)

おそらくこれで接続できるようになるはずです

サーバ側が対応している ciphers の調べ方

このあたりのやり方だとコマンドでできます
あとは外部からアクセスできるのであれば SSLChecker などのツールを使うと簡単に対応している ciphers を調べることができます

curl では普通にできる

おそらく curl はサーバ側の ciphers を調べているか最新でダメな場合に ciphers を変えてリトライしているのかもしれません

最後に

なぜか curl だとうまく行くが requests や http.client ではうまくいかない場合があります
そんあ場合は ciphers を見直してみると良いかなと思います

参考サイト

2023年3月1日水曜日

Python で SSL 証明書の検証と期限チェックを行う方法

Python で SSL 証明書の検証と期限チェックを行う方法

概要

取得した SSL 証明書と鍵の検証を行う方法を紹介します
期限が切れていないと鍵との組み合わせが正しいかもチェックしてみたいと思います

環境

  • Python 3.10.2
  • cryptography 39.0.1
  • pyOpenSSL 23.0.0

インストール

  • pipenv install pyOpenSSL cryptography

サンプルコード

from datetime import datetime
import OpenSSL
import sys

# 証明書と鍵を読み込む
with open('./fullchain.pem', 'rb') as f:
    cert_data = f.read()

with open('./privkey.pem', 'rb') as f:
    key_data = f.read()

# フォーマットチェック
try:
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_data)
    key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, key_data)
except OpenSSL.crypto.Error as e:
    print('PEMフォーマットではありません')
    sys.exit(1)

# 有効期限の取得
bytes = cert.get_notAfter()
timestamp = bytes.decode('utf-8')
formatted_timestamp = datetime.strptime(timestamp, '%Y%m%d%H%M%S%z').date().isoformat()
print (f"有効期限は{formatted_timestamp}")

# 証明書と鍵のペアを検証する
context = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
context.use_privatekey(key)
context.use_certificate(cert)

try:
    context.check_privatekey()
    print('証明書と鍵のペアは正しいです')
except OpenSSL.SSL.Error as e:
    print('証明書と鍵のペアが正しくありません:', e)

ちょっと解説

フォーマットチェックは load_certificate で読み込めたかどうかで判定します
エラーの場合はフォーマットエラーとして判断します

フォーマットが正しい場合は有効期限の取得を行います

最後に鍵とのペアチェックを証明書のコンテキストの check_privatekey で行います

参考サイト

2023年2月7日火曜日

fullchain な証明書をサーバ証明書とCA証明書に分割する方法

fullchain な証明書をサーバ証明書とCA証明書に分割する方法

概要

nginx などはサーバ証明とCA証明書 (or 中間CA証明書) を結合して配置することができます
ただミドルウェアやツールによってはサーバ証明書とCA証明書は別に分けて配置しなければいけない場合があります

そんな場合に結合されたサーバ証明書を分割する方法を紹介します

環境

  • Ubuntu 18.04
  • openssl 1.1.1 11

サーバ証明書とCA証明書の順番を確認する

コマンドで確認できます
基本的に結合されている証明書は単純に複数の証明書が文字列で順番に結合されているだけです

ただ普通に見てもそれがどの証明書かわからないので openssl コマンドで確認します

  • openssl verify -show_chain --untrusted fullchain.pem fullchain.pem

で以下のような結果になれば OK です
以下の場合は上から順番にサーバ証明書CA証明書、中間CA証明書になります

fullchain.pem: OK
Chain:
depth=0: CN = *.myserver.com (untrusted)
depth=1: C = US, O = Let's Encrypt, CN = R3 (untrusted)
depth=2: C = US, O = Internet Security Research Group, CN = ISRG Root X1

分割する

あとは fullchain.pem を分割すれば OK です   「BEGIN CERTICATE」と「END CERTIFICATE」でひとかたまりなので最初の塊を server.crt として保存しあとの 2 つの塊を ca.crt として保存すれば OK です

手動で実施しても OK ですし適当なスクリプトを作成しても OK です

気をつけなければいけないのは必ず openssl コマンドで確認した順番と証明書の種類を意識して分割してください

サーバ証明書は基本一つになりCA関連の証明書は複数あります