Description
Summary
I'm using asyncnet to establish TLS connections and would like to access the verified certificate chain of the peer I'm connected with (either client or server).
Description
Currently it's not possible to retrieve the certificate chain from an OpenSSL-wrapped AsyncSocket
after the TLS-Handshake has completed. In the chat server example from the asyncnet module this would be in processClient
::
...
var sslContext = newContext()
proc processClient(client: AsyncSocket) {.async.} =
wrapConnectedSocket(sslContext, client, handshakeAsServer)
while true:
let line = await client.recvLine()
let certs = client.getPeerCertificates() # not possible
if line.len == 0: break
for c in clients:
await c.send(line & "\c\L")
...
OpenSSL provides SSL_get0_verified_chain
, but it needs the SSL
pointer of the current connection, which I can't access. There are two possible solutions that I can think of:
-
Export the
sslHandle
field ofAsyncSocket
/Socket
, so users can callSSL_get0_verified_chain
on it -
add a
proc getPeerCertificates
to the standard library, e.g.:
type
PSTACK_OF_X509* = SslPtr
Certificate* = string
proc CRYPTO_free*(p: pointer, file: cstring, line: int) {.cdecl, dynlib: DLLSSLName, importc.}
proc i2d_X509*(x: PX509, output: ptr ptr cchar): int {.cdecl, dynlib: DLLSSLName, importc.}
proc OPENSSL_sk_num*(stack: SslPtr): int {.cdecl, dynlib: DLLSSLName, importc.}
proc OPENSSL_sk_value*(stack: SslPtr, index: int): pointer {.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_get0_verified_chain*(ssl: SslPtr): PSTACK_OF_X509 {.cdecl, dynlib: DLLSSLName, importc.}
proc getPeerCertificates*(socket: AsyncSocket): seq[Certificate] =
result = newSeq[Certificate]()
let stack = SSL_get0_verified_chain(socket.sslHandle)
if stack == nil:
return
let length = OPENSSL_sk_num(stack)
if length == 0:
return
for i in 0 .. length - 1:
var der: ptr cchar = nil
let derLen = i2d_X509(cast[PX509](OPENSSL_sk_value(stack, i)), addr der)
var cert = newString(derLen)
copyMem(addr cert[0], der, derLen)
CRYPTO_free(der, instantiationInfo().filename, instantiationInfo().line)
result.add(cert)
Alternatives
I tried to register a callback using SSL_CTX_set_info_callback
to retrieve the SSL
pointer, but it seemed unnessecarily hard since it is already stored in the socket.