使用 Android 和自签名证书

Using Android and a self signed certificate

我在 android 中使用自签名证书时遇到问题。我面临例外 java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

我会用几句话描述我的背景:

我正在 Raspberry Pi 上使用 NodeJS 运行 提供的 REST 服务开发一个 Android 应用程序。对于连接,我想使用 SSL/TLS 来保护连接,即使这个应用程序也只会在本地网络中使用。

所以我的问题是,我不能使用 Lets Encrypt 来创建证书,因为没有要验证的域,对吧?我的服务器仅是 运行 本地服务器,没有要验证的域。 因此,我为自己签名的服务器创建了一个自签名证书。它工作正常,我将证书添加到我的 macOS 钥匙串并且连接是安全的。 对于 Android,我使用此 approach 来验证我的自签名证书,但它不起作用。我通过原始资源包含了服务器证书。我认为这可能是问题所在,我必须将唱歌密钥的 public 密钥包含到信任管理器中,而不是服务器证书本身,对吧? 这就是说我必须创建一个自己的 CA?据我了解:我创建了一个 RSA 密钥对并使用私钥签署证书,我的 public 密钥需要包含在 TrustManager 中,对吗?

希望有人能帮助我:)

编辑: 我获取 SSLContext 和 getStringFromInputStream() 的方法按预期工作。

public static SSLContext getSslContext(Context context) throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException, NoSuchProviderException {
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC");
    InputStream inputStream = context.getResources().openRawResource(R.raw.keystore);

    Certificate certificate;

    try {
        certificate = certificateFactory.generateCertificate(inputStream);
    } finally {
        inputStream.close();
    }

    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", certificate);

    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
    trustManagerFactory.init(keyStore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

    return sslContext;
}

AsyncTask 打开一个连接

public class RestRetrieveTask extends AsyncTask<String, Void, String> 
{
    private Context context;

    public RestRetrieveTask(Context context) {
        this.context = context;
    }

    @Override
    protected String doInBackground(String... strings) {
        String result = null;

        try {
            SSLContext sslContext = CustomSslCertificateAuthority.getSslContext(context);

            URL url = new URL(strings[0]);
            HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
            httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());

            InputStream in = httpsURLConnection.getInputStream();
            result = getStringFromInputStream(in);
            in.close();
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
        }

        return result;
    }

    @Override
    protected void onPostExecute(String s) {
        System.out.println(s);
    }
}

我通过创建自己的 CA 并将根证书的 public 密钥添加到密钥库来解决了这个问题。 它就像一个魅力,我只需要创建我自己的 HostnameVerifier 因为我没有要验证的主机名。