sirve - proxy java example
¿Cómo se envía una solicitud HTTPS a través de un proxy en Java? (2)
El proxy HTTPS no tiene sentido porque no puede terminar su conexión HTTP en el proxy por razones de seguridad. Con su política de confianza, podría funcionar si el servidor proxy tiene un puerto HTTPS. Su error se debe al conectarse al puerto proxy HTTP con HTTPS.
Puede conectarse a través de un proxy mediante un túnel SSL (muchas personas llaman a ese proxy) mediante el comando proxy CONNECT. Sin embargo, Java no admite una versión más reciente de túnel de proxy. En ese caso, necesita manejar el túnel usted mismo. Puedes encontrar código de ejemplo aquí,
http://www.javaworld.com/javaworld/javatips/jw-javatip111.html
EDITAR: Si desea anular todas las medidas de seguridad en JSSE, todavía necesita su propio TrustManager. Algo como esto,
public SSLTunnelSocketFactory(String proxyhost, String proxyport){
tunnelHost = proxyhost;
tunnelPort = Integer.parseInt(proxyport);
dfactory = (SSLSocketFactory)sslContext.getSocketFactory();
}
...
connection.setSSLSocketFactory( new SSLTunnelSocketFactory( proxyHost, proxyPort ) );
connection.setDefaultHostnameVerifier( new HostnameVerifier()
{
public boolean verify( String arg0, SSLSession arg1 )
{
return true;
}
} );
EDIT 2: acabo de probar mi programa que escribí hace unos años usando SSLTunnelSocketFactory y tampoco funciona. Aparentemente, Sun introdujo un nuevo error en algún momento en Java 5. Vea este informe de error,
http://bugs.sun.com/view_bug.do?bug_id=6614957
La buena noticia es que el error de tunelización de SSL está arreglado para que pueda usar la fábrica predeterminada. Acabo de intentar con un proxy y todo funciona como se esperaba. Ver mi codigo
public class SSLContextTest {
public static void main(String[] args) {
System.setProperty("https.proxyHost", "proxy.xxx.com");
System.setProperty("https.proxyPort", "8888");
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
// set up a TrustManager that trusts everything
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
System.out.println("getAcceptedIssuers =============");
return null;
}
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
System.out.println("checkClientTrusted =============");
}
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
System.out.println("checkServerTrusted =============");
}
} }, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
sslContext.getSocketFactory());
HttpsURLConnection
.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String arg0, SSLSession arg1) {
System.out.println("hostnameVerifier =============");
return true;
}
});
URL url = new URL("https://www.verisign.net");
URLConnection conn = url.openConnection();
BufferedReader reader =
new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Esto es lo que obtengo cuando ejecuto el programa,
checkServerTrusted =============
hostnameVerifier =============
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
......
Como puede ver, se llama tanto a SSLContext como a hostnameVerifier. HostnameVerifier solo interviene cuando el nombre de host no coincide con el certificado. Utilicé "www.verisign.net" para activar esto.
Estoy intentando enviar una solicitud a un servidor utilizando la clase HttpsUrlConnection. El servidor tiene problemas de certificados, así que configuro un TrustManager que confía en todo, así como un verificador de nombre de host que es igualmente indulgente. Este administrador funciona bien cuando hago mi solicitud directamente, pero no parece que se use en absoluto cuando envío la solicitud a través de un proxy.
Establecí mi configuración de proxy de esta manera:
Properties systemProperties = System.getProperties();
systemProperties.setProperty( "http.proxyHost", "proxyserver" );
systemProperties.setProperty( "http.proxyPort", "8080" );
systemProperties.setProperty( "https.proxyHost", "proxyserver" );
systemProperties.setProperty( "https.proxyPort", "8080" );
El TrustManager para el SSLSocketFactory predeterminado está configurado de esta manera:
SSLContext sslContext = SSLContext.getInstance( "SSL" );
// set up a TrustManager that trusts everything
sslContext.init( null, new TrustManager[]
{
new X509TrustManager()
{
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted( X509Certificate[] certs, String authType )
{
// everything is trusted
}
public void checkServerTrusted( X509Certificate[] certs, String authType )
{
// everything is trusted
}
}
}, new SecureRandom() );
// this doesn''t seem to apply to connections through a proxy
HttpsURLConnection.setDefaultSSLSocketFactory( sslContext.getSocketFactory() );
// setup a hostname verifier that verifies everything
HttpsURLConnection.setDefaultHostnameVerifier( new HostnameVerifier()
{
public boolean verify( String arg0, SSLSession arg1 )
{
return true;
}
} );
Si ejecuto el siguiente código, termino con una SSLHandshakException ("Conexión remota del host remoto durante el protocolo de enlace"):
URL url = new URL( "https://someurl" );
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setDoOutput( true );
connection.setRequestMethod( "POST" );
connection.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
connection.setRequestProperty( "Content-Length", "0" );
connection.connect();
Supongo que me falta algún tipo de configuración que tenga que ver con el uso de un proxy cuando se trata de SSL. Si no uso un proxy, se llama a mi método checkServerTrusted; esto es lo que necesito que suceda cuando también estoy pasando por el proxy.
Normalmente no trato con Java y no tengo mucha experiencia con HTTP / web. Creo que he proporcionado todos los detalles necesarios para entender lo que estoy tratando de hacer. Si este no es el caso, hágamelo saber.
Actualizar:
Después de leer el artículo sugerido por ZZ Coder, hice los siguientes cambios en el código de conexión:
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setSSLSocketFactory( new SSLTunnelSocketFactory( proxyHost, proxyPort ) );
connection.setDoOutput( true );
connection.setRequestMethod( "POST" );
connection.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
connection.setRequestProperty( "Content-Length", "0" );
connection.connect();
El resultado (SSLHandshakeException) es el mismo. Cuando configuro el SLLSocketFactory aquí para el SSLTunnelSocketFactory (la clase explicada en el artículo), las cosas que hice con el TrustManager y el SSLContext se anulan. ¿Todavía no necesito eso?
Otra actualización:
Modifiqué la clase SSLTunnelSocketFactory para usar el SSLSocketFactory que usa mi TrustManager que confía en todo. No parece que esto haya hecho ninguna diferencia. Este es el método createSocket de SSLTunnelSocketFactory:
public Socket createSocket( Socket s, String host, int port, boolean autoClose )
throws IOException, UnknownHostException
{
Socket tunnel = new Socket( tunnelHost, tunnelPort );
doTunnelHandshake( tunnel, host, port );
SSLSocket result = (SSLSocket)dfactory.createSocket(
tunnel, host, port, autoClose );
result.addHandshakeCompletedListener(
new HandshakeCompletedListener()
{
public void handshakeCompleted( HandshakeCompletedEvent event )
{
System.out.println( "Handshake finished!" );
System.out.println(
"/t CipherSuite:" + event.getCipherSuite() );
System.out.println(
"/t SessionId " + event.getSession() );
System.out.println(
"/t PeerHost " + event.getSession().getPeerHost() );
}
} );
result.startHandshake();
return result;
}
Cuando mi código llama a connection.connect, se llama a este método y la llamada a doTunnelHandshake se realiza correctamente. La siguiente línea de código usa mi SSLSocketFactory para crear un SSLSocket; el valor de toString del resultado después de esta llamada es:
"1d49247 [SSL_NULL_WITH_NULL_NULL: Socket [addr = / proxyHost, puerto = proxyPort, localport = 24372]]" .
Esto no tiene sentido para mí, pero podría ser la razón por la que las cosas se descomponen después de esto.
Cuando se llama a result.startHandshake (), se vuelve a llamar al mismo método createSocket desde, de acuerdo con la pila de llamadas, HttpsClient.afterConnect, con los mismos argumentos, excepto que Socket s es nulo, y cuando aparece alrededor del resultado. De nuevo, el resultado es el mismo SSLHandshakeException.
¿Todavía me falta una pieza importante para este rompecabezas cada vez más complicado?
Esta es la traza de pila:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:808) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1112) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1139) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123) at gsauthentication.SSLTunnelSocketFactory.createSocket(SSLTunnelSocketFactory.java:106) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:391) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166) at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133) at gsauthentication.GSAuthentication.main(GSAuthentication.java:52) Caused by: java.io.EOFException: SSL peer shut down incorrectly at com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:333) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:789) ... 8 more
Pruebe la biblioteca HttpClient de Apache Commons en lugar de intentar rodar la suya propia: http://hc.apache.org/httpclient-3.x/index.html
De su código de muestra:
HttpClient httpclient = new HttpClient();
httpclient.getHostConfiguration().setProxy("myproxyhost", 8080);
/* Optional if authentication is required.
httpclient.getState().setProxyCredentials("my-proxy-realm", " myproxyhost",
new UsernamePasswordCredentials("my-proxy-username", "my-proxy-password"));
*/
PostMethod post = new PostMethod("https://someurl");
NameValuePair[] data = {
new NameValuePair("user", "joe"),
new NameValuePair("password", "bloggs")
};
post.setRequestBody(data);
// execute method and handle any error responses.
// ...
InputStream in = post.getResponseBodyAsStream();
// handle response.
/* Example for a GET reqeust
GetMethod httpget = new GetMethod("https://someurl");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
*/