Android se conecta a un servicio de reposo de WCF en IIS con la autenticación de certificado de cliente autofirmada

Saludos,

Estoy intentando crear una aplicación de prueba de Android que sólo realizará la siguiente acción 'Conectarse a un servicio de reposo JSON WCF en IIS con la autenticación de certificado de cliente firmada', sin embargo, en este momento sigo recibiendo 'acceso prohibido' cuando ejecuto la aplicación. Un WCF de cliente para el servicio de WCF parece funcionar bien y la aplicación de cliente de Android para el servicio de WCF también funciona cuando deshabilito 'requieren certificados de cliente'.

La parte extraña es que Eclipse informa que se encuentra el certificado del cliente y que se crea un KeyManager con él, pero no se envía nada al servidor.

Se han realizado los siguientes pasos para los certificados autofirmados

  1. Creado un rootCA.cer con como certificados secundarios un serverCA.cer y un clientCA.cer
  2. Se ha creado una información de clave privada desde el cliente
  3. Con Portecle dos keystores fueron creados en el formato BKS, uno que contiene la PKI de clientCA llamada keystore.bks y el otro truststore.bks que tiene el rootCA como entrada
  4. La PKI tiene el cliente alias que se utiliza para comprobar si la PKI se puede recuperar
  5. El archivo truststore.bks contiene el directorio raíz.CA
  6. Ambos keystores se agregaron en el res / raw de Eclipse de Android

Para el manejo de los certificados auto-firmados en Android he intentado utilizar varios ejemplos sin embargo el EasySSLSocketFactory y EasySSLTrustManager de StackOverflow: self-signed-ssl-acceptance-android trabajado en su mayor parte. También intenté crear los keystores con el keytool del defecto, sin embargo este resultado en los keystores más incorrectos que son creados.

Actualización 2011-03-17: información del sistema
El sistema operativo del sistema que aloja IIS es Windows XP con IIS-5 con .NET 4.0. El servicio en IIS tiene asignado serverCA.cer como certificado de servidor y los certificados de cliente requeridos están habilitados.

La versión android en la que estoy trabajando es 2.3.3 con Eclipse y ha establecido el permiso para Internet y tiene el almacén de claves y truststore agregado como recurso en bruto en el proyecto Eclipse.

Además, cuando miro en modo de depuración lo que el KeyManagerFactory.getKeyManagers () devuelve veo que hay un elemento en la lista.

A continuación se detallan las acciones / código que utilizo con el problema:

Los certificados se crearon con makecert ya que primero tenía que trabajar entre un servicio WCF y un cliente.

makecert.exe -r -n "CN=rootCA,O=Organization,OU=Org Unit,L=Location,S=SH,C=Country" -pe -ss root -sr LocalMachine -sky exchange -m 96 -a sha1 -len 2048 rootCA.cer -sv rootCA.pvk makecert.exe -n "CN=serverCA" -pe -ss my -sr LocalMachine -sky exchange -m 96 -in "rootCA" -is root -ir LocalMachine -a sha1 -eku 1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2 serverCA.cer makecert.exe -n "CN=clientCA" -pe -ss my -sr CurrentUser -sky exchange -m 96 -in "rootCA" -is root -ir LocalMachine -a sha1 -eku 1.3.6.1.5.5.7.3.2 clientCA.cer -sv clientCA. pvk2pfx.exe -pvk clientCA.pvk -spc clientCA.cer -pfx clientCA.pfx 

El WCF configurado es el siguiente:

 <?xml version="1.0"?> <configuration> <system.serviceModel> <extensions> <behaviorExtensions> <add name="consoleOutputBehavior" type="JsonTestService.ConsoleOutputBehaviorExtensionElement, JsonTestService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </behaviorExtensions> </extensions> <standardEndpoints> <webHttpEndpoint> <standardEndpoint name="JsonStandardEndpoint" defaultOutgoingResponseFormat="Json" automaticFormatSelectionEnabled="true"> <security mode="Transport"> <transport clientCredentialType="Certificate" proxyCredentialType="None" realm="" /> </security> </standardEndpoint> </webHttpEndpoint> </standardEndpoints> <bindings> <webHttpBinding> <binding name="JsonBinding"> <security mode="Transport"> <transport clientCredentialType="Certificate" proxyCredentialType="None" realm="" /> </security> </binding> </webHttpBinding> </bindings> <serviceHostingEnvironment aspNetCompatibilityEnabled="false" /> <behaviors> <endpointBehaviors> <behavior name="jsonBehavior"> <webHttp defaultBodyStyle="Wrapped" defaultOutgoingResponseFormat="Json" /> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior name="defaultBehavior"> <serviceDebug includeExceptionDetailInFaults="true" /> <serviceCredentials> <clientCertificate> <authentication certificateValidationMode="Custom" mapClientCertificateToWindowsAccount="false" customCertificateValidatorType="JsonTestService.CustomX509CertificateValidator, JsonTestService" /> </clientCertificate> <serviceCertificate findValue="serverCA" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" /> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="defaultBehavior" name="JsonTestService.TestService"> <endpoint address="json" behaviorConfiguration="jsonBehavior" binding="webHttpBinding" bindingConfiguration="JsonBinding" name="JsonEndpoint" contract="JsonTestService.ITestService" kind="webHttpEndpoint" endpointConfiguration="JsonStandardEndpoint"> </endpoint> </service> </services> </system.serviceModel> <system.web> <authentication mode="None" /> </system.web> </configuration> 

El objeto del servicio WCF

 namespace JsonTestService{ /// /// DataContract /// [DataContract(Name = "Foo", Namespace = "http://www.example.com/data")] public class FooDataContract { [DataMember(Order = 0)] public string Item { get; set; } [DataMember(Order = 1)] public int Count { get; set; } } /// /// Service Contract /// [ServiceContract(Namespace = "http://www.example.com/service")] public interface ITestService { [OperationContract] [WebInvoke(Method = "POST" , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json , BodyStyle = WebMessageBodyStyle.WrappedRequest , UriTemplate = "GetFoo.json/{name}?item={item}&count={countOfFoo}")] FooDataContract[] GetFoo(string name, int item, int countOfFoo); [OperationContract] [WebInvoke(Method = "GET" , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json , BodyStyle = WebMessageBodyStyle.WrappedRequest , UriTemplate = "GetFooRaw.json")] FooDataContract[] GetFooRaw(); } /// /// Service Implementation /// /// /// Each request will have its own instance of the service /// [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class TestService : ITestService { public FooDataContract[] GetFoo(string name, int item, int countOfFoo) { List result = null; for (int i = 0; i (); result.Add(new FooDataContract() { // default to "null" Name = (name ?? "null") + "_" + i, Age = age }); } return result == null ? null : result.ToArray(); } public FooDataContract[] GetFooRaw() { List result = new List(); for (int i = 0; i < 5; i++) result.Add(new FooDataContract() { Item = (i + 1) * 6, Name = "Test" + i.ToString() }); return result.ToArray(); } } 

El método Android que llama al servicio WCF es el siguiente

 private void testSSLDataTransfer() throws ClientProtocolException, IOException, Exception { try { SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(getKeyStore(),"",getTrustStore()), 443)); //password is empty HttpParams params = new BasicHttpParams(); params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1); params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1)); params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, "utf8"); ClientConnectionManager clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry); HttpContext context = new BasicHttpContext(); DefaultHttpClient client = new DefaultHttpClient(clientConnectionManager, params); HttpPost post = new HttpPost("https://10.12.14.16:443/JsonTest/TestService.svc/json/GetFoo.json/Test?item=12&count=2"); HttpGet get = new HttpGet("https://10.12.14.16:443/JsonTest/TestService.svc/json/GetFooBar.json"); post.setHeader("Accept", "application/json"); post.setHeader("Content-type", "application/json"); post.setHeader("User-Agent", "android"); get.setHeader("Accept", "application/json"); get.setHeader("Content-type", "application/json"); get.setHeader("User-Agent", "android"); HttpResponse response = client.execute(get, context); String statusLine = response.getStatusLine().toString(); //for debuf to see the response HttpEntity responseEntity = response.getEntity(); InputStream stream = responseEntity.getContent(); InputStreamReader reader = new InputStreamReader(stream); java.lang.StringBuffer stringBuffer = new java.lang.StringBuffer(); int read = 0; while((read = reader.read()) >= 0) stringBuffer.append((char)read); String s = stringBuffer.toString(); stream.close(); } catch (ClientProtocolException e) { throw e; } catch (IOException e) { String text = e.getMessage(); throw e; } catch (Exception e) { throw e; } } 

La siguiente parte es utilizada por el método testSSLDataTransfer para recuperar el certificado de cliente keystore y el truststore

 private KeyStore getKeyStore() throws IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException, UnrecoverableKeyException, Exception { KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); InputStream in = this.getApplicationContext().getResources().openRawResource(R.raw.keystore); try { keystore.load(in, "changeit".toCharArray()); Key key = keystore.getKey("client", null); //It has no password and this way it finds the Key } catch (Exception e) { throw e; } finally { in.close(); } return keystore; } private KeyStore getTrustStore() throws IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException { KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType()); InputStream in = this.getApplicationContext().getResources().openRawResource(R.raw.truststore); try { truststore.load(in, "changeit".toCharArray()); } finally { in.close(); } return truststore; } 

El EasySSLSocketFactory se ha modificado ligeramente para que el código se vea como:

 import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.security.KeyStore; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.scheme.LayeredSocketFactory; import org.apache.http.conn.scheme.SocketFactory; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /** * This socket factory will create ssl socket that accepts self signed * certificate * * @author olamy * @version $Id: EasySSLSocketFactory.java 765355 2009-04-15 20:59:07Z evenisse * $ * @since 1.2.3 */ public class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory { private SSLContext sslcontext = null; private KeyStore keystore = null; private KeyStore truststore = null; String keystorepassword = null; public EasySSLSocketFactory() { } public EasySSLSocketFactory(KeyStore keystore, String keystorepassword,KeyStore truststore) { this.keystore = keystore; this.keystorepassword = keystorepassword; this.truststore = truststore; } private static SSLContext createEasySSLContext(KeyStore keystore, String keystorepassword,KeyStore truststore) throws IOException { try { KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keystore, keystorepassword.toCharArray()); KeyManager[] list = keyManagerFactory.getKeyManagers(); SSLContext context = SSLContext.getInstance("TLS"); context.init(list, new TrustManager[] { new EasyX509TrustManager(truststore) }, null); return context; } catch (Exception e) { throw new IOException(e.getMessage()); } } private SSLContext getSSLContext() throws IOException { if (this.sslcontext == null) { this.sslcontext = createEasySSLContext(keystore, keystorepassword, truststore); } return this.sslcontext; } /** * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, * java.lang.String, int, java.net.InetAddress, int, * org.apache.http.params.HttpParams) */ public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { int connTimeout = HttpConnectionParams.getConnectionTimeout(params); int soTimeout = HttpConnectionParams.getSoTimeout(params); InetSocketAddress remoteAddress = new InetSocketAddress(host, port); SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket()); if ((localAddress != null) || (localPort > 0)) { // we need to bind explicitly if (localPort < 0) { localPort = 0; // indicates "any" } InetSocketAddress isa = new InetSocketAddress(localAddress, localPort); sslsock.bind(isa); } sslsock.connect(remoteAddress, connTimeout); sslsock.setSoTimeout(soTimeout); return sslsock; } /** * @see org.apache.http.conn.scheme.SocketFactory#createSocket() */ public Socket createSocket() throws IOException { return getSSLContext().getSocketFactory().createSocket(); } /** * @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket) */ public boolean isSecure(Socket socket) throws IllegalArgumentException { return true; } /** * @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, * java.lang.String, int, boolean) */ public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return getSSLContext().getSocketFactory().createSocket(socket, host, port,autoClose); } // ------------------------------------------------------------------- // javadoc in org.apache.http.conn.scheme.SocketFactory says : // Both Object.equals() and Object.hashCode() must be overridden // for the correct operation of some connection managers // ------------------------------------------------------------------- public boolean equals(Object obj) { return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class)); } public int hashCode() { return EasySSLSocketFactory.class.hashCode(); } } 

Certificados de cliente no compatibles con Android hasta una versión futura. Para tener certificados de cliente en una aplicación uno debe implementar código él / ella misma para tener este envío.

  • WCF vs WEB api vs Web servicios para el nuevo sitio web de comercio electrónico
  • Cargar archivos de MS Word de Android a .Net WCF?
  • Android con servicio web de WCF usando ksoap2 - error SoapFault - código de error: 'a: ActionNotSupported'
  • Servidor para la notificación de empuje de GCM a android en C #
  • Android - Https conexión a un servicio WCF de .NET da SSLException: "Ningún certificado de compañero"
  • Cómo consumir servicio WCF con Android
  • Wcf progressive descargar
  • Obtención de imagen de la matriz de bytes en el objeto JSON a la aplicación de Android
  • Creación de una arquitectura cliente-servidor con estado en WCF
  • Tener problemas para conectarse al servicio wcf en localhost desde el emulador de Android
  • ¿Cómo hacer SSO basada en reclamaciones para aplicaciones Android e IPhone usando ADFS STS?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.