【问题标题】:SSL Client authentication in Android 4.xAndroid 4.x 中的 SSL 客户端身份验证
【发布时间】:2012-11-23 08:36:32
【问题描述】:

我想创建一个连接到服务器的应用程序。此服务器使用 SSL 客户端身份验证。应用程序的用户应该能够选择证书并允许使用它,就像它在浏览器应用程序中实现一样。

在浏览器应用程序中,身份验证按预期工作,因此我使用的证书是有效的。

当我尝试在我的应用程序中连接时,我收到以下错误:

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException:
SSL handshake terminated: ssl=0x2a2d3b38:
Failure in SSL library, usually a protocol error
error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
(external/openssl/ssl/s3_pkt.c:1290 0x2a2df880:0x00000003)

我尝试按照 android 文档进行实施。

这是我的示例 Activity 的代码:

public class ClientCertificateActivity extends Activity implements
    KeyChainAliasCallback {

protected static final String TAG = "CERT_TEST";
private String alias;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    choseCertificate();
    LinearLayout layout = new LinearLayout(this);
    Button connectToServer = new Button(this);
    connectToServer.setText("Try to connect to Server");
    connectToServer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
    connectToServer.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            connectToServer();
        }
    });
    layout.addView(connectToServer);
    addContentView(layout, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
}

protected void connectToServer() {
    final Context ctx = this;
    new AsyncTask<Void, Void, Boolean>() {

        private Exception error;

        @Override
        protected Boolean doInBackground(Void... arg) {
            try {
                PrivateKey pk = KeyChain.getPrivateKey(ctx, alias);
                X509Certificate[] chain = KeyChain.getCertificateChain(ctx,
                        alias);

                KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
                TrustManagerFactory tmf = TrustManagerFactory
                        .getInstance(TrustManagerFactory
                                .getDefaultAlgorithm());
                tmf.init(keyStore);

                SSLContext context = SSLContext.getInstance("TLS");
                context.init(null, tmf.getTrustManagers(), null);

                URL url = new URL("https://usecert.example.com/");
                HttpsURLConnection urlConnection = (HttpsURLConnection) url
                        .openConnection();
                urlConnection.setSSLSocketFactory(context
                        .getSocketFactory());
                InputStream in = urlConnection.getInputStream();

                return true;
            } catch (Exception e) {
                e.printStackTrace();
                error = e;
                return false;
            }
        }

        @Override
        protected void onPostExecute(Boolean valid) {
            if (error != null) {
                Toast.makeText(ctx, "Error: " + error.getMessage(),
                        Toast.LENGTH_LONG).show();
                return;
            }
            Toast.makeText(ctx, "Success: ", Toast.LENGTH_SHORT).show();
        }
    }.execute();

}

protected void choseCertificate() {
    KeyChain.choosePrivateKeyAlias(this, this,
            new String[] { "RSA", "DSA" }, null, "m.ergon.ch", 443, null);
}

@Override
public void alias(String alias) {
    this.alias = alias;
}
}

urlConnection.getInputStream();抛出异常

这是服务器和客户端之间握手的捕获。

感谢您的任何建议和提示。

【问题讨论】:

    标签: android https ssl-certificate authentication httpsurlconnection


    【解决方案1】:

    您永远不会使用您的私钥初始化 KeyManager,因此客户端身份验证无法获取它。

    您必须实现 X509KeyManager 才能返回您的 PrivateKey 和一些硬编码的别名。 这是库存电子邮件应用程序 (ICS+) 中的一个供参考。您可能需要对其进行一些修改,但应该很容易理解:基本上它只是将密钥、别名和证书链保存到字段并通过适当的方法返回它们(StubKeyManager 只是为未实现和不需要的方法抛出异常) :

    public static class KeyChainKeyManager extends StubKeyManager {
        private final String mClientAlias;
        private final X509Certificate[] mCertificateChain;
        private final PrivateKey mPrivateKey;
    
        public static KeyChainKeyManager fromAlias(Context context, String alias)
                throws CertificateException {
            X509Certificate[] certificateChain;
            try {
                certificateChain = KeyChain.getCertificateChain(context, alias);
            } catch (KeyChainException e) {
                logError(alias, "certificate chain", e);
                throw new CertificateException(e);
            } catch (InterruptedException e) {
                logError(alias, "certificate chain", e);
                throw new CertificateException(e);
            }
    
            PrivateKey privateKey;
            try {
                privateKey = KeyChain.getPrivateKey(context, alias);
            } catch (KeyChainException e) {
                logError(alias, "private key", e);
                throw new CertificateException(e);
            } catch (InterruptedException e) {
                logError(alias, "private key", e);
                throw new CertificateException(e);
            }
    
            if (certificateChain == null || privateKey == null) {
                throw new CertificateException("Can't access certificate from keystore");
            }
    
            return new KeyChainKeyManager(alias, certificateChain, privateKey);
        }
    
        private KeyChainKeyManager(
                String clientAlias, X509Certificate[] certificateChain, 
                PrivateKey privateKey) {
            mClientAlias = clientAlias;
            mCertificateChain = certificateChain;
            mPrivateKey = privateKey;
        }
    
    
        @Override
        public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
             return mClientAlias;
        }
    
        @Override
        public X509Certificate[] getCertificateChain(String alias) {
              return mCertificateChain;
        }
    
        @Override
        public PrivateKey getPrivateKey(String alias) {
                return mPrivateKey;
        }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-15
    • 2014-11-13
    • 1970-01-01
    • 2019-01-08
    • 1970-01-01
    • 1970-01-01
    • 2010-12-05
    • 1970-01-01
    相关资源
    最近更新 更多