Looks like the certificate type is marked as nodoc, even when it is used as part of the public API, for example via OpenSSL::SSL::Socket - Crystal 1.11.2?
Also seems LibCrypto doesn’t bind the required X509_get0_notBefore method. So doesn’t seem like it’s doable out of the box via the stdlib, but surely could get it to work with some manual effort.
As @Blacksmoke16 said, this isn’t doable with the Crystal Standard Library.
I put together an example that retrieves the certificate from a remote host and prints the “not after” date.
require "openssl"
lib LibCrypto
alias ASN1_TIME = ASN1_STRING
# NOTE: output time is GMT (UTC+00:00)
fun asn1_time_to_tm = ASN1_TIME_to_tm(s : ASN1_TIME*, tm : LibC::Tm*) : LibC::Int
fun x509_get0_not_after = X509_get0_notAfter(x : X509) : ASN1_TIME*
end
module OpenSSL::X509
class Certificate
def not_after : Time
tm = uninitialized LibC::Tm
raise Error.new("X509_get0_notAfter") if @cert.null?
asn1_time = LibCrypto.x509_get0_not_after(@cert)
raise "Invalid ASN1_TIME on X509 certificate" if LibCrypto.asn1_time_to_tm(asn1_time, pointerof(tm)) == 0
# The C tm structure has a few quirks:
# - tm_year provides the year as an offset from the year 1900
# - tm_mon is 0-based, meaning January is 0 and December is 11
# - tm_sec can contain a leap second, which Crystal doesn't support.
Time.utc(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec >= 60 ? 59 : tm.tm_sec)
end
end
end
HOST = "forum.crystal-lang.org"
PORT = 443
begin
socket = TCPSocket.new(HOST, PORT)
ssl = OpenSSL::SSL::Socket::Client.new(socket, hostname: HOST)
puts "Certificate Validity for #{HOST}:#{PORT}"
puts "\tNot After: #{ssl.peer_certificate.not_after}"
ensure
socket.close unless socket.nil?
ssl.close unless ssl.nil?
end
I only tested this on Linux, but it should work on most systems (dunno about Windows).
If you want to read a certificate from a path—using the same API as Ruby—define a new initialize method and use the BIO_ and PEM_ functions from OpenSSL, such as BIO_new, BIO_write, and PEM_read_bio_X509. You’ll probably have to read the Crystal source code for OpenSSL::X509::Certificate to understand what’s already provided.
Feel free to ask for clarification if something isn’t clear.