Solicitud HTTP de SSL de Android mediante cert auto-firmado y CA
Tengo una aplicación de Android que se está conectando a un servicio web SSL que alojamos. El servidor Web es apache y tiene su propia CA que creamos y un certificado SSL auto-firmado.
He importado nuestro certificado de CA en la tableta de Android en la sección de certificados de confianza de usuario en Seguridad.
- HttpClient falla con Handshake Falló en Android 5.0 Lollipop
- Anulación del Administrador de confianza de SSL en Android
- Aplicación androide "Sin certificado par"
- ¿Cómo puedo implementar Pinning del certificado SSL mientras uso React Native
- TLS con extensión de indicación de nombre de servidor (RFC 3546) en Android
He probado el acceso al servidor web y puedo confirmar que el certificado del servicio web se muestra como válido (captura de pantalla a continuación)
Aquí está el certificado en la configuración de seguridad:
Ahora, cuando intento acceder al servicio web en mi aplicación, obtengo la excepción "No Peer Certificate" que se dispara.
Esta es la implementación SSL simplificada:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // allows network on main thread (temp hack) StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); SchemeRegistry schemeRegistry = new SchemeRegistry(); //schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); schemeRegistry.register(new Scheme("https", newSSLSocketFactory(), 443)); HttpParams params = new BasicHttpParams(); SingleClientConnManager mgr = new SingleClientConnManager(params, schemeRegistry); HttpClient client = new DefaultHttpClient(mgr, params); HttpPost httpRequest = new HttpPost("https://our-web-service.com"); try { client.execute(httpRequest); } catch (Exception e) { e.printStackTrace(); // } } /* * Standard SSL CA Store Setup // */ private SSLSocketFactory newSSLSocketFactory() { KeyStore trusted; try { trusted = KeyStore.getInstance("AndroidCAStore"); trusted.load(null, null); Enumeration<String> aliases = trusted.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); X509Certificate cert = (X509Certificate) trusted.getCertificate(alias); Log.d("", "Alias="+alias); Log.d("", "Subject DN: " + cert.getSubjectDN().getName()); Log.d("", "Issuer DN: " + cert.getIssuerDN().getName()); } SSLSocketFactory sf = new SSLSocketFactory(trusted); sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); return sf; } catch (Exception e) { // TODO Auto-generated catch block throw new AssertionError(e); } } }
El bucle while sólo escupe los certificados y puedo ver mi propia CA en los registros. Pero aún así obtengo la excepción "No Peer Certificate".
10-17 18: 29: 01.234: I / System.out (4006): Ningún certificado de compañero
¿Tengo que cargar manualmente mi certificado de CA de alguna manera en esta implementación?
- Uso de UnboundID SDK con un archivo de certificado SSL para conectarse al servidor LDAP en la aplicación de Android
- Https conexión, las diferencias entre Android 2.3 y 4
- ¿Qué usamos para la configuración de seguridad de red de Android N para un certificado auto-firmado?
- Aplicación de Android que trabaja en WIFI y 3G (Sin proxy), pero no funciona en 3G (Si proxy y puerto están asignados)
- Android 4.4.2 Protocolo SSL abortado
- Comodo SSL: ERR_CERT_AUTHORITY_INVALID en Chrome móvil y Opera móvil (Android)
- Cómo agregar un certificado pkcs12 de cliente a Postman Chrome, W7?
- Conexión de Https con conexión cerrada por peer en Android 6.0
Resuelto mediante: HttpsURLConnection
URLConnection conn = null; URL url = new URL(strURL); conn = url.openConnection(); HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
Esto parece funcionar bien con los certificados de CA instalados por el usuario.
Puede realizar la tarea también utilizando DefaultHttpClient
, aunque aquí se sugiere :
Preferir HttpURLConnection para el nuevo código
Preste atención también en la importación o adición de certificados a su aplicación, ya que puede tener problemas al actualizar el certificado cuando expire.
Aquí cómo obtener un DefaultHttpClient
confiando en un certificado autofirmado:
* This method returns the appropriate HttpClient. * @param isTLS Whether Transport Layer Security is required. * @param trustStoreInputStream The InputStream generated from the BKS keystore. * @param trustStorePsw The password related to the keystore. * @return The DefaultHttpClient object used to invoke execute(request) method. private DefaultHttpClient getHttpClient(boolean isTLS, InputStream trustStoreInputStream, String trustStorePsw) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException, UnrecoverableKeyException { DefaultHttpClient client = null; SchemeRegistry schemeRegistry = new SchemeRegistry(); Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 8080); schemeRegistry.register(http); if(isTLS) { KeyStore trustKeyStore = null; char[] trustStorePswCharArray = null; if(trustStorePsw!=null) { trustStorePswCharArray = trustStorePsw.toCharArray(); } trustKeyStore = KeyStore.getInstance("BKS"); trustKeyStore.load(trustStoreInputStream, trustStorePswCharArray); SSLSocketFactory sslSocketFactory = null; sslSocketFactory = new SSLSocketFactory(trustKeyStore); Scheme https = new Scheme("https", sslSocketFactory, 8443); schemeRegistry.register(https); } HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT); HttpConnectionParams.setSoTimeout(httpParams, SOCKET_TIMEOUT); ClientConnectionManager clientConnectionManager = new ThreadSafeClientConnManager(httpParams, schemeRegistry); client = new DefaultHttpClient(clientConnectionManager, httpParams); return client; }
Y aquí cómo obtener una HttpsURLConnection
:
* This method set the certificate for the HttpsURLConnection * @param url The url to contact. * @param certificateInputStream The InputStream generated from the .crt certificate. * @param certAlias The alias for the certificate. * @return The returned HttpsURLConnection private HttpsURLConnection getHttpsURLConnection(URL url, InputStream certificateInputStream, String certAlias) throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException { HttpsURLConnection connection = null; CertificateFactory certFactory = null; Certificate cert = null; KeyStore keyStore = null; TrustManagerFactory tmFactory = null; SSLContext sslContext = null; // Load certificates from an InputStream certFactory = CertificateFactory.getInstance("X.509"); cert = certFactory.generateCertificate(certificateInputStream); certificateInputStream.close(); // Create a KeyStore containing the trusted certificates keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); keyStore.setCertificateEntry(certAlias, cert); // Create a TrustManager that trusts the certificates in our KeyStore tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmFactory.init(keyStore); // Create an SSLContext that uses our TrustManager sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmFactory.getTrustManagers(), null); connection = (HttpsURLConnection)url.openConnection(); connection.setSSLSocketFactory(sslContext.getSocketFactory()); return connection; }