我正在使用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
传递给另一个应用程序。
溶液:
- 将受信任的代码移动到服务器。例如,如果您的应用程序中有一个名为
addNewProductDirectlyToDatabaseOnServer
的方法,请将其替换为名为addNewProductToServer
的方法,并让它为您的服务器调用 REST API,从而添加一个新用户,而不会让邪恶的应用程序直接控制您的数据库。 - 使用密码对单个用户进行身份验证。这不是绝对必要的,但它有助于确保正确的人正在访问您的服务器,如果坏人获得了某人的密码(或者如果某人成为坏人(,您可以通过禁用他们的密码/登录名来撤销他们对服务器的访问权限。
如果您有兴趣了解有关攻击者如何从apk文件中提取源代码的更多信息,我建议您查看以下工具:
- https://code.google.com/p/android-apktool/- APKTOOL
- https://code.google.com/p/dex2jar/和 http://java.decompiler.free.fr/?q=jdgui