使用 TLS 和 Python 进行身份验证
Use TLS and Python for authentication
我想为在 Raspberry Pi 上运行并像本地服务器一样工作的软件制作一个小的更新脚本。那应该连接到 Web 中的主服务器以获取软件更新并验证软件的许可证。
为此,我设置了两个 python 脚本。我希望这些通过 TLS 套接字连接。然后客户端检查服务器证书,服务器检查它是否是授权客户端之一。我在 this page.
上使用 twisted 找到了解决方案
现在还有一个问题。我想知道哪个客户端(取决于证书)正在建立连接。有没有办法在 Python 3 中用 twisted 做到这一点?
我对每个答案都很满意。
一句话:是的,这个是很有可能的,应有尽有
移植到 python 3 - 我在 Python 3.4 下在我的 Mac 上测试了以下所有内容,似乎
工作正常。
简短的回答是
“use twisted.internet.ssl.Certificate.peerFromTransport
”
但鉴于需要进行大量设置才能达到目标
可能,我构建了一个完整的示例,您应该能够
尝试并以此为基础。
为了后代,您首先需要生成一些客户端证书
由同一个 CA 签名。你可能已经这样做了,但其他人也可以这样做
理解答案并自己尝试(这样我就可以测试我的
自己回答 ;-)),他们需要这样的代码:
# newcert.py
from twisted.python.filepath import FilePath
from twisted.internet.ssl import PrivateCertificate, KeyPair, DN
def getCAPrivateCert():
privatePath = FilePath(b"ca-private-cert.pem")
if privatePath.exists():
return PrivateCertificate.loadPEM(privatePath.getContent())
else:
caKey = KeyPair.generate(size=4096)
caCert = caKey.selfSignedCert(1, CN="the-authority")
privatePath.setContent(caCert.dumpPEM())
return caCert
def clientCertFor(name):
signingCert = getCAPrivateCert()
clientKey = KeyPair.generate(size=4096)
csr = clientKey.requestObject(DN(CN=name), "sha1")
clientCert = signingCert.signRequestObject(
csr, serialNumber=1, digestAlgorithm="sha1")
return PrivateCertificate.fromCertificateAndKeyPair(clientCert, clientKey)
if __name__ == '__main__':
import sys
name = sys.argv[1]
pem = clientCertFor(name.encode("utf-8")).dumpPEM()
FilePath(name.encode("utf-8") + b".client.private.pem").setContent(pem)
使用此程序,您可以像这样创建一些证书:
$ python newcert.py a
$ python newcert.py b
现在您应该有几个可以使用的文件了:
$ ls -1 *.pem
a.client.private.pem
b.client.private.pem
ca-private-cert.pem
那么您需要一个使用这些证书之一的客户端,并发送一些
数据:
# tlsclient.py
from twisted.python.filepath import FilePath
from twisted.internet.endpoints import SSL4ClientEndpoint
from twisted.internet.ssl import (
PrivateCertificate, Certificate, optionsForClientTLS)
from twisted.internet.defer import Deferred, inlineCallbacks
from twisted.internet.task import react
from twisted.internet.protocol import Protocol, Factory
class SendAnyData(Protocol):
def connectionMade(self):
self.deferred = Deferred()
self.transport.write(b"HELLO\r\n")
def connectionLost(self, reason):
self.deferred.callback(None)
@inlineCallbacks
def main(reactor, name):
pem = FilePath(name.encode("utf-8") + b".client.private.pem").getContent()
caPem = FilePath(b"ca-private-cert.pem").getContent()
clientEndpoint = SSL4ClientEndpoint(
reactor, u"localhost", 4321,
optionsForClientTLS(u"the-authority", Certificate.loadPEM(caPem),
PrivateCertificate.loadPEM(pem)),
)
proto = yield clientEndpoint.connect(Factory.forProtocol(SendAnyData))
yield proto.deferred
import sys
react(main, sys.argv[1:])
最后,一个可以区分它们的服务器:
# whichclient.py
from twisted.python.filepath import FilePath
from twisted.internet.endpoints import SSL4ServerEndpoint
from twisted.internet.ssl import PrivateCertificate, Certificate
from twisted.internet.defer import Deferred
from twisted.internet.task import react
from twisted.internet.protocol import Protocol, Factory
class ReportWhichClient(Protocol):
def dataReceived(self, data):
peerCertificate = Certificate.peerFromTransport(self.transport)
print(peerCertificate.getSubject().commonName.decode('utf-8'))
self.transport.loseConnection()
def main(reactor):
pemBytes = FilePath(b"ca-private-cert.pem").getContent()
certificateAuthority = Certificate.loadPEM(pemBytes)
myCertificate = PrivateCertificate.loadPEM(pemBytes)
serverEndpoint = SSL4ServerEndpoint(
reactor, 4321, myCertificate.options(certificateAuthority)
)
serverEndpoint.listen(Factory.forProtocol(ReportWhichClient))
return Deferred()
react(main, [])
为了简单起见,我们将重新使用 CA 自己的证书
服务器,但在更现实的场景中,您显然需要更多
适当的证书。
您现在可以 运行 whichclient.py
在一个 window 中,然后 python tlsclient.py a;
python tlsclient.py b
在另一个 window 中,然后查看 whichclient.py
打印出来
a
然后是 b
,通过 commonName
识别客户端
证书主题中的字段。
这里需要注意的是,您最初可能想将该呼叫拨给
Certificate.peerFromTransport
转换为 connectionMade
方法;那不会
工作。
Twisted does not presently have a callback for "TLS handshake complete";
希望它最终会,但在它发生之前,你必须等到你
从对等方接收到一些经过身份验证的数据,以确保握手成功
完全的。对于几乎所有的应用程序,这都很好,因为到时候你
已收到执行任何操作的指示(在您的情况下下载更新)
对等方必须已经发送了证书。
我想为在 Raspberry Pi 上运行并像本地服务器一样工作的软件制作一个小的更新脚本。那应该连接到 Web 中的主服务器以获取软件更新并验证软件的许可证。 为此,我设置了两个 python 脚本。我希望这些通过 TLS 套接字连接。然后客户端检查服务器证书,服务器检查它是否是授权客户端之一。我在 this page.
上使用 twisted 找到了解决方案现在还有一个问题。我想知道哪个客户端(取决于证书)正在建立连接。有没有办法在 Python 3 中用 twisted 做到这一点?
我对每个答案都很满意。
一句话:是的,这个是很有可能的,应有尽有 移植到 python 3 - 我在 Python 3.4 下在我的 Mac 上测试了以下所有内容,似乎 工作正常。
简短的回答是
“use twisted.internet.ssl.Certificate.peerFromTransport
”
但鉴于需要进行大量设置才能达到目标
可能,我构建了一个完整的示例,您应该能够
尝试并以此为基础。
为了后代,您首先需要生成一些客户端证书 由同一个 CA 签名。你可能已经这样做了,但其他人也可以这样做 理解答案并自己尝试(这样我就可以测试我的 自己回答 ;-)),他们需要这样的代码:
# newcert.py
from twisted.python.filepath import FilePath
from twisted.internet.ssl import PrivateCertificate, KeyPair, DN
def getCAPrivateCert():
privatePath = FilePath(b"ca-private-cert.pem")
if privatePath.exists():
return PrivateCertificate.loadPEM(privatePath.getContent())
else:
caKey = KeyPair.generate(size=4096)
caCert = caKey.selfSignedCert(1, CN="the-authority")
privatePath.setContent(caCert.dumpPEM())
return caCert
def clientCertFor(name):
signingCert = getCAPrivateCert()
clientKey = KeyPair.generate(size=4096)
csr = clientKey.requestObject(DN(CN=name), "sha1")
clientCert = signingCert.signRequestObject(
csr, serialNumber=1, digestAlgorithm="sha1")
return PrivateCertificate.fromCertificateAndKeyPair(clientCert, clientKey)
if __name__ == '__main__':
import sys
name = sys.argv[1]
pem = clientCertFor(name.encode("utf-8")).dumpPEM()
FilePath(name.encode("utf-8") + b".client.private.pem").setContent(pem)
使用此程序,您可以像这样创建一些证书:
$ python newcert.py a
$ python newcert.py b
现在您应该有几个可以使用的文件了:
$ ls -1 *.pem
a.client.private.pem
b.client.private.pem
ca-private-cert.pem
那么您需要一个使用这些证书之一的客户端,并发送一些 数据:
# tlsclient.py
from twisted.python.filepath import FilePath
from twisted.internet.endpoints import SSL4ClientEndpoint
from twisted.internet.ssl import (
PrivateCertificate, Certificate, optionsForClientTLS)
from twisted.internet.defer import Deferred, inlineCallbacks
from twisted.internet.task import react
from twisted.internet.protocol import Protocol, Factory
class SendAnyData(Protocol):
def connectionMade(self):
self.deferred = Deferred()
self.transport.write(b"HELLO\r\n")
def connectionLost(self, reason):
self.deferred.callback(None)
@inlineCallbacks
def main(reactor, name):
pem = FilePath(name.encode("utf-8") + b".client.private.pem").getContent()
caPem = FilePath(b"ca-private-cert.pem").getContent()
clientEndpoint = SSL4ClientEndpoint(
reactor, u"localhost", 4321,
optionsForClientTLS(u"the-authority", Certificate.loadPEM(caPem),
PrivateCertificate.loadPEM(pem)),
)
proto = yield clientEndpoint.connect(Factory.forProtocol(SendAnyData))
yield proto.deferred
import sys
react(main, sys.argv[1:])
最后,一个可以区分它们的服务器:
# whichclient.py
from twisted.python.filepath import FilePath
from twisted.internet.endpoints import SSL4ServerEndpoint
from twisted.internet.ssl import PrivateCertificate, Certificate
from twisted.internet.defer import Deferred
from twisted.internet.task import react
from twisted.internet.protocol import Protocol, Factory
class ReportWhichClient(Protocol):
def dataReceived(self, data):
peerCertificate = Certificate.peerFromTransport(self.transport)
print(peerCertificate.getSubject().commonName.decode('utf-8'))
self.transport.loseConnection()
def main(reactor):
pemBytes = FilePath(b"ca-private-cert.pem").getContent()
certificateAuthority = Certificate.loadPEM(pemBytes)
myCertificate = PrivateCertificate.loadPEM(pemBytes)
serverEndpoint = SSL4ServerEndpoint(
reactor, 4321, myCertificate.options(certificateAuthority)
)
serverEndpoint.listen(Factory.forProtocol(ReportWhichClient))
return Deferred()
react(main, [])
为了简单起见,我们将重新使用 CA 自己的证书 服务器,但在更现实的场景中,您显然需要更多 适当的证书。
您现在可以 运行 whichclient.py
在一个 window 中,然后 python tlsclient.py a;
python tlsclient.py b
在另一个 window 中,然后查看 whichclient.py
打印出来
a
然后是 b
,通过 commonName
识别客户端
证书主题中的字段。
这里需要注意的是,您最初可能想将该呼叫拨给
Certificate.peerFromTransport
转换为 connectionMade
方法;那不会
工作。
Twisted does not presently have a callback for "TLS handshake complete";
希望它最终会,但在它发生之前,你必须等到你
从对等方接收到一些经过身份验证的数据,以确保握手成功
完全的。对于几乎所有的应用程序,这都很好,因为到时候你
已收到执行任何操作的指示(在您的情况下下载更新)
对等方必须已经发送了证书。