1

Я искал это в течение нескольких недель и не могу найти ответ нигде. Я пытаюсь сделать следующее для Android. Этот код из приложения C#, которое я написал, но переносил его на Android. Конечная точка веб-сайта требует, чтобы сертификат был прикреплен к запросу о взаимной аутентификации для совершения вызова веб-службы.Сертификаты для Android и клиентов

 string certThumbprint = "E1313F6A2D770783868755D016CE748F6A9B0028"; 
     X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); 
     try 
     { 
      certStore.Open(OpenFlags.ReadOnly); 
     } 
     catch (Exception e) 
     { 
      if (e is CryptographicException) 
      { 
       Console.WriteLine("Error: The store is unreadable."); 
      } 
      else if (e is SecurityException) 
      { 
       Console.WriteLine("Error: You don't have the required permission."); 
      } 
      else if (e is ArgumentException) 
      { 
       Console.WriteLine("Error: Invalid values in the store."); 
      } 
      else 
      { 
       throw; 
      } 
     } 
     X509Certificate2Collection certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, false); 
     certStore.Close(); 
     if (0 == certCollection.Count) 
     { 
      throw new Exception("Error: No certificate found containing thumbprint " + certThumbprint); 
     } 
     X509Certificate2 certificate = certCollection[0]; 
     return certificate; 

Я тогда делал это (запрос является HttpWebRequest):

request.ClientCertificates.Add(cert); 

Это отлично работает в C# однако когда я перехожу на Android я получаю «файл не найден» ошибка на getInputStream(). Вот мой Android код:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); 

     StrictMode.setThreadPolicy(policy); 
     CertificateFactory cf = CertificateFactory.getInstance("X.509"); 

     InputStream caInput = new BufferedInputStream(new FileInputStream("/sdcard/Certificate.pfx")); 
     KeyHelper kh = new KeyHelper(); 
     Certificate ca = kh.GetKey("Password"); 
     String keyStoreType = KeyStore.getDefaultType(); 
     keyStore = KeyStore.getInstance(keyStoreType); 
     keyStore.load(null, null); 
     keyStore.setCertificateEntry("ca", ca); 
     KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
     kmf.init(keyStore, "Password".toCharArray()); 
     SSLContext context = SSLContext.getInstance("TLS"); 
     context.init(kmf.getKeyManagers(),null,new SecureRandom()); 
     HttpsURLConnection urlConnection = 
       (HttpsURLConnection)url.openConnection(); 
     urlConnection.setRequestProperty("x-ms-version",AZURE_REST_VERSION); 
     urlConnection.setDoInput(true); 
     urlConnection.setDoOutput(true); 
     urlConnection.setRequestMethod("GET"); 
     urlConnection.setSSLSocketFactory(context.getSocketFactory()); 
     urlConnection.connect(); 

     InputStream in = new BufferedInputStream(urlConnection.getInputStream()); //<-----Blows up here 

    } catch (KeyStoreException e) { 
     throw new KeyStoreException("Keystore Exception",e); 
    } catch (NoSuchAlgorithmException e) { 
     throw new NoSuchAlgorithmException("Algorithm exception",e); 
    } catch (KeyManagementException e) { 
     throw new KeyManagementException("Key Exception", e); 
    } 

Я пытался поставить скрипач между эмулятором и конечной точкой, и он возвращается с 200. Я думаю, что это потому, что мой сертификат в моем местном частном магазине на моем Dev машине. Есть предположения?

ответ

2

ОК. Я нашел ответ на этот вопрос. Проблема заключается в том, что сам подписанный сертификат нельзя использовать до тех пор, пока он не будет установлен в TrustStore android. Однако TrustStore по умолчанию доступен только для чтения после запуска приложения, поэтому его трудно изменить. Я настраивал собственный собственный хранилище доверия, однако корневые сертификаты не были частью его, поэтому вызовы на любой https потерпят неудачу. Решение пришло из этого блог:

http://nelenkov.blogspot.com/2011/12/using-custom-certificate-trust-store-on.html

Короче говоря, установку пользовательских доверенные сертификаты, который содержит самоподписывающийся сертификат, а затем экспортировать все сертификаты доверия по умолчанию магазина в и импортировать их в пользовательском хранилище доверенного , Затем используйте это хранилище доверия, чтобы настроить SSL-контекст (также необходимо использовать настраиваемое хранилище ключей, так как сертификат клиента также должен быть прикреплен к запросу). Я считаю, что если бы я не разрешал самоподписанные сертификаты, это не было бы большой проблемой, так как корневой сертификат для сертификата клиента существовал бы в TrustStore по умолчанию (или, по крайней мере, я надеюсь, что это будет).

0

Кроме того, вы никогда не должны жёстко пути:

InputStream caInput = new BufferedInputStream(new FileInputStream("/sdcard/Certificate.pfx")); 

Вместо этого используйте:

Environment.getExternalStorageDirectory() 

Посмотрите это: http://developer.android.com/training/basics/data-storage/files.html

Также сертификаты Посмотрите на это: Using a self-signed certificate to create a secure client-server connection in android

+0

Хотя heplful это не практическое решение, поскольку вам нужно добавить хранилище ключей к самому проекту андроида. Это побеждает цель, поскольку, хотя у меня уже есть сертификат, конечный пользователь может загрузить его в приложение, мне нужно создать хранилище ключей и заполнить его сертификатом. После реализации этого кода я теперь получаю ошибку SSLPeerUnverifiedError «no peer certificate». Самоподписанный сертификат существует как на стороне клиента, так и на стороне сервера. Таким образом, поиск продолжается. – AnthonyBCodes

Смежные вопросы