如果我在Android中使用SSLSocket并硬编码源代码中信任库的密码是不安全的



我正在使用java的SSLSocket将Android应用程序安全地连接到服务器。证书位于信任库中。但是连接似乎需要信任库的密码,因此它目前被硬编码到设备中。我对PKI了解不多,但这似乎并不安全。我正在将信任库与原始资源文件夹中的应用程序捆绑在一起。是否有更好的方法来安全地允许应用程序使用 TLS 连接到服务器?我对TLS/SSL非常陌生,因此我将不胜感激任何帮助或建议。

这是代码的客户端。

        store = KeyStore.getInstance("BKS"); 
        InputStream in = appcontext.getResources().openRawResource(R.raw.truststore);
        store.load(in, "PASSWORD".toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        tmf.init(store);
        sslcontext = SSLContext.getInstance("TLS");
        sslcontext.init(null, tmf.getTrustManagers(), new SecureRandom());
        SSLSocketFactory sslsocketfactory = sslcontext.getSocketFactory();
        // connect to socket
        SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(
                "192.168.1.16", 9999);

我还没有在Android上尝试过,但这是它在纯Java中的工作方式。

您在此处将密钥库用作信任库,因此(希望(它不包含任何私钥或秘密材料。在这种情况下,密码的目的是验证商店的完整性。

Keystore.load(InputStream, char[])文档中:

可以给出密码来解锁密钥库(例如密钥库( 驻留在硬件令牌设备上(,或检查 密钥库数据。如果未提供用于完整性检查的密码,则 不执行完整性检查。

您可以使用null作为密码(在这种情况下,您将始终能够加载证书(,或者使用具有实际密码的非空char数组(在这种情况下,不正确的密码将失败,并显示">java.io.IOException:密钥库被篡改,或密码不正确"(。

在此处使用密码是为了提高安全性(以防止信任库被篡改(。话虽如此,如果您将此信任库和此应用程序捆绑在一起,则能够替换信任库的攻击者很可能也会获得对密码的访问权限(通过反编译(。(我对 Android 应用程序分发不太熟悉,但如果应用程序已签名,则篡改原始资源(如信任库(也可能会使签名无效,因此这可能是获得此保护的更现实的方式。否则,我想您可以要求用户每次都输入信任库的密码,但这似乎不现实。


这不是您问题的一部分,但是为了保护SSL/TLS连接,您还需要验证服务器发送的证书对于您尝试访问的主机名是否有效,SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("192.168.1.16", 9999);

最近在Java的这个问题中讨论了这个问题。(不幸的是,您将无法使用新的Java 7 API在Android上自动执行此操作,因此您必须手动检查证书。您可能也对最近的这个问题感兴趣。


编辑(根据您的评论(:

我担心有人重新编译应用程序,检索 密码和信任库,并使用它来连接到我的服务器。 如果可能的话,那么它有点违背了TLS的目的 第一名。

从此评论中不清楚您了解信任库的用途(例如,请参阅此问题(。

客户端

应用程序中的信任库的目的不是向服务器验证您的应用程序,而是确保您的客户端可以验证它连接到的服务器的身份,以便它不会被诱骗连接到 MITM 攻击者。这不是关于您的服务器如何信任您的应用程序/客户端/用户,而是关于您的应用程序如何信任您的服务器。

服务器认证应用程序用户是密钥库的目的。这对于对用户进行身份验证很好(但如果每个用户都有不同的证书,这将更有意义(。确保连接只能来自您的应用程序是另一个更困难的问题(如果您无法始终完全控制客户端硬件,则会导致丢失的原因(。在应用中使用通用客户端证书可以为您争取一些时间,但任何掌握客户端代码的人最终都可以对其进行逆向工程。我不会花太多时间在上面。您可能对以下问题感兴趣:

  • 如何安全地验证向我发送数据的客户端应用程序?
  • 在没有硬编码密钥的情况下对客户端进行身份验证?
  • 客户端代码篡改检测

问题:您在源代码中包含的任何内容都可以被任何获得 apk 文件的人读取。因此,如果您在源代码(或资源文件(中包含密码和信任库,则任何使用 apk 的人都可以读取它。

事实上,不可能保证连接到您服务器的应用程序是您自己的应用程序。 想象一下修改后的 Dalvik VM,它等到您的应用程序运行SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("192.168.1.16", 9999);,然后将sslsocket传递给另一个应用程序。

溶液:

  1. 将受信任的代码移动到服务器。例如,如果您的应用程序中有一个名为 addNewProductDirectlyToDatabaseOnServer 的方法,请将其替换为名为 addNewProductToServer 的方法,并让它为您的服务器调用 REST API,从而添加一个新用户,而不会让邪恶的应用程序直接控制您的数据库。
  2. 使用密码对单个用户进行身份验证。这不是绝对必要的,但它有助于确保正确的人正在访问您的服务器,如果坏人获得了某人的密码(或者如果某人成为坏人(,您可以通过禁用他们的密码/登录名来撤销他们对服务器的访问权限。

如果您有兴趣了解有关攻击者如何从apk文件中提取源代码的更多信息,我建议您查看以下工具:

  • https://code.google.com/p/android-apktool/- APKTOOL
  • https://code.google.com/p/dex2jar/和 http://java.decompiler.free.fr/?q=jdgui

最新更新