Https through proxy with OkHttp got handshake error - ssl

I'm going to download image with Glide library that needs https and proxy config.
I implemented all anonymous certificates and proxy settings for unsafe client (in my dev environment) but get handshake error. This is my OkHttpClient passed to Glide
val unsafeOkHttpClient: OkHttpClient
get() {
try {
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
#SuppressLint("TrustAllX509TrustManager")
#Throws(CertificateException::class)
override fun checkClientTrusted(
chain: Array<java.security.cert.X509Certificate>,
authType: String
) {
}
#SuppressLint("TrustAllX509TrustManager")
#Throws(CertificateException::class)
override fun checkServerTrusted(
chain: Array<java.security.cert.X509Certificate>,
authType: String
) {
}
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> {
return arrayOf()
}
})
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
val sslSocketFactory = sslContext.socketFactory
val builder = OkHttpClient.Builder()
val proxy = Proxy(
Proxy.Type.HTTP,
InetSocketAddress.createUnresolved(PROXY_URL, PROXY_PORT)
)
builder.proxy(proxy)
builder.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
builder.hostnameVerifier(HostnameVerifier { _, _ -> true })
val connectionSpecs = ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.cipherSuites(
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
).build()
builder.connectionSpecs(listOf(connectionSpecs))
return builder.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
I should mention that ConnectionSpec is get from my server configurations. And always i get this error:
Even i used very simple client but result is same.
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xbe2b3c68: Failure in SSL library, usually a protocol error
error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/tls_record.cc:587 0xbe5d2a88:0x00000001)
error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (external/boringssl/src/ssl/handshake.cc:580 0xd084f543:0x00000000)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.NativeSsl.doHandshake(NativeSsl.java:387)
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:226)
... 23 more
I tried too many ways for example exclude okHttp from glide and use OkHttp itself, downgrade okHttp, upgrade all libs ( Retrofit , Glide ) .I found some posts here but cloud not make it works.
https://github.com/square/okhttp/issues/3787
https://github.com/Microsoft/cpprestsdk/issues/650
UPDATED
As i mentioned all images are open in browser ( with proxy extension) and also i got 200 with Curl like this:
curl --insecure -x http://myProxy:9052 -i https://myimage.png
But i find out that TLS version of main server and proxy server are not same. One uses TLS1.2 and other is TLS1.1. So i'm thinking about may this configuration lead to handshake failure cause my request will do not know to handshake with which version! This is my guess and asked the network admin already : "Why we have two different confines for server and proxy!" I'm waitings for their response. If you have any idea please feel free to add comment or post any answer.

After strugle with many thing from client side, backed team set a valid certificate that make my problem solved.
I mean they did not use self-sigend certificate but they used an invalid certificate! That is why i got hand shake error and in browser we can passed this error by accept responcibility of danger and click proceed button.
So if you see the same problem: Handshake error but you can proceed it in browser with my situation lets chech SSL certificate first to save time!

Related

Ratchet PHP server establishes connection, but Kotlin never receives acknowledgement

I have a ratchet server, that I try to access via Websocket. It is similar to the tutorial: logging when there is a new client or when it receives a message. The Ratchet server reports having successfully established a connection while the Kotlin client does not (the connection event in Kotlin is never fired). I am using the socket-io-java module v.2.0.1. The client shows a timeout after the specified timeout time, gets detached at the server and attaches again after a short while, just as it seems to think, the connection did not properly connect (because of a missing connection response?).
The successful connection confirmation gets reported to the client, if the client is a Websocket-Client in the JS-console of Chrome, but not to my Kotlin app. Even an Android emulator running on the same computer doesn´t get a response (So I think the problem is not wi-fi related).
The connection works fine with JS, completing the full handshake, but with an Android app it only reaches the server, but never the client again.
That´s my server code:
<?php
namespace agroSMS\Websockets;
use Ratchet\ConnectionInterface;
use Ratchet\MessageComponentInterface;
class SocketConnection implements MessageComponentInterface
{
protected \SplObjectStorage $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
error_log("New client attached");
}
function onClose(ConnectionInterface $conn)
{
$this->clients->detach($conn);
error_log("Client detached");
}
function onError(ConnectionInterface $conn, \Exception $e)
{
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
function onMessage(ConnectionInterface $from, $msg)
{
error_log("Received message: $msg");
// TODO: Implement onMessage() method.
}
}
And the script that I run in the terminal:
<?php
use Ratchet\Server\IoServer;
use agroSMS\Websockets\SocketConnection;
use Ratchet\WebSocket\WsServer;
use Ratchet\Http\HttpServer;
require dirname(__DIR__) . '/vendor/autoload.php';
$server = IoServer::factory(
new HttpServer(
new WsServer(
new SocketConnection()
)
)
);
$server->run();
What I run in the browser for tests (returns "Connection established" in Chrome, but for some reason not in the Browser "Brave"):
var conn = new WebSocket('ws://<my-ip>:80');
conn.onopen = function(e) {
console.log("Connection established!");
};
conn.onmessage = function(e) {
console.log(e.data);
};
What my Kotlin-code looks like:
try {
val uri = URI.create("ws://<my-ip>:80")
val options = IO.Options.builder()
.setTimeout(60000)
.setTransports(arrayOf(WebSocket.NAME))
.build()
socket = IO.socket(uri, options)
socket.connect()
.on(Socket.EVENT_CONNECT) {
Log.d(TAG, "[INFO] Connection established")
socket.send(jsonObject)
}
.once(Socket.EVENT_CONNECT_ERROR) {
val itString = gson.toJson(it)
Log.d(TAG, itString)
}
}catch(e : Exception) {
Log.e(TAG, e.toString())
}
After a minute the Kotlin code logs a "timeout"-error, detaches from the server, and attaches again.
When I stop the script on the server, it then gives an error: "connection reset, websocket error" (which makes sense, but why doesn´t he get the connection in the first time?)
I also tried to "just" change the protocol to "wss" in the url, in case it might be the problem, even though my server doesn´t even work with SSL, but this just gave me another error:
[{"cause":{"bytesTransferred":0,"detailMessage":"Read timed out","stackTrace":[],"suppressedExceptions":[]},"detailMessage":"websocket error","stackTrace":[],"suppressedExceptions":[]}]
And the connection isn´t even established at the server. So this try has been more like a down-grade.
I went to the github page of socket.io-java-client to find a solution to my problem there and it turned out, the whole problem was, that I misunderstood a very important concept:
That socket.io uses Websockets doesn´t mean it is compatible with Websockets.
So speaking in clear words:
If you use socket.io at client side, you also need to use it at the server side and vice versa. Since socket.io sends a lot of meta data with its packets, a pure Websocket-server will accept their connection establishment, but his acknowledgement coming back will not be accepted by the socket.io client.
You have to go for either full socket.io or full pure Websockets.

GRPC Okhttp android client channel with self signed ssl certificate

I have a grpc-js server using self signed ssl certificates.
var credentials = grpc.ServerCredentials.createSsl(
fs.readFileSync('./node/grpc/ssl/ca.crt'),
[{
cert_chain: fs.readFileSync('./node/grpc/ssl/server.crt'),
private_key: fs.readFileSync('./node/grpc/ssl/server.key')
}],
true
);
I then tested this setup with a grpc-js client with the following credential setup and this works.
var credentials = grpc.credentials.createSsl(
fs.readFileSync('./node/grpc/ssl/ca.crt'),
fs.readFileSync('./node/grpc/ssl/client.key'),
fs.readFileSync('./node/grpc/ssl/client.crt')
);
I want to replicate this in Android using OkHttpChannelBuilder but it is a bit more complicated. This is what I have so far.
private val mChannel : ManagedChannel
init {
/**
* Server certificate to make it trusted.
*/
val serverCrtFile = applicationContext.resources.openRawResource(R.raw.server)
val serverCertificate: X509Certificate =
CertificateFactory.getInstance("X.509").generateCertificate(serverCrtFile) as X509Certificate
val caKeyStore: KeyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
load(null, null)
setCertificateEntry("server", serverCertificate)
}
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply {
init(caKeyStore)
}
val sslContext = SSLContext.getInstance("TLS").apply {
init(null, trustManagerFactory.trustManagers, null)
}
mChannel = OkHttpChannelBuilder
.forAddress(BuildConfig.GRPC_HOST_ADDRESS, BuildConfig.GRPC_HOST_PORT)
.sslSocketFactory(sslContext.socketFactory)
.keepAliveTime(10, TimeUnit.SECONDS)
.useTransportSecurity()
.keepAliveWithoutCalls(true)
.build()
}
Everything worked before implementing ssl (so using plaintext() on the channel builder).
The error I get now is io.grpc.StatusRuntimeException: UNAVAILABLE: End of stream or IOException.
Can someone please tell me if I am doing something wrong and how I can get a successful connection like between the js server and client.
Looks like the SSL handshake failed on the server side so it will be helpful to get server side detailed logs to see what went wrong.
One possibility is using KeyStore.getInstance. Can you try using "PKCS12"?
KeyStore.getInstance("PKCS12")

Retrofit & okhttp won't work after I turn on internet on phone

Scenario:
Open app without internet, the app will try to do a request, and will fail
Turn on internet connection, and press retry button to trigger internet request
Retrofit & okhttp will always give me HTTP FAILED: java.net.SocketTimeoutException: timeout
Restarting the app with internet enabled from start will make everything work, unless I close it again, and fail a request, from that point on it will give me the same error.
I never had this issue on Java, just on Kotlin.
private val interceptor: Interceptor =
object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var builder = chain.request().newBuilder()
Prefs.token?.let { token ->
builder = builder.addHeader("Authorization", "Bearer $token")
}
return chain.proceed(builder.build())
}
}
private val httpLoggingInterceptor: HttpLoggingInterceptor by lazy {
val interceptor = HttpLoggingInterceptor()
interceptor.level =
if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
interceptor
}
private val httpClient: OkHttpClient by lazy {
OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(interceptor)
.build()
}
val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl("https://api.secret.com/v1/")
.addConverterFactory(GsonConverterFactory.create(gson))
.client(httpClient)
.build()
}
And the service classes look like this
#GET("something")
fun something(): Call<SomeResponse>
I've tried playing around with timeout values, no matter the timeout time, I will get the same error.
Creating a new http client for every request will fix the issue, but I don't think is a good idea.
Your issue looks like OkHttp Bug. If you follow the link, you will find long discussion with many possible solutions.
Following solution works for my project:
Update OkHttp at lest up to 4.3.0.
Set ping interval, for example 1 second
okHttpClientBuilder.pingInterval(1, TimeUnit.SECONDS)
How it works
The root of the issue is that Android OS doesn't provide any way to know that connection isn't active any more. So that for library connection looks like alive, but it's already dead. As a result we get timeout exception on every request. Once we set ping, OkHttp starts sending Ping frames, so that if server doesn't respond library knows that connection is already dead, and it's time to create a new one.
Not recommended solutions, but it should work
Turn-off connection pool
okHttpClientBuilder.connectionPool(new ConnectionPool(0, 1, TimeUnit.NANOSECONDS))
Use Http 1.1
okHttpClientBuilder.protocols(listOf(Protocol.HTTP_1_1))
In both not recommended solutions you just stop reusing already opened connection that makes each request time little bit longer.

Apache Http Client Put Request Error

I'm trying to upload a file using the Apache Http Client's PUT method. The code is as below;
def putFile(resource: String, file: File): (Int, String) = {
val httpClient = new DefaultHttpClient(connManager)
httpClient.getCredentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(un, pw))
val url = address + "/" + resource
val put = new HttpPut(url)
put.setEntity(new FileEntity(file, "application/xml"))
executeHttp(httpClient, put) match {
case Success(answer) => (answer.getStatusLine.getStatusCode, "Successfully uploaded file")
case Failure(e) => {
e.printStackTrace()
(-1, e.getMessage)
}
}
}
When I tried running the method, I get to see the following error:
org.apache.http.NoHttpResponseException: The target server failed to respond
at org.apache.http.impl.conn.DefaultResponseParser.parseHead(DefaultResponseParser.java:101)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:252)
at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:281)
at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:247)
at org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader(AbstractClientConnAdapter.java:219)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:298)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:633)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:454)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
I do not know what has gone wrong? I'm able to do GET requests, but PUT seems not to work! Any clues as to where I should look for?
Look on the server. If GET Works, but PUT does not, then you have to figure out the receiving end.
Also, you may want to write a simple HTML File that has a form with PUT Method in it to rule out your Java Part.
As a sidenode: Its technically possible that something in between stops the request from going through or the response reaching you. Best setup a dummy HTTP Server to do the testing against.
Maybe its also a timeout issue, so the server takes to long to process your PUT.
The connection you are trying to use is a stale connection and therefore the request is failing.
But why are you only seeing an error for the PUT request and you are not seeing it for the GET request?
If you check the DefaultHttpRequestRetryHandler class you will see that by default HttpClient attempts to automatically recover from I/O exceptions. The default auto-recovery mechanism is limited to just a few exceptions that are known to be safe.
HttpClient will make no attempt to recover from any logical or HTTP protocol errors (those derived from HttpException class).
HttpClient will automatically retry those methods that are assumed to be idempotent. Your GET request, but not your PUT request!!
HttpClient will automatically retry those methods that fail with a transport exception while the HTTP request is still being transmitted to the target server (i.e. the request has not been fully transmitted to the server).
This is why you don't notice any error with your GET request, because the retry mechanism handles it.
You should define a CustomHttpRequestRetryHandler extending the DefaultHttpRequestRetryHandler. Something like this:
public class CustomHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler {
#Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
if(exception instanceof NoHttpResponseException) {
return true;
}
return super.retryRequest(exception, executionCount, context);
}
}
Then just assign your CustomHttpRequestRetryHandler
final HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setRetryHandler(new CustomHttpRequestRetryHandler());
And that's it, now your PUT request is handled by your new RetryHandler (like the GET was by the default one)

accesing a https site using firefox addon

I am using a self signed ssl certificate to set up a https site and using the request package to access the contents on this site. However the program seems to get stuck and it is not printing the contents of the site. Is there any way to overcome this issue.
Warning: This should only be used for debugging. Automatically adding an override for a wrong SSL certificate compromises the entire connection - if you do that then you can just skip using SSL in the first place. When you release this extension for other people you should use a valid certificate.
You probably want to add a certificate override manually. That's something you would use nsICertOverrideService.rememberValidityOverride() for (chrome authority required). The only problem is getting the certificate that you want to add an override for. But trying to contact the server and calling nsIRecentBadCertsService.getRecentBadCert() then should do. Something like this:
var Request = require("request").Request;
var host = "example.com";
var port = "443";
Request({
url: "https://" + host + ":" + port + "/foo",
onComplete: function(response)
{
var status = null;
try
{
status = response.status;
} catch(e) {}
if (!status)
{
// There was a connection error, probably a bad certificate
var {Cc, Ci} = require("chrome");
var badCerts = Cc["#mozilla.org/security/recentbadcerts;1"]
.getService(Ci.nsIRecentBadCertsService);
var status = badCerts.getRecentBadCert(host + ":" + port);
if (status)
{
var overrideService = Cc["#mozilla.org/security/certoverride;1"]
.getService(Ci.nsICertOverrideService);
overrideService.rememberValidityOverride(host, port, status.serverCert,
Ci.nsICertOverrideService.ERROR_UNTRUSTED, false);
// Override added, now you should re-do the request
...
}
}
}
});
Note: This code hasn't been tested, in particular I'm not sure whether detecting connection errors will really work by checking response.status (my guess is that it should throw if there was a connection error but the documentation doesn't say anything).