4

我正在尝试在 Python 服务器和 Android 客户端应用程序之间进行两种 SSL 身份验证。我可以访问服务器和客户端,并希望使用我自己的证书来实现客户端身份验证。到目前为止,我已经能够验证服务器证书并在没有客户端身份验证的情况下进行连接。

客户端需要什么样的证书,我如何让它在握手过程中自动发送到服务器?这是我到目前为止的客户端和服务器端代码。我的方法错了吗?

服务器代码

while True: # Keep listening for clients
    c, fromaddr = sock.accept()

    ssl_sock = ssl.wrap_socket(c,
            keyfile = "serverPrivateKey.pem",
            certfile = "servercert.pem",
            server_side = True,
            # Require the client to provide a certificate
            cert_reqs = ssl.CERT_REQUIRED,
            ssl_version = ssl.PROTOCOL_TLSv1,
            ca_certs = "clientcert.pem", #TODO must point to a file of CA certificates??
            do_handshake_on_connect = True,
            ciphers="!NULL:!EXPORT:AES256-SHA")

    print ssl_sock.cipher()
    thrd = sock_thread(ssl_sock)
    thrd.daemon = True
    thrd.start()

我怀疑我可能为 ca_certs 使用了错误的文件...?

客户代码

    private boolean connect() {
    try {
        KeyStore keystore = KeyStore.getInstance("BKS"); // Stores the client certificate, to be sent to server
        KeyStore truststore = KeyStore.getInstance("BKS"); // Stores the server certificate we want to trust
        // TODO: change hard coded password... THIS IS REAL BAD MKAY
        truststore.load(mSocketService.getResources().openRawResource(R.raw.truststore), "test".toCharArray());
        keystore.load(mSocketService.getResources().openRawResource(R.raw.keystore), "test".toCharArray());

        // Use the key manager for client authentication. Keys in the key manager will be sent to the host
        KeyManagerFactory keyFManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyFManager.init(keystore, "test".toCharArray());

        // Use the trust manager to determine if the host I am connecting to is a trusted host
        TrustManagerFactory trustMFactory = TrustManagerFactory.getInstance(TrustManagerFactory
                .getDefaultAlgorithm());
        trustMFactory.init(truststore);

        // Create the socket factory and add both the trust manager and key manager
        SSLCertificateSocketFactory socketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory
                .getDefault(5000, new SSLSessionCache(mSocketService));
        socketFactory.setTrustManagers(trustMFactory.getTrustManagers());
        socketFactory.setKeyManagers(keyFManager.getKeyManagers());

        // Open SSL socket directly to host, host name verification is NOT performed here due to
        // SSLCertificateFactory implementation
        mSSLSocket = (SSLSocket) socketFactory.createSocket(mHostname, mPort);
        mSSLSocket.setSoTimeout(TIMEOUT);

        // Most SSLSocketFactory implementations do not verify the server's identity, allowing man-in-the-middle
        // attacks. This implementation (SSLCertificateSocketFactory) does check the server's certificate hostname,
        // but only for createSocket variants that specify a hostname. When using methods that use InetAddress or
        // which return an unconnected socket, you MUST verify the server's identity yourself to ensure a secure
        // connection.
        verifyHostname();
        // Safe to proceed with socket now
...

我使用 openssl 生成了一个客户端私钥、一个客户端证书、一个服务器私钥和一个服务器证书。然后我将客户端证书添加到keystore.bks(我存储在其中/res/raw/keystore.bks)然后我将服务器证书添加到truststore.bks

因此,现在当客户端尝试连接时,我在服务器端收到此错误:

ssl.SSLError: [Errno 1] _ssl.c:504: error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate

当我尝试在 android 客户端中执行此操作时

SSLSession s = mSSLSocket.getSession();
s.getPeerCertificates();

我收到此错误:

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

所以很明显,我使用的密钥库似乎没有正确的对等证书,因此没有向服务器发送一个。

我应该在密钥库中放入什么来防止此异常?

此外,这种双向SSL认证的方法是否安全有效?

4

2 回答 2

1

服务器需要信任客户端证书。执行此操作的常用方法是创建一个 CA,然后让它签署服务器证书和客户端证书。每个人都将在各自的信任库中拥有 CA 证书。然后你需要SSLContext用这样的东西初始化:

KeyStore trustStore = loadTrustStore();
KeyStore keyStore = loadKeyStore();

TrustManagerFactory tmf = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);

KeyManagerFactory kmf = KeyManagerFactory
                    .getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, KEYSTORE_PASSWORD.toCharArray());

SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

然后,您可以SSLContext根据需要使用创建套接字工厂。它们将使用正确的密钥和证书进行初始化。

于 2013-07-04T08:17:46.430 回答
0

更新

我无法将对等证书添加到密钥库,并将该证书发送到服务器。

做一些可能有助于您的旅程的研究。mvsjes2 报告错误的端口可能会导致抛出 SSLPeerUnverifiedException。验证您使用的是端口 443,因为我在您的代码中看不到您设置端口的位置。

另请查看我发现也很有见地的 emmby 答案。

原帖

所以很明显,我使用的密钥库似乎没有正确的对等证书,因此没有向服务器发送一个。

尝试查看有关使用连接到未知证书颁发机构的这篇文章,这将让您使用 Android 不默认的证书。. 您可能必须在应用程序中包含公共/中间证书,以确保现有/旧设备能够与您的服务器连接。按照这篇文章,看看这是否解决了你的问题。

此外,这种双向SSL认证的方法是否安全有效?

只有你能先让它工作!只要您的代码可以检查 SSL 证书的有效性,它就应该足够有效。安全方面...只要 ssl 证书本身是使用强大的 RSA 签名/密钥创建的,它就会比普通的 http 更安全。如果您仍然有问题,请告诉我。

于 2013-07-03T16:58:33.133 回答