【问题标题】:Android where to save server TLS X509CertificateAndroid 在哪里保存服务器 TLS X509Certificate
【发布时间】:2013-12-04 16:21:27
【问题描述】:

我正在尝试连接到服务器 o 获取我需要重用的 SSL 证书(用于应用程序中的安全 TCP 连接)。我需要将二进制形式的证书传递给这些类,如果用户第二天尝试重新连接到同一台服务器,我还需要将这个新证书与保存的证书进行比较。 出于这个原因,我有这个代码:

ttpsURLConnection = (HttpsURLConnection)url.openConnection();
inputStream = httpsURLConnection.getInputStream();
Certificate[] cert = httpsURLConnection.getServerCertificates();

//we are taking the last certificate which should be the server certificate  
X509Certificate x509Certificate = (X509Certificate) cert[cert.length-1];
byte [] encodedCertificate = x509Certificate.getEncoded();

我想知道保存此证书的最佳和最安全的方法,以便不必每次都打开连接。

首先我想到在这篇文章的帮助下使用 Android KeyChain: http://nelenkov.blogspot.com.es/2011/11/using-ics-keychain-api.html。 问题是他需要用户交互,然后用户必须在他的设备上激活模式或图钉,否则这不起作用。另一个不便之处是证书将可供所有应用使用,我不希望这样。

我也尝试过使用 KeySore:

String keyStoreType = KeyStore.getDefaultType();
keyStore = KeyStore.getInstance(keyStoreType);

InputStream stream = null;
char[] password = null;
keyStore.load(stream, password);
if(keyStore.getCertificate(CERTIFICATE_ALIAS) == null){
    Log.d(TAG, "KeyStore doesn't contain field "+CERTIFICATE_ALIAS);
keyStore.setCertificateEntry(CERTIFICATE_ALIAS, certificate);
 } else {           
     Log.d(TAG, "KeyStore already contains field "+CERTIFICATE_ALIAS);
     }
 } 

每次我回来时 keyStore.getCertificate(CERTIFICATE_ALIAS) == null 总是返回 true,所以不可能以这种方式“保存”证书。

我正在考虑使用私有模式将证书保存到 SharedPreferences,但我想知道它是否安全?如果没有,是否有更好的方法来保存仅由我的应用程序使用的证书(api 级别 14++) 谢谢

【问题讨论】:

    标签: android ssl certificate


    【解决方案1】:

    证书在设计上是公开的,因此将它们存储在哪里并不重要。 KeyStore 将起作用,但您必须调用 store 方法才能将其实际刷新到磁盘。如果您需要连接到多个服务器,最好使用证书主题名称的 SHA1 哈希作为密钥,这样您就不必搜索整个密钥库。如果您使用这种方法,您可以简单地将证书保存为文件并使用文件名作为密钥(ala Apache,Android 也在内部为受信任的证书执行此操作)。

    如果您将它们导入系统信任库(通过KeyChain),证书将在系统范围内用作信任锚,这可能不是您想要的。

    【讨论】:

      【解决方案2】:

      使用 KeyStore 是一个很好的解决方案。使用Keystore.store(OutputStream, char[]) 将此密钥库保存在应用程序持久存储区域(例如 SD 卡)中,并在每次应用程序启动时重新加载密钥库。

      使用此密钥库,您将能够创建一个 SSLSocketFactory,它只接受来自使用此证书的服务器的连接:

      KeyStore ks = getKeystore();
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      tmf.init(ks);
      TrustManager[] tms = tmf.getTrustManagers();
      SSLContext ctx = SSLContext.getDefault();
      ctx.init(null, tms, null);
      HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
      

      另一种解决方案是存储证书的 SHA-1 或 SHA-256 指纹,并在 SSL 握手后将此指纹与服务器证书进行比较。

      【讨论】:

      • 除非所有服务器证书都是自签名的,否则使用它们来初始化TrustManager 可能不是一个好主意。
      • 是的,这不是常见的做法。但是 AFAIK 可以在 X509 证书路径验证算法中将子 CA 视为信任锚。此外,如果为唯一用途设置密钥库并使用服务器证书对其进行初始化,则相当于直接信任。
      • 您可以使用任何东西作为信任锚,但是从网络中获取一些东西并盲目地将其放入信任存储区是一个坏主意。如果它是预先安装的,或者至少由用户以某种方式审查过,那就没问题了。如果该服务器一开始就受到威胁,那么确保您连接到同一台服务器并不会为您带来太多好处,但这是所有 TOFU 方案的问题。
      猜你喜欢
      • 2022-01-13
      • 1970-01-01
      • 2016-01-08
      • 1970-01-01
      • 2016-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-10
      相关资源
      最近更新 更多