7

サーバーに接続するアプリケーションを作成したいと思います。このサーバーはSSLクライアント認証を使用します。アプリケーションのユーザーは証明書を選択し、ブラウザアプリケーションに実装されているように証明書の使用を許可する必要があります。Android 4.xでのSSLクライアント認証

ブラウザアプリケーションでは、認証が正常に機能しているため、使用する証明書が有効です。

私は私のアプリに接続しようとすると、私は次のエラーを取得する:

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: 
SSL handshake terminated: ssl=0x2a2d3b38: 
Failure in SSL library, usually a protocol error 
error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure 
(external/openssl/ssl/s3_pkt.c:1290 0x2a2df880:0x00000003) 

私は私の実装のためのアンドロイドドキュメントに従うことを試みました。ここで

私のサンプルアクティビティのコードです:

public class ClientCertificateActivity extends Activity implements 
    KeyChainAliasCallback { 

protected static final String TAG = "CERT_TEST"; 
private String alias; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    choseCertificate(); 
    LinearLayout layout = new LinearLayout(this); 
    Button connectToServer = new Button(this); 
    connectToServer.setText("Try to connect to Server"); 
    connectToServer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); 
    connectToServer.setOnClickListener(new OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      connectToServer(); 
     } 
    }); 
    layout.addView(connectToServer); 
    addContentView(layout, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); 
} 

protected void connectToServer() { 
    final Context ctx = this; 
    new AsyncTask<Void, Void, Boolean>() { 

     private Exception error; 

     @Override 
     protected Boolean doInBackground(Void... arg) { 
      try { 
       PrivateKey pk = KeyChain.getPrivateKey(ctx, alias); 
       X509Certificate[] chain = KeyChain.getCertificateChain(ctx, 
         alias); 

       KeyStore keyStore = KeyStore.getInstance("AndroidCAStore"); 
       TrustManagerFactory tmf = TrustManagerFactory 
         .getInstance(TrustManagerFactory 
           .getDefaultAlgorithm()); 
       tmf.init(keyStore); 

       SSLContext context = SSLContext.getInstance("TLS"); 
       context.init(null, tmf.getTrustManagers(), null); 

       URL url = new URL("https://usecert.example.com/"); 
       HttpsURLConnection urlConnection = (HttpsURLConnection) url 
         .openConnection(); 
       urlConnection.setSSLSocketFactory(context 
         .getSocketFactory()); 
       InputStream in = urlConnection.getInputStream(); 

       return true; 
      } catch (Exception e) { 
       e.printStackTrace(); 
       error = e; 
       return false; 
      } 
     } 

     @Override 
     protected void onPostExecute(Boolean valid) { 
      if (error != null) { 
       Toast.makeText(ctx, "Error: " + error.getMessage(), 
         Toast.LENGTH_LONG).show(); 
       return; 
      } 
      Toast.makeText(ctx, "Success: ", Toast.LENGTH_SHORT).show(); 
     } 
    }.execute(); 

} 

protected void choseCertificate() { 
    KeyChain.choosePrivateKeyAlias(this, this, 
      new String[] { "RSA", "DSA" }, null, "m.ergon.ch", 443, null); 
} 

@Override 
public void alias(String alias) { 
    this.alias = alias; 
} 
} 

例外がここurlConnection.getInputStream();

でスローされますが、握手のキャプチャですサーバーとcの間リュート。 Network capture of the SSL Handshake

ご意見ありがとうございます。

答えて

4

あなたの秘密鍵でKeyManagerを初期化することは決してないので、クライアント認証がそれを受け取る方法はありません。

X509KeyManagerを実装して、PrivateKeyとハードコードされたエイリアスを返す必要があります。 参考のために在庫のある電子メールアプリケーション(ICS +)の1つです。基本的には、キー、エイリアス、および証明書チェーンをフィールドに保存し、適切なメソッド(StubKeyManagerは、実装されていないメソッドと不要なメソッドの例外をスローするだけです)を返すだけです。

public static class KeyChainKeyManager extends StubKeyManager { 
    private final String mClientAlias; 
    private final X509Certificate[] mCertificateChain; 
    private final PrivateKey mPrivateKey; 

    public static KeyChainKeyManager fromAlias(Context context, String alias) 
      throws CertificateException { 
     X509Certificate[] certificateChain; 
     try { 
      certificateChain = KeyChain.getCertificateChain(context, alias); 
     } catch (KeyChainException e) { 
      logError(alias, "certificate chain", e); 
      throw new CertificateException(e); 
     } catch (InterruptedException e) { 
      logError(alias, "certificate chain", e); 
      throw new CertificateException(e); 
     } 

     PrivateKey privateKey; 
     try { 
      privateKey = KeyChain.getPrivateKey(context, alias); 
     } catch (KeyChainException e) { 
      logError(alias, "private key", e); 
      throw new CertificateException(e); 
     } catch (InterruptedException e) { 
      logError(alias, "private key", e); 
      throw new CertificateException(e); 
     } 

     if (certificateChain == null || privateKey == null) { 
      throw new CertificateException("Can't access certificate from keystore"); 
     } 

     return new KeyChainKeyManager(alias, certificateChain, privateKey); 
    } 

    private KeyChainKeyManager(
      String clientAlias, X509Certificate[] certificateChain, 
      PrivateKey privateKey) { 
     mClientAlias = clientAlias; 
     mCertificateChain = certificateChain; 
     mPrivateKey = privateKey; 
    } 


    @Override 
    public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { 
     return mClientAlias; 
    } 

    @Override 
    public X509Certificate[] getCertificateChain(String alias) { 
      return mCertificateChain; 
    } 

    @Override 
    public PrivateKey getPrivateKey(String alias) { 
      return mPrivateKey; 
    } 
} 
+0

そのおかげでありがとう。しかし私の秘密鍵でこのようなKeyManagerを作成するにはどうすればよいですか?この部分は[Android Doc for HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)の例にはありません – surffan

+0

直接行う方法はないようですこの。 'PrivateKey'といくつかのハードコードされたエイリアスを返すために' X509KeyManager'を実装する必要があります。サーバー関連のメソッドを実装する必要はありません。 http://developer.android.com/reference/javax/net/ssl/X509KeyManager.html –

+0

サンプルコードの更新された回答をご覧ください。 –

関連する問題