Skip to content

Documentation: Usage of HTTPS certificates for local development #812

Description

@ThisIsMissEm

Hi! I've been working on Mastodon which uses http.rb, and in local development, I'm using localcan to route traffic to various development servers all with auto-generated SSL certificates from a root certificate that localcan created. This means they're all self-signed.

Whilst reading the HTTPS documentation, I noticed that it was using ctx.set_params on the OpenSSL::SSL::SSLContext, I noticed a line in the documentation of:

The cert, key, and extra_chain_cert attributes are deprecated. It is recommended to use add_certificate instead.

Source: https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-cert

I'm wondering if the documentation should be updated to use that add_certificate method instead?

As I also have a Root CA certificate, as well as individual .crt and .key files per domain, I'm wondering if there's a way to tell http.rb to just add that Root CA certificate to it's trust chain, without overriding any existing certificates?

Activity

  1. tarcieri commented on Jul 20, 2025

    @tarcieri
    Member

    I'm wondering if the documentation should be updated to use that add_certificate method instead?

    Yes, updating the documentation sounds good. If you can get me a working replacement code example, I can do the update.

    As I also have a Root CA certificate, as well as individual .crt and .key files per domain, I'm wondering if there's a way to tell http.rb to just add that Root CA certificate to it's trust chain, without overriding any existing certificates?

    I believe that's handled by OpenSSL::X509::Store which you can access using OpenSSL::X509::SSLContext#cert_store

  2. ThisIsMissEm commented on Jul 20, 2025

    @ThisIsMissEm
    Author

    Okay, so, yeah, from what I can tell this snippet:

    HTTP.get("https://example.com", ssl_context: OpenSSL::SSL::SSLContext.new.tap do |ctx|
      ctx.set_params(
        cert: OpenSSL::X509::Certificate.new(File.read("client.crt")),
        key:  OpenSSL::PKey::RSA.new(File.read("client.key"))
      )
    end)

    becomes:

    HTTP.get("https://example.com", ssl_context: OpenSSL::SSL::SSLContext.new.tap do |ctx|
      ctx.add_certificate(
        OpenSSL::X509::Certificate.new(File.read("client.crt")),
        OpenSSL::PKey::RSA.new(File.read("client.key"))
      )
    end)

    and the second snippet:

    ssl_context = OpenSSL::SSL::SSLContext.new.tap do |ctx|
      ctx.set_params(
        cert: certs.shift,  # The root certificate
        key:  OpenSSL::PKey::RSA.new(File.read("path_to_your_private_key.pem")),
        extra_chain_cert: certs  # The intermediate certificates
      )
    end

    becomes:

    bundle = File.read("path_to_your_fullchain.pem")
    certificate_content_regex = /-----BEGIN CERTIFICATE-----(?:.|\n)+?-----END CERTIFICATE-----/
    certs = bundle.scan(certificate_content_regex).map { OpenSSL::X509::Certificate.new(_1) }
    
    ssl_context = OpenSSL::SSL::SSLContext.new.tap do |ctx|
      ctx.add_certificate(
        certs.shift,  # The root certificate
        OpenSSL::PKey::RSA.new(File.read("path_to_your_private_key.pem")),
        certs  # The intermediate certificates
      )
    end

    Hope that helps. n.b., I haven't tested exactly this code, but I did do similar the other day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions