Android:oauth.signpost.exception.OAuthCommunicationException:与服务提供商的通信失败:没有对等证书

Android:oauth.signpost.exception.OAuthCommunicationException:与服务提供商的通信失败:没有对等证书,android,ssl,oauth,ssl-certificate,httpclient,Android,Ssl,Oauth,Ssl Certificate,Httpclient,我一直在尝试学习如何使用oAuth 1.0a将android应用程序验证到服务器上(我知道它已经过时,但这正是我被迫使用的),并且我一直在学习如何将应用程序连接到oAuth 1.0a的教程 然而,在编写了他们建议的活动之后,我遇到了如下错误: oauth.signpost.exception.OAuthCommunicationException: Communication with the service provider failed: No peer certificate

我一直在尝试学习如何使用oAuth 1.0a将android应用程序验证到服务器上(我知道它已经过时,但这正是我被迫使用的),并且我一直在学习如何将应用程序连接到oAuth 1.0a的教程

然而,在编写了他们建议的活动之后,我遇到了如下错误:

oauth.signpost.exception.OAuthCommunicationException: Communication with the service provider failed: No peer certificate
        at oauth.signpost.AbstractOAuthProvider.retrieveToken(AbstractOAuthProvider.java:218)
        at oauth.signpost.AbstractOAuthProvider.retrieveRequestToken(AbstractOAuthProvider.java:74)
        at com.company.companyone.account.PrepareRequestTokenActivity$OAuthRequestTokenTask.doInBackground(PrepareRequestTokenActivity.java:141)
        at com.company.companyone.account.PrepareRequestTokenActivity$OAuthRequestTokenTask.doInBackground(PrepareRequestTokenActivity.java:122)
        at android.os.AsyncTask$2.call(AsyncTask.java:287)
        at java.util.concurrent.FutureTask.run(FutureTask.java:234)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
        at java.lang.Thread.run(Thread.java:856)
原因:javax.net.ssl.SSLPeerUnverifiedException:无对等证书

我写的活动如下:

public class PrepareRequestTokenActivity extends Activity {

private CommonsHttpOAuthConsumer consumer;
private CommonsHttpOAuthProvider provider;
private final static String TAG = "PrepareRequestTokenActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_prepare_request_token);
    try {

        System.setProperty("debug", "true");

        consumer = new CommonsHttpOAuthConsumer(Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);



        provider = new CommonsHttpOAuthProvider(
                Constants.REQUEST_URL,
                Constants.ACCESS_URL,
                Constants.AUTHORIZE_URL);



        provider.setOAuth10a(true);

    } catch (Exception e) {

        Log.d(TAG, "Error intializing consumer and provider", e);
        e.printStackTrace();

    }


    Log.i(TAG, "Starting task to retrieve request token.");

    new OAuthRequestTokenTask(this,consumer,provider).execute();

}

@Override
protected void onResume(){
    super.onResume();
    //need to get the preferences to store response tokens later.
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
    final Uri uri = getIntent().getData();

    //make sure that the callback worked correctly
    if(uri != null && uri.getScheme().equals(Constants.OAUTH_CALLBACK_SCHEME)){
        Log.i(TAG, "Got Callback : uri = " +uri);
        Log.i(TAG, "Attempting to Retrieve Access Token");

        new RetrieveAccessTokenTask(this,consumer,provider,prefs).execute(uri);
        //done
        finish();
    }else{
        Log.d(TAG, "Fatal Error: oAuth Callback malformed.");
    }


}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_prepare_request_token, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

private class OAuthRequestTokenTask extends AsyncTask<String, Void, String>{

    private CommonsHttpOAuthConsumer reqConsumer;
    private CommonsHttpOAuthProvider reqProvider;
    private Context context;

    public OAuthRequestTokenTask(PrepareRequestTokenActivity prepareRequestTokenActivity, CommonsHttpOAuthConsumer reqConsumer, CommonsHttpOAuthProvider reqProvider){
        this.context = prepareRequestTokenActivity.getApplicationContext();
        this.reqConsumer = reqConsumer;
        this.reqProvider = reqProvider;



    }

    @Override
    protected String doInBackground(String... strings) {
        try {
            Log.i(TAG, "Retrieving request token from Magento servers");

            final String url = reqProvider.retrieveRequestToken(reqConsumer, Constants.OAUTH_CALLBACK_URL);
            Log.i(TAG, "Popping a browser with the authorize URL : " + url);
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_FROM_BACKGROUND);
            context.startActivity(intent);

        } catch (Exception e) {
            Log.e(TAG, "Error during OAUth retrieve request token", e);
            e.printStackTrace();
        }
        return null;
    }
}

private class RetrieveAccessTokenTask extends AsyncTask<Uri, Void, Void> {

    private Context context;
    private CommonsHttpOAuthConsumer consumer;
    private CommonsHttpOAuthProvider provider;
    private SharedPreferences prefs;

    //collect the instance variables for the Access Token request
    public RetrieveAccessTokenTask(PrepareRequestTokenActivity prepareRequestTokenActivity, CommonsHttpOAuthConsumer consumer, CommonsHttpOAuthProvider provider, SharedPreferences prefs) {
        this.context = prepareRequestTokenActivity.getApplicationContext();
        this.consumer = consumer;
        this.provider = provider;
        this.prefs = prefs;
    }


    //called by .execute()
    //takes Verifier Token and uses it to retrieve the Access Token by validating this instance of the app.
    @Override
    protected Void doInBackground(Uri... params) {
        final Uri oauth_uri = params[0];

        final String verifier = oauth_uri.getQueryParameter(OAuth.OAUTH_VERIFIER);

        try{
            //set the provider to do it's ish.
            provider.retrieveAccessToken(consumer,verifier);

            //save the token in the shared preferences so that we can use it in our request methods.
            //MAKE SURE YOU LEAVE THIS AS COMMIT. We want this to persist in memory, and apply doesn't guarantee that the token will remain if the app closes immediately.
            final SharedPreferences.Editor edit = prefs.edit();
            edit.putString(OAuth.OAUTH_TOKEN, consumer.getToken());
            edit.putString(OAuth.OAUTH_TOKEN_SECRET, consumer.getTokenSecret());
            edit.commit();

            String token = prefs.getString(OAuth.OAUTH_TOKEN,"");
            String secret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET,"");

            consumer.setTokenWithSecret(token,secret);

            Toast.makeText(context, "Token Success! += "+ token, Toast.LENGTH_LONG);


            //kick it back to the mainactivity.
            context.startActivity(new Intent(context, MainActivity.class));

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
这是抛出错误的行

final String url = reqProvider.retrieveRequestToken(reqConsumer, Constants.OAUTH_CALLBACK_URL);
我将错误跟踪到CommonHttpOAuthProvider.sendRequest()方法。它在以下时间中断:

HttpResponse response = httpClient.execute((HttpUriRequest) request.unwrap());
在执行request.unwrap()调用之前


TL;DR:我做错什么了吗?Android不喜欢自签名SSL证书吗?

简短回答:是的,SSL证书导致了问题。下面是自签名和无序证书的解决方案

注意:我很确定这不会影响安全性,但我也不确定SSL证书的顺序是否非常重要。所以把这个和盐一起吃。我肯定计划在live环境启动并运行后删除此代码,因此我不建议将此代码保留在应用程序中,除非您的环境完全受控,并且您将连接到的所有主机都已知

步骤1:出现故障的证书。 需要定义一个自定义TrustManager来为您处理坏证书。 (我在somwhere网上找到了此解决方案的参考,但现在找不到链接)

步骤3:实现一个新的HTTPClient,它使用我们刚刚创建的自定义SSLSocketFactory。在定义https连接的HTTPClient时使用此选项

public HttpClient getNewHttpClient(){
    try{
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
        //sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(),80));
        registry.register(new Scheme("https", sf,443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params,registry);

        return new DefaultHttpClient(ccm, params);

    } catch (Exception e) {
        e.printStackTrace();
        //if we get an error here then we need to return something or a fatal network error will occur.
        return new DefaultHttpClient();
    }
}
/**
 * Created by Nevuroth on 1/19/15.
 */
public class CustomX509TrustManager implements X509TrustManager{

private X509TrustManager defaultManager = null;

/**
 * Custom constructor for the x509 trust manager. This workaround won't take away from security, but it will drop and accept the self signed cert for our test server at the
 * end of the cert chain, as well as allowing
 *
 * @param keyStore
 * @throws NoSuchAlgorithmException
 * @throws KeyStoreException
 */
public CustomX509TrustManager(KeyStore keyStore) throws NoSuchAlgorithmException, KeyStoreException{
    super();
    TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    factory.init(keyStore);
    TrustManager[] trustManagers = factory.getTrustManagers();
    if(trustManagers.length ==0){
        throw new NoSuchAlgorithmException("Failed to find Default trust managers");
    }

    this.defaultManager = (X509TrustManager) trustManagers[0];

}


//we just want the standard functionality for x509
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    defaultManager.checkClientTrusted(x509Certificates, s);
}

/**
 *  Here's where the magic happens, we're going to be compensating for out of order certificates in the X509 header
 *  as well as compensating for self-signed certificates (kind of), by passing the certificate before it in the chain
 *  to the chekc servertrusted method
 *  This won't compensate for purely self-signed certs.... but you can do so by adding it to the accepted issuers method.
 * @param x509Certificates
 * @param s
 * @throws CertificateException
 */

@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

    //Clean the certificates and make sure they are in the proper order.
    int chainln = x509Certificates.length;
    if(x509Certificates.length > 1){
        //Clean the chains by matching issuer and subject fields until we can't continue
        int index;
        boolean foundNext;
        for(index=0; index < x509Certificates.length; ++index){
            foundNext = false;
            for(int nextIndex = index + 1; nextIndex < x509Certificates.length; ++nextIndex){
                //look for the next certificate in the chain.
                if(x509Certificates[index].getIssuerDN().equals(x509Certificates[nextIndex].getSubjectDN())){
                    foundNext = true;
                    //exchange certificates so that 0 through index+1 are in proper order.
                    if(nextIndex != index+1){
                        X509Certificate tempCert = x509Certificates[nextIndex];
                        x509Certificates[nextIndex] = x509Certificates[index+1];
                        x509Certificates[index+1] = tempCert;
                    }
                    break;
                }

            }

            if(!foundNext){
                break;
            }

        }

        //if the cert is self signed and if it is expired, if so we drop it and pass the rest to checkServerTrusted, hoping we may have a similar bu unexpired trusted root.
        chainln = index +1;
        X509Certificate lastCert = x509Certificates[chainln - 1];
        Date now = new Date();
        if(lastCert.getSubjectDN().equals(lastCert.getIssuerDN()) && now.after(lastCert.getNotAfter())){
            --chainln;
        }
    }

    defaultManager.checkServerTrusted(x509Certificates, s);

}

//you can add an accepted issuer
@Override
public X509Certificate[] getAcceptedIssuers() {
    return this.defaultManager.getAcceptedIssuers();
}
}
/**
 * Created by Nevuroth on 1/19/15.
 */
    public class CustomSSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");


public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, UnrecoverableKeyException, KeyStoreException {
    super(truststore);

    TrustManager tm = new CustomX509TrustManager(truststore);

    sslContext.init(null, new TrustManager[]{tm}, null);

}

@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
    return sslContext.getSocketFactory().createSocket(socket, host, port,autoClose);
}

@Override
public Socket createSocket() throws IOException{
    return sslContext.getSocketFactory().createSocket();
}
}
public HttpClient getNewHttpClient(){
    try{
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
        //sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(),80));
        registry.register(new Scheme("https", sf,443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params,registry);

        return new DefaultHttpClient(ccm, params);

    } catch (Exception e) {
        e.printStackTrace();
        //if we get an error here then we need to return something or a fatal network error will occur.
        return new DefaultHttpClient();
    }
}