2011-03-16 55 views
5

问候,的Android连接到一个WCF休息服务在IIS中使用自签名的客户证书认证

我想创建一个Android测试应用程序,将只执行以下操作“连接到一个JSON WCF休息服务在IIS中使用自签名客户端证书身份验证',但是此刻我在运行应用程序时仍然收到“禁止访问”。 WCF服务的客户端WCF似乎工作正常,并且当我禁用“需要客户端证书”时,Android客户端应用程序也适用于WCF服务。

奇怪的部分是Eclipse通知客户端证书已找到并且KeyManager是使用它创建的,但没有任何内容发送到服务器。

以下步骤已经进行了创建一个rootCA.cer与作为独生子女证一serverCA.cer和clientCA.cer

  • 私有密钥的信息已经从创建的自签名证书

    1. 所述clientCA.cer
    2. 随着Portecle 2个密钥库中在BKS格式创建,一种含clientCA的PKI称为keystore.bks并且其具有与根CA作为条目
    3. 的PKI其他truststore.bks具有别名客户端是用于你如果PKI可以检索
    4. 的truststore.bks BLE检查包含rootCA.cer在res加入
    5. 两个密钥库/ Android的



    的Eclipse中的原料对于在Android中处理自签名证书我尝试使用几个示例,但是EasySSLSocketFactory和EasySSLTrustManager从StackOverflow:self-signed-ssl-acceptance-android worked for the most part.我也尝试使用默认keytool创建密钥库,但是这会导致更多不正确的密钥库被创建。




    更新2011-03-17:系统信息
    系统操作系统承载IIS是Windows XP中使用IIS-5与.NET 4.0。 IIS中的服务将serverCA.cer指定为服务器证书,并且启用了要求的客户端证书。

    我正在使用的Android版本是2.3.3和Eclipse,并且已经设置了Internet的权限,并且在Eclipse项目中添加了作为原始资源的密钥库和信任库。

    此外,当我在调试模式下查找什么KeyManagerFactory.getKeyManagers()返回,我看到列表中有一个项目。


    这里是动作/代码,我这个问题的详细资讯:

    该证书用makecert创建的,因为它首先有一个WCF服务和客户端之间的合作。

    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 
    



    的WCF配置是如下:

    <?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> 
    



    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(); 
        } 
    }



    调用WCF Android的方法的目的服务如下WED

    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; 
        } 
    } 
    



    以下部分用于通过所述方法testSSLDataTransfer检索客户端证书密钥库和信任库

    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; 
    } 
    



    的EasySSLSocketFactory已稍作修改,以便代码如下所示:

    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(); 
        } 
    }
  • 回答

    2

    Android不支持的客户端证书直到将来的版本。 要在应用程序中拥有客户端证书,必须实现自己的代码以进行发送。

    +0

    这仍然是这种情况? – LamonteCristo 2012-12-12 17:49:37

    +0

    @ makerofthings7 不,这不再是这种情况(事实上,我不确定是否有这种情况) 虽然不是微不足道的,但客户端证书认证到WCF服务是可能的。看到这个问题:http://stackoverflow.com/questions/24476591 – Felix 2014-08-26 18:18:22

    相关问题