当前位置: 首页 > 面试题库 >

使用TLS和Python进行身份验证

范鸿畅
2023-03-14
问题内容

我想为在Raspberry
Pi上运行并像本地服务器一样工作的软件制作一些更新脚本。该服务器应连接到Web上的主服务器,以获取软件更新并验证软件的许可证。为此,我设置了两个python脚本。我希望它们通过TLS套接字连接。然后,客户端检查服务器证书,然后服务器检查它是否是授权的客户端之一。我在此页面上找到了解决方案。

现在还有一个问题。我想知道 哪个客户端 (取决于证书)正在建立连接。有没有办法在Python 3中做到这一点?

我对每个答案都很满意。


问题答案:

一言以蔽之: 是的 ,这是完全有可能的,并且所有必要的东西都被移植到了python 3-我在Mac上的Python
3.4下测试了以下所有内容,并且看起来工作正常。

简短的答案是“使用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,您可以在一个窗口中运行,然后python tlsclient.py a; python tlsclient.py b在另一个窗口中运行,然后查看whichclient.py打印输出 a,然后b分别通过commonName
其证书主题中的字段来识别客户端

需要注意的是,您最初可能希望将该调用 Certificate.peerFromTransport放入connectionMade方法中。那行不通。
Twisted目前没有“
TLS握手完成”的回调;希望最终会成功,但是直到这样做之前,您必须等到从对等方收到一些经过身份验证的数据后,才能确保握手已完成。对于几乎所有应用程序来说,这都是很好的,因为到您收到执行任何操作的说明时(对于您而言,是下载更新),对等方必须已经发送了证书。



 类似资料:
  • 但请求呢?和是用户的属性,但应将它们发送到endpoint。如果我将资源发送到endpoint,则没有多大意义。 对此有没有办法,遵循JSONAPI并保持API的意义?

  • 我正在尝试使用urllib3连接到网页。代码如下所示。 如果我们假设url是需要使用用户名和密码进行身份验证的某个网页,那么我是否使用正确的代码进行身份验证? 我使用urllib2做这件事很舒服,但使用urllib3做不到同样的事情。 非常感谢

  • jwt不应该仅仅用于认证用户吗?我读到过可以在里面存储非敏感的东西,比如用户ID。将权限级别之类的东西存储在令牌中可以吗?这样我可以避免数据库调用。

  • 问题内容: 我可以通过接收到请求的xml 但不是 没有JavaScript错误,没有跨域策略问题。可能是语法错误,但是我找不到合适的教程。有什么建议吗? 问题答案: 我认为您需要纯格式:

  • 我需要使用 JDBC 连接到使用 Windows 身份验证的 SQL Server,其中我提供了连接字符串中的用户名和密码。这可以通过Windows身份验证实现吗? 我已经用JTDS和msql-jdbc尝试过这个,但无法让它工作。 我已经尝试了用户名和域的各种组合,但通常得到这样的东西: 019-01-18 14:15:31错误com.pts.demo.service.JdbcService-用户

  • 在不限制客户端证书的情况下,TLS中的相互身份验证有什么用? 以下是我对使用TLS的客户端/相互身份验证的理解。 现在rfc5246表示如下 这不会实现任何身份验证正确吗?例如,如果我有一个服务器信任由世界各地受信任的CA签署的任何证书,那么为什么还要费心于客户端身份验证呢?