Je crée une application Android qui utilise https
pour la communication avec le serveur. J'utilise retrofit
et OkHttp
pour effectuer des requêtes. fonctionne bien pour les requêtes standard http
. Voici les étapes que j'ai suivies.
Étape 1: a acquis le fichier de certification du serveur à l'aide de la commande
echo -n | openssl s_client -connect api.****.tk:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > gtux.cert
Étape 2: a converti le certificat au format BKS à l'aide des commandes suivantes.
keytool -importcert -v -trustcacerts -file "gtux.cert" -alias imeto_alias -keystore "my_keystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-146.jar" -storetype BKS
Il m'a demandé un mot de passe et le fichier a été créé avec succès.
Étape 3:
Créer un OkHttpClient et utiliser le même pour faire des requêtes https
public class MySSLTrust {
public static OkHttpClient trustcert(Context context){
OkHttpClient okHttpClient = new OkHttpClient();
try {
KeyStore ksTrust = KeyStore.getInstance("BKS");
InputStream instream = context.getResources().openRawResource(R.raw.my_keystore);
ksTrust.load(instream, "secret".toCharArray());
// TrustManager decides which certificate authorities to use.
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ksTrust);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) {
e.printStackTrace();
}
return okHttpClient;
}
}
Étape 4:
RestAdapter doit être créé
RestAdapter.Builder()
.setRequestInterceptor(intercept)
.setEndpoint("https://api.****.tk")
.setClient(new OkClient(this))
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new AndroidLog("RETROFIT"))
.build();
Mais finalement, quand je lance l'application, elle me jette CertPathValidatorException : Trust anchor for certificate path not found
. S'il vous plaît aidez-moi à résoudre ce problème. Je vous remercie.
Autres tentatives d'échec: J'ai essayé d'installer le certificat dans mon Xperia Z2 et il indique que le fichier a été installé, mais que lorsque j'exécute l'application, la même exception est levée.
Journal des erreurs Voici le journal des erreurs que j'ai obtenu lors de l'exécution ...
Collé là pour qu'il soit facile à lire.
AVERTISSEMENT: cette réponse provient de juil 2015 et utilise Retrofit et OkHttp à partir de ce moment.
Cochez ce lien pour plus d’informations sur Retrofit v2 et celui-ci pour les méthodes OkHttp actuelles.
D'accord, je l'ai obtenu en utilisant Guide du développeur Android .
Tout comme OP, j'essaie d'utiliser Retrofit et OkHttp pour connectez-vous à un serveur SSL auto-signé.
Voici le code qui fait fonctionner les choses (j'ai supprimé les blocs try/catch):
public static RestAdapter createAdapter(Context context) {
// loading CAs from an InputStream
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream cert = context.getResources().openRawResource(R.raw.my_cert);
Certificate ca;
try {
ca = cf.generateCertificate(cert);
} finally { cert.close(); }
// creating a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// creating a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// creating an SSLSocketFactory that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
// creating an OkHttpClient that uses our SSLSocketFactory
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
// creating a RestAdapter that uses this custom client
return new RestAdapter.Builder()
.setEndpoint(UrlRepository.API_BASE)
.setClient(new OkClient(okHttpClient))
.build();
}
Pour aider au débogage, j'ai également ajouté .setLogLevel(RestAdapter.LogLevel.FULL)
à mes commandes de création RestAdapter et je pouvais le voir se connecter et obtenir la réponse du serveur.
Tout ce que cela a pris a été mon fichier original . Crt enregistré dans main/res/raw
. Le fichier . Crt , également appelé certificat, est l'un des deux fichiers créés lors de la création d'un certificat à l'aide de openssl
. Généralement, il s'agit d'un fichier .crt ou .cert, tandis que l'autre est un fichier .key.
Après tout, le fichier .crt est votre clé publique et le fichier .key est votre clé privée.
Comme je peux le voir, vous avez déjà un fichier . Cert , qui est identique, essayez donc de l’utiliser.
PS: Pour ceux qui le liront plus tard et qui n’ont qu’un . Pem fichier, selon cette réponse , vous avez seulement besoin de cela pour convertir l'un en l'autre:
openssl x509 -outform der -in your-cert.pem -out your-cert.crt
PS²: Pour ceux qui ne possèdent aucun fichier, vous pouvez utiliser la commande suivante (bash) pour extraire la clé publique (ou certificat). depuis n'importe quel serveur:
echo -n | openssl s_client -connect your.server.com:443 | \
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/my_cert.crt
Il suffit de remplacer le your.server.com
Et le port (s'il ne s'agit pas du protocole HTTPS standard) et de choisir un chemin d'accès valide pour la création de votre fichier de sortie.
Use the below code to solve the CertPathValidatorException issue.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(YOUR_BASE_URL)
.client(getUnsafeOkHttpClient().build())
.build();
public static OkHttpClient.Builder getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(Java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(Java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new Java.security.cert.X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new Java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return builder;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Pour plus de détails, visitez https://mobikul.com/Android-retrofit-handling-sslhandshakeexception/
Je n'utilise pas Retrofit et pour OkHttp , voici la seule solution de certificat auto-signé qui a fonctionné pour moi:
Obtenez un certificat de notre site comme dans la question de Gowtham et mettez-le dans res/raw dir du projet:
echo -n | openssl s_client -connect elkews.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ./res/raw/elkews_cert.crt
Utilisez Paulo answer pour définir la fabrique ssl (de nos jours, utilisez OkHttpClient.Builder ()) mais sans RestAdapter création.
Ajoutez ensuite le suivant solution à corriger: SSLPeerUnverifiedException: nom d'hôte non vérifié
Ainsi, la fin du code de Paulo (après l'initialisation sslContext) qui fonctionne pour moi ressemble à ce qui suit:
...
OkHttpClient.Builder builder = new OkHttpClient.Builder().sslSocketFactory(sslContext.getSocketFactory());
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return "secure.elkews.com".equalsIgnoreCase(hostname);
});
OkHttpClient okHttpClient = builder.build();
Retrofit 2.3.
// Load CAs from an InputStream
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
InputStream inputStream = context.getResources().openRawResource(R.raw.ssl_certificate); //(.crt)
Certificate certificate = certificateFactory.generateCertificate(inputStream);
inputStream.close();
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", certificate);
// Create a TrustManager that trusts the CAs in our KeyStore.
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
X509TrustManager x509TrustManager = (X509TrustManager) trustManagers[0];
// Create an SSLSocketFactory that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{x509TrustManager}, null);
sslSocketFactory = sslContext.getSocketFactory();
//create Okhttp client
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory,x509TrustManager)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
Vous convertissez cert en BKS Keystore, pourquoi n'utilisez-vous pas .cert
_ directement, depuis https://developer.Android.com/training/articles/security-ssl.html :
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream instream = context.getResources().openRawResource(R.raw.gtux_cert);
Certificate ca;
try {
ca = cf.generateCertificate(instream);
} finally {
caInput.close();
}
KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
kStore.load(null, null);
kStore.setCertificateEntry("ca", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm(););
tmf.init(kStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
okHttpClient.setSslSocketFactory(context.getSocketFactory());
Si vous avez un certificat, vous pouvez le fournir, mais peu de services Web n’auront pas ce certificat, veuillez suivre les instructions ci-dessous.
// creating a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
// creating a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// creating an SSLSocketFactory that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
// creating a RestAdapter using the custom client
return new RestAdapter.Builder()
.setEndpoint(UrlRepository.API_BASE)
.setClient(new OkClient(okHttpClient))
.build();
Voici la version Kotlin.
Merci :)
fun unSafeOkHttpClient() :OkHttpClient.Builder {
val okHttpClient = OkHttpClient.Builder()
try {
// Create a trust manager that does not validate certificate chains
val trustAllCerts: Array<TrustManager> = arrayOf(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?){}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
})
// Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory
if (trustAllCerts.isNotEmpty() && trustAllCerts.first() is X509TrustManager) {
okHttpClient.sslSocketFactory(sslSocketFactory, trustAllCerts.first() as X509TrustManager)
okHttpClient.hostnameVerifier { _, _ -> true }
}
return okHttpClient
} catch (e: Exception) {
return okHttpClient
}
}
Après une longue recherche et des recherches trop approfondies, j’ai trouvé la solution du certificat: Android et oui, c’est différent d’iOS où nous avons besoin d’un certificat mais Android nous avons juste besoin d'une broche de hachage et c'est tout.
Comment obtenir une broche de hachage pour un certificat?
Initialement, utilisez simplement une mauvaise goupille de hachage et votre classe Java émettra une erreur avec les broches de hachage ou la chaîne de broches correctes, copiez-les simplement et collez-les dans votre code.
Cette solution a résolu mon problème: https://stackoverflow.com/a/45853669/34480
Mise en œuvre dans Kotlin: Retrofit 2.3.0
private fun getUnsafeOkHttpClient(mContext: Context) :
OkHttpClient.Builder? {
var mCertificateFactory : CertificateFactory =
CertificateFactory.getInstance("X.509")
var mInputStream = mContext.resources.openRawResource(R.raw.cert)
var mCertificate : Certificate = mCertificateFactory.generateCertificate(mInputStream)
mInputStream.close()
val mKeyStoreType = KeyStore.getDefaultType()
val mKeyStore = KeyStore.getInstance(mKeyStoreType)
mKeyStore.load(null, null)
mKeyStore.setCertificateEntry("ca", mCertificate)
val mTmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val mTrustManagerFactory = TrustManagerFactory.getInstance(mTmfAlgorithm)
mTrustManagerFactory.init(mKeyStore)
val mTrustManagers = mTrustManagerFactory.trustManagers
val mSslContext = SSLContext.getInstance("SSL")
mSslContext.init(null, mTrustManagers, null)
val mSslSocketFactory = mSslContext.socketFactory
val builder = OkHttpClient.Builder()
builder.sslSocketFactory(mSslSocketFactory, mTrustManagers[0] as X509TrustManager)
builder.hostnameVerifier { _, _ -> true }
return builder
}