I am trying to implement a XML-RPC server with TLS client certificate in Twisted python. Furthermore, the end goal is to allow access to some methods only to a defined list of users (i.e. to a list of certificates).
While I got the first part down, I have difficulties transferring the certificate information down to the XMLRPC.render_POST
where I am planning to apply the filter.
I found this answer which describes how to display the common name, but I still have two problems:
Here's what I have so far :
import sys
import OpenSSL
from twisted.python.filepath import FilePath
from twisted.internet import endpoints
from twisted.internet import ssl
from twisted.python import log
from twisted.web import xmlrpc, server
from twisted.internet.ssl import Certificate
from twisted.internet.protocol import Protocol
class ReportWhichClient(Protocol):
# adapted from https://stackoverflow.com/a/28682511
def dataReceived(self, data):
peerCertificate = Certificate.peerFromTransport(self.transport)
print(peerCertificate.getSubject().commonName.decode('utf-8'))
class Example(xmlrpc.XMLRPC):
"""
An example object to be published.
"""
def xmlrpc_echo(self, x):
"""
Return all passed args.
"""
return x
def main():
log.startLogging(sys.stdout)
from twisted.internet import reactor
key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,FilePath("my.key").getContent())
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,FilePath("my.crt").getContent())
ca = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, FilePath("ca.crt").getContent())
contextFactory = ssl.CertificateOptions(privateKey=key, certificate=cert, verify=True, caCerts=[ca] , requireCertificate=True)
root = Example()
endpoint = endpoints.SSL4ServerEndpoint(reactor, 8083,contextFactory)
mySite = server.Site(root)
mySite.protocol = ReportWhichClient
endpoint.listen(mySite)
reactor.run()
if __name__ == '__main__':
main()
Is this the correct approach ? What should I do to have the required information at the resource level ?
Any answer would be great, at this point I have been racking my brain trying many solutions without any result.
Thanks
Ok so the solution was in front of me all this time.
After re-reading the source code, it appears that transport
is present in the request. All I had to do was to add the @withRequest
decorator and get all the information from there :
class Example(xmlrpc.XMLRPC):
@withRequest
def xmlrpc_echo(self, request, x):
peerCertificate = Certificate.peerFromTransport(request.transport)
key = peerCertificate.getPublicKey().original
# display the client public key in PEM format
print(OpenSSL.crypto.dump_publickey(OpenSSL.crypto.FILETYPE_PEM, key))
return x