【问题标题】:Two way SSL authentication on androidandroid上的两种SSL身份验证
【发布时间】:2013-06-25 06:08:49
【问题描述】:

我正在尝试在 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认证的方式安全有效吗?

【问题讨论】:

  • 请问您运行的是哪个版本的Android?
  • @lainS 我正在测试 4.1.2 和 4.2.2
  • 你有没有想过解决这个问题?我有一个类似的问题。我将客户端证书添加到密钥库,但它从未发送到服务器。我试过包装 x509keymanager 类,我可以看到它根本没有被调用。

标签: android python sockets ssl openssl


【解决方案1】:

更新

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

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

还可以查看 emmby 答案,我发现它也很有见地。

原帖

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

Try looking at this article about using the connect to Unknown Certificates authority, which will let you use certificate that is not default with Android.。您可能必须在应用程序中包含公共/中间证书,以确保现有/旧设备能够与您的服务器连接。按照这篇文章,看看这是否解决了你的问题。

另外,这种双向SSL认证的方式安全有效吗?

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

【讨论】:

  • 这不能回答我的问题。我已经将服务器证书添加到我的信任库中,并且无需 client 身份验证即可连接。我无法将对等证书添加到密钥库,并将该证书发送到服务器。
  • 我添加了一些可能有帮助的资源。如果这仍然不起作用,至少其他人会想到替代方案。 “如果已经消除了不可能,那么剩下的无论多么不可能,都必须是真相”。
  • 感谢您的帮助,但这并不能真正解决我的问题。就我而言,端口无关紧要,因为我可以访问和控制服务器。我不是在写 HttpClient。此外,我首先得到了 SSLPeerUnverifiedException,因为我认为我没有放置正确的客户端证书/证书链/我不知道我需要在密钥库中什么
【解决方案2】:

服务器需要信任客户端证书。通常的方法是创建一个 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 创建套接字工厂。它们将使用正确的密钥和证书进行初始化。

【讨论】:

    猜你喜欢
    • 2012-05-22
    • 2015-04-01
    • 1970-01-01
    • 2014-07-22
    • 1970-01-01
    • 2015-01-02
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    相关资源
    最近更新 更多