Клиентский сертификат не работает с Android - Как отлаживать?
Я пытаюсь реализовать обмен клиентскими сертификатами для приложения Android, но пока без особого успеха - и кажется, что эта функция, если вообще возможно, очень сложна. Полный поток, который я реализую, описан в моем предыдущем вопросе.
Я следовал там коду и коду из этого поста в блоге, описывая тот же сценарий, более или менее, безрезультатно.
Что не работает: открытие SSL-соединения (HttpsURLConnection
) между клиентом Android и сервером заставляет сервер возвращать код состояния 403.
AFAIK, это 403, потому что сервер не получает или не доверяет клиентскому сертификату, который он получает, и я не уверен, как его отладить.
Что работает:
- Создание запроса PKCS#10, отправка его в CA и получение подписанного PKCS # 7 (P7B)
- Сохранение полученного P7B с закрытым ключом в хранилище ключей и его экспорт в PKCS # 12 (P12)
- (Самое неприятное): выбрать P12 с устройства, установить его в Windows, связаться с сервером и получить согласованный (200 HTTP-OK) ответ.
Что я изменил: из примеров кода, которые я получил ( отсюда и здесь), мне пришлось изменить несколько вещей. Я использую HttpsURLConnection, а не OkHttpClient, так как там использовался @Than (но это не должно иметь значения), я не могу предоставить сертификаты, как это сделал Rich Freedman (у него был сертификат, и я получаю его через PKCS#10 и #7), поэтому я создал CustomTrustManager, который будет доверять сертификату сервера, и по этой причине я использую SpongyCastle (v1.5.0.0, если это важно, установите в качестве поставщика, вставленного в 0), а также не сохраняю сертификат, но все сделано в памяти.
Вопрос в том, что делать дальше:
- Как я могу сказать, что ожидает сервер (клиент-сертификат мудрый)?
- Как я могу узнать, какие клиентские сертификаты (если таковые имеются) отправляются на сервер?
- Как отладить этот сценарий в целом? (Прокси, такие как Fiddler, бесполезны для базового SSL)
Спасибо!
1 ответ
Это не очень хороший ответ, но здесь слишком много информации, чтобы опубликовать его в качестве комментария.
Для ведения журнала, отладки вы можете создать свой собственный X509KeyManager
который использует обычный менеджер ключей, полученный из KeyManagerFactory
:
@DebugLog
аннотация взята из библиотеки Хьюго, созданной Джейком Уортоном. Он печатает аргументы функции и что она возвращает. Вы можете использовать обычный Log.d или что угодно.
например:
class MyKeyManager implements X509KeyManager {
private final X509KeyManager keyManager;
MyKeyManager(X509KeyManager keyManager) {
this.keyManager = keyManager;
}
@DebugLog
@Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return this.keyManager.chooseClientAlias(strings, principals, socket);
}
@DebugLog
@Override
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
return keyManager.chooseServerAlias(s, principals, socket);
}
@DebugLog
@Override
public X509Certificate[] getCertificateChain(String s) {
return keyManager.getCertificateChain(s);
}
@DebugLog
@Override
public String[] getClientAliases(String s, Principal[] principals) {
return keyManager.getClientAliases(s, principals);
}
@DebugLog
@Override
public String[] getServerAliases(String s, Principal[] principals) {
return keyManager.getServerAliases(s, principals);
}
@DebugLog
@Override
public PrivateKey getPrivateKey(String s) {
return keyManager.getPrivateKey(s);
}
}
И использовать его для инициации SSLContext
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, password);
final X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0];
X509KeyManager km = new MyKeyManager(origKm);
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(new KeyManager[]{km}, tmf.getTrustManagers(), null);
Вы увидите, какой метод вызывается, какие аргументы (полученные из сертификата serwer), а также какой сертификат и закрытый ключ возвращает ваш менеджер ключей.