title: Android connect to a WCF rest service in IIS with self-signed client-certificate authentication
body: <p>Greetings,</p>
  
  <p>I am trying to create an Android test application that will only perform the following action 'Connect to a JSON WCF rest service in IIS with self-signed client-certificate authentication', however at the moment I keep receiving 'forbidden access' when I run the application.
  A client WCF to the WCF service seems to work fine and the Android client application to the WCF service also works when I disabled 'require client certificates'.</p>
  
  <p>The strange part is that Eclipse informs that the client-certificate is found and that a KeyManager is created with it, but nothing is send to the server.</p>
  
  <p>The following steps have been undertaken for the self-signed certificates</p>
  
  <ol>
  <li>Created a rootCA.cer with as child certificates a serverCA.cer and clientCA.cer</li>
  <li>A Private Key Information  has been created from the clientCA.cer</li>
  <li>With Portecle two keystores were created in the BKS format, one containing the PKI of clientCA called keystore.bks and the other truststore.bks which has the rootCA as entry</li>
  <li>The PKI has the alias client that is used to double check if the PKI can be retrieved</li>
  <li>The truststore.bks contains the rootCA.cer</li>
  <li>Both keystores were added in the res/raw of Eclipse of Android</li>
  </ol>
  
  <p><br/><br/></p>
  
  <p>For the handling of the self-signed certificates in Android I tried to use several examples however the EasySSLSocketFactory and EasySSLTrustManager from <A href="http://stackoverflow.com/questions/1217141/self-signed-ssl-acceptance-android">StackOverflow:self-signed-ssl-acceptance-android worked for the most part.</a> I also tried creating the keystores with the default keytool, however this result into more incorrect keystores being created.</p>
  
  <p><br/>
  <br/><br/>
  <b>Update 2011-03-17: system information</b><br/>
  The system operating system that is hosting IIS is Windows XP with IIS-5 with .NET 4.0.
  The service in IIS has the serverCA.cer assigned as server certificate and the require client certificates is enabled. </p>
  
  <p>The android version I'm working on is 2.3.3 with Eclipse and have set the permission for Internet and have the keystore and truststore added as a raw resource in the Eclipse project.</p>
  
  <p>Additionally when I look up in debug mode what the KeyManagerFactory.getKeyManagers() return I see that there is one item in the list.
  <br/>
  <br/><br/></p>
  
  <p><b>Here are details of actions/code that I use with the issue:</b></p>
  
  <p>The certificates were created with makecert as it first had to work between a WCF service and client.</p>
  
  <pre><code>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
  </code></pre>
  
  <p><br/><br/>
  The WCF configured is as followed:</p>
  
  <pre><code>&lt;?xml version="1.0"?&gt;
  &lt;configuration&gt;
      &lt;system.serviceModel&gt;
      &lt;extensions&gt;
          &lt;behaviorExtensions&gt;
              &lt;add name="consoleOutputBehavior" type="JsonTestService.ConsoleOutputBehaviorExtensionElement, JsonTestService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /&gt;
          &lt;/behaviorExtensions&gt;
      &lt;/extensions&gt;
      &lt;standardEndpoints&gt;
          &lt;webHttpEndpoint&gt;
              &lt;standardEndpoint name="JsonStandardEndpoint" defaultOutgoingResponseFormat="Json"
                  automaticFormatSelectionEnabled="true"&gt;
                  &lt;security mode="Transport"&gt;
                      &lt;transport clientCredentialType="Certificate" proxyCredentialType="None" realm="" /&gt;
                  &lt;/security&gt;
              &lt;/standardEndpoint&gt;
          &lt;/webHttpEndpoint&gt;
      &lt;/standardEndpoints&gt;
      &lt;bindings&gt;
          &lt;webHttpBinding&gt;
              &lt;binding name="JsonBinding"&gt;
                  &lt;security mode="Transport"&gt;
                      &lt;transport clientCredentialType="Certificate" proxyCredentialType="None" realm="" /&gt;
                  &lt;/security&gt;
              &lt;/binding&gt;
          &lt;/webHttpBinding&gt;
      &lt;/bindings&gt;
      &lt;serviceHostingEnvironment aspNetCompatibilityEnabled="false" /&gt;
      &lt;behaviors&gt;
          &lt;endpointBehaviors&gt;
              &lt;behavior name="jsonBehavior"&gt;
                  &lt;webHttp defaultBodyStyle="Wrapped" defaultOutgoingResponseFormat="Json" /&gt;
              &lt;/behavior&gt;
          &lt;/endpointBehaviors&gt;
          &lt;serviceBehaviors&gt;
              &lt;behavior name="defaultBehavior"&gt;
                  &lt;serviceDebug includeExceptionDetailInFaults="true" /&gt;
                  &lt;serviceCredentials&gt;
                      &lt;clientCertificate&gt;
                          &lt;authentication certificateValidationMode="Custom" mapClientCertificateToWindowsAccount="false"
                                          customCertificateValidatorType="JsonTestService.CustomX509CertificateValidator, JsonTestService"
                                          /&gt;
                      &lt;/clientCertificate&gt;
                      &lt;serviceCertificate findValue="serverCA" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" /&gt;
                  &lt;/serviceCredentials&gt;
              &lt;/behavior&gt;
          &lt;/serviceBehaviors&gt;
      &lt;/behaviors&gt;
      &lt;services&gt;
          &lt;service behaviorConfiguration="defaultBehavior" name="JsonTestService.TestService"&gt;
              &lt;endpoint address="json" behaviorConfiguration="jsonBehavior"
                  binding="webHttpBinding" bindingConfiguration="JsonBinding"
                  name="JsonEndpoint" contract="JsonTestService.ITestService" kind="webHttpEndpoint"
                  endpointConfiguration="JsonStandardEndpoint"&gt;
              &lt;/endpoint&gt;
          &lt;/service&gt;
      &lt;/services&gt;
      &lt;/system.serviceModel&gt;
      &lt;system.web&gt;
          &lt;authentication mode="None" /&gt;
      &lt;/system.web&gt;
  &lt;/configuration&gt;
  </code></pre>
  
  <p><br/><br/>
  The Object of the WCF service</p>
  
  <pre><code>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 &lt; 5; i++)
              result.Add(new FooDataContract() { Item = (i + 1) * 6, Name = "Test" + i.ToString() });
          return result.ToArray();
      }
  }</code></pre>
  
  <p><br/><br/>
  The Android method that calls the WCF service is as followed</p>
  
  <pre><code>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;
      }
  }
  </code></pre>
  
  <p><br/><br/></p>
  
  <p>The following part is used by the method testSSLDataTransfer to retrieve the client certificate keystore and the truststore</p>
  
  <pre><code>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;
  }
  </code></pre>
  
  <p><br/><br/>
  The EasySSLSocketFactory has been modified slightly so that the code looks like:</p>
  
  <pre><code>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 &gt; 0)) {
                  // we need to bind explicitly
                  if (localPort &lt; 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();
      }
  }</code></pre>