Android 使用自签名客户端证书身份验证连接到 IIS 中的 WCF Rest 服务

发布于 2024-10-23 18:53:40 字数 16592 浏览 1 评论 0原文

您好,

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

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

对自签名证书执行了以下步骤

  1. 创建了 rootCA.cer,其中包含 serverCA.cer 和 clientCA.cer 作为子证书
  2. 已从 clientCA.cer 创建了私钥信息
  3. 使用 Portecle 在 BKS 中创建了两个密钥库格式,一个包含 clientCA 的 PKI,称为 keystore.bks,另一个 truststore.bks 以 rootCA 作为条目。
  4. PKI 具有别名客户端,用于仔细检查是否可以检索 PKI。
  5. truststore.bks 包含 rootCA。 cer
  6. 两个密钥库都添加到 Android Eclipse 的 res/raw 中



对于 Android 中自签名证书的处理,我尝试使用几个示例,但是来自 StackOverflow:self-signed-ssl-acceptance-android 大部分工作。 我还尝试使用创建密钥库默认密钥工具,但这会导致创建更多不正确的密钥库。




2011-03-17更新:系统信息
托管 IIS 的系统操作系统是带有 IIS-5 和 .NET 4.0 的 Windows XP。 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方法如下

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

Greetings,

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'.

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.

The following steps have been undertaken for the self-signed certificates

  1. Created a rootCA.cer with as child certificates a serverCA.cer and clientCA.cer
  2. A Private Key Information has been created from the clientCA.cer
  3. 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
  4. The PKI has the alias client that is used to double check if the PKI can be retrieved
  5. The truststore.bks contains the rootCA.cer
  6. Both keystores were added in the res/raw of Eclipse of Android

For the handling of the self-signed certificates in Android I tried to use several examples however the EasySSLSocketFactory and EasySSLTrustManager fromStackOverflow:self-signed-ssl-acceptance-android worked for the most part. I also tried creating the keystores with the default keytool, however this result into more incorrect keystores being created.

Update 2011-03-17: system information
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.

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.

Additionally when I look up in debug mode what the KeyManagerFactory.getKeyManagers() return I see that there is one item in the list.

Here are details of actions/code that I use with the issue:

The certificates were created with makecert as it first had to work between a WCF service and client.

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

The WCF configured is as followed:

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

The Object of the WCF service

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

The Android method that calls the WCF service is as followed

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

The following part is used by the method testSSLDataTransfer to retrieve the client certificate keystore and the 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;
}

The EasySSLSocketFactory has been modified slightly so that the code looks like:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

心病无药医 2024-10-30 18:53:40

Android 在未来版本之前不支持客户端证书。
要在应用程序中拥有客户端证书,必须自己实现代码才能发送此证书。

Client certificates not supported by Android untill a future version.
To have client certificates in an application one must implement code him/herself to have this send.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文