PageRenderTime 26ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/afirma-core/src/main/java/es/gob/afirma/core/misc/http/UrlHttpManagerImpl.java

https://gitlab.com/edgardo001/clienteafirma
Java | 312 lines | 237 code | 43 blank | 32 comment | 49 complexity | d3c4b805df9802f755bffbcbfb98fa34 MD5 | raw file
  1. /* Copyright (C) 2011 [Gobierno de Espana]
  2. * This file is part of "Cliente @Firma".
  3. * "Cliente @Firma" is free software; you can redistribute it and/or modify it under the terms of:
  4. * - the GNU General Public License as published by the Free Software Foundation;
  5. * either version 2 of the License, or (at your option) any later version.
  6. * - or The European Software License; either version 1.1 or (at your option) any later version.
  7. * Date: 11/01/11
  8. * You may contact the copyright holder at: soporte.afirma5@mpt.es
  9. */
  10. package es.gob.afirma.core.misc.http;
  11. import java.io.File;
  12. import java.io.FileInputStream;
  13. import java.io.IOException;
  14. import java.io.InputStream;
  15. import java.io.OutputStream;
  16. import java.net.CookieHandler;
  17. import java.net.CookieManager;
  18. import java.net.CookiePolicy;
  19. import java.net.HttpURLConnection;
  20. import java.net.InetAddress;
  21. import java.net.Proxy;
  22. import java.net.URL;
  23. import java.security.KeyManagementException;
  24. import java.security.KeyStore;
  25. import java.security.KeyStoreException;
  26. import java.security.NoSuchAlgorithmException;
  27. import java.security.UnrecoverableKeyException;
  28. import java.security.cert.CertificateException;
  29. import java.security.cert.X509Certificate;
  30. import java.util.StringTokenizer;
  31. import java.util.logging.Logger;
  32. import javax.net.ssl.HostnameVerifier;
  33. import javax.net.ssl.HttpsURLConnection;
  34. import javax.net.ssl.KeyManager;
  35. import javax.net.ssl.KeyManagerFactory;
  36. import javax.net.ssl.SSLContext;
  37. import javax.net.ssl.SSLSession;
  38. import javax.net.ssl.SSLSocketFactory;
  39. import javax.net.ssl.TrustManager;
  40. import javax.net.ssl.X509TrustManager;
  41. import es.gob.afirma.core.misc.AOUtil;
  42. import es.gob.afirma.core.misc.Base64;
  43. import es.gob.afirma.core.misc.Platform;
  44. /** Clase para la lectura y envío de datos a URL remotas.
  45. * @author Carlos Gamuci.
  46. * @author Tomás García-Merás. */
  47. public class UrlHttpManagerImpl implements UrlHttpManager {
  48. private static final Logger LOGGER = Logger.getLogger("es.gob.afirma"); //$NON-NLS-1$
  49. private static final String JAVA_PARAM_DISABLE_SSL_CHECKS = "disableSslChecks"; //$NON-NLS-1$
  50. /** Tiempo de espera por defecto para descartar una conexión HTTP. */
  51. public static final int DEFAULT_TIMEOUT = -1;
  52. private static final String HTTPS = "https"; //$NON-NLS-1$
  53. private static final String URN_SEPARATOR = ":"; //$NON-NLS-1$
  54. private static final String PROT_SEPARATOR = URN_SEPARATOR + "//"; //$NON-NLS-1$
  55. private static final HostnameVerifier DEFAULT_HOSTNAME_VERIFIER = HttpsURLConnection.getDefaultHostnameVerifier();
  56. private static final SSLSocketFactory DEFAULT_SSL_SOCKET_FACTORY = HttpsURLConnection.getDefaultSSLSocketFactory();
  57. private static final String KEYSTORE = "javax.net.ssl.keyStore"; //$NON-NLS-1$
  58. private static final String KEYSTORE_PASS = "javax.net.ssl.keyStorePassword"; //$NON-NLS-1$
  59. private static final String KEYSTORE_TYPE = "javax.net.ssl.keyStoreType"; //$NON-NLS-1$
  60. private static final String KEYSTORE_DEFAULT_TYPE = "JKS"; //$NON-NLS-1$
  61. private static final String KEYMANAGER_INSTANCE = "SunX509";//$NON-NLS-1$
  62. private static final String SSL_CONTEXT = "SSL";//$NON-NLS-1$
  63. static {
  64. final CookieManager cookieManager = new CookieManager();
  65. cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
  66. CookieHandler.setDefault(cookieManager);
  67. }
  68. protected UrlHttpManagerImpl() {
  69. // Vacio y "protected"
  70. }
  71. private static final TrustManager[] DUMMY_TRUST_MANAGER = new TrustManager[] {
  72. new X509TrustManager() {
  73. @Override
  74. public java.security.cert.X509Certificate[] getAcceptedIssuers() {
  75. return null;
  76. }
  77. @Override
  78. public void checkClientTrusted(final X509Certificate[] certs, final String authType) { /* No hacemos nada */ }
  79. @Override
  80. public void checkServerTrusted(final X509Certificate[] certs, final String authType) { /* No hacemos nada */ }
  81. }
  82. };
  83. @Override
  84. public byte[] readUrl(final String url, final UrlHttpMethod method) throws IOException {
  85. return readUrl(url, DEFAULT_TIMEOUT, null, null, method);
  86. }
  87. private static boolean isLocal(final URL url) {
  88. if (url == null) {
  89. throw new IllegalArgumentException("La URL no puede ser nula"); //$NON-NLS-1$
  90. }
  91. try {
  92. return InetAddress.getByName(url.getHost()).isLoopbackAddress();
  93. }
  94. catch (final Exception e) {
  95. LOGGER.warning("Error comprobando si una URL es el bucle local: " + e); //$NON-NLS-1$
  96. return false;
  97. }
  98. }
  99. @Override
  100. public byte[] readUrl(final String urlToRead,
  101. final int timeout,
  102. final String contentType,
  103. final String accept,
  104. final UrlHttpMethod method) throws IOException {
  105. if (urlToRead == null) {
  106. throw new IllegalArgumentException("La URL a leer no puede ser nula"); //$NON-NLS-1$
  107. }
  108. // Vemos si lleva usuario y contrasena
  109. final String authString;
  110. final String url;
  111. final URLName un = new URLName(urlToRead);
  112. if (un.getUsername() != null || un.getPassword() != null) {
  113. final String tmpStr;
  114. if (un.getUsername() != null && un.getPassword() != null) {
  115. tmpStr = un.getUsername() + URN_SEPARATOR + un.getPassword();
  116. }
  117. else if (un.getUsername() != null) {
  118. tmpStr = un.getUsername();
  119. }
  120. else {
  121. tmpStr = un.getPassword();
  122. }
  123. authString = Base64.encode(tmpStr.getBytes());
  124. url = un.getProtocol() + PROT_SEPARATOR + un.getHost() + (un.getPort() != -1 ? URN_SEPARATOR + Integer.toString(un.getPort()) : "") + "/" + un.getFile(); //$NON-NLS-1$ //$NON-NLS-2$
  125. }
  126. else {
  127. url = urlToRead;
  128. authString = null;
  129. }
  130. String urlParameters = null;
  131. String request = null;
  132. if (UrlHttpMethod.POST.equals(method) || UrlHttpMethod.PUT.equals(method)) {
  133. final StringTokenizer st = new StringTokenizer(url, "?"); //$NON-NLS-1$
  134. request = st.nextToken();
  135. urlParameters = st.nextToken();
  136. }
  137. final URL uri = new URL(request != null ? request : url);
  138. final boolean disableSSLChecks = Boolean.getBoolean(JAVA_PARAM_DISABLE_SSL_CHECKS);
  139. if (disableSSLChecks && uri.getProtocol().equals(HTTPS)) {
  140. try {
  141. disableSslChecks();
  142. }
  143. catch(final Exception e) {
  144. Logger.getLogger("es.gob.afirma").warning( //$NON-NLS-1$
  145. "No se ha podido ajustar la confianza SSL, es posible que no se pueda completar la conexion: " + e //$NON-NLS-1$
  146. );
  147. }
  148. }
  149. final HttpURLConnection conn;
  150. if (Platform.OS.ANDROID.equals(Platform.getOS()) || isLocal(uri)) {
  151. conn = (HttpURLConnection) uri.openConnection(Proxy.NO_PROXY);
  152. }
  153. else {
  154. conn = (HttpURLConnection) uri.openConnection();
  155. }
  156. conn.setRequestMethod(method.toString());
  157. if (authString != null) {
  158. conn.addRequestProperty("Authorization", "Basic " + authString); //$NON-NLS-1$ //$NON-NLS-2$
  159. }
  160. conn.addRequestProperty(
  161. "Accept", //$NON-NLS-1$
  162. accept != null ? accept : "*/*" //$NON-NLS-1$
  163. );
  164. conn.addRequestProperty("Connection", "keep-alive"); //$NON-NLS-1$ //$NON-NLS-2$
  165. if (contentType != null) {
  166. conn.addRequestProperty("Content-Type", contentType); //$NON-NLS-1$
  167. }
  168. conn.addRequestProperty("Host", uri.getHost()); //$NON-NLS-1$
  169. conn.addRequestProperty("Origin", uri.getProtocol() + "://" + uri.getHost()); //$NON-NLS-1$ //$NON-NLS-2$
  170. if (timeout != DEFAULT_TIMEOUT) {
  171. conn.setConnectTimeout(timeout);
  172. conn.setReadTimeout(timeout);
  173. }
  174. if (urlParameters != null) {
  175. conn.setRequestProperty("Content-Length", String.valueOf(urlParameters.getBytes("UTF-8").length)); //$NON-NLS-1$ //$NON-NLS-2$
  176. conn.setDoOutput(true);
  177. final OutputStream os = conn.getOutputStream();
  178. os.write(urlParameters.getBytes("UTF-8")); //$NON-NLS-1$
  179. os.close();
  180. }
  181. conn.connect();
  182. final int resCode = conn.getResponseCode();
  183. final String statusCode = Integer.toString(resCode);
  184. if (statusCode.startsWith("4") || statusCode.startsWith("5")) { //$NON-NLS-1$ //$NON-NLS-2$
  185. if (uri.getProtocol().equals(HTTPS)) {
  186. enableSslChecks();
  187. }
  188. throw new HttpError(resCode, conn.getResponseMessage(), url);
  189. }
  190. final InputStream is = conn.getInputStream();
  191. final byte[] data = AOUtil.getDataFromInputStream(is);
  192. is.close();
  193. if (disableSSLChecks && uri.getProtocol().equals(HTTPS)) {
  194. enableSslChecks();
  195. }
  196. return data;
  197. }
  198. /** Habilita las comprobaciones de certificados en conexiones SSL dejándolas con su
  199. * comportamiento por defecto. */
  200. public static void enableSslChecks() {
  201. HttpsURLConnection.setDefaultSSLSocketFactory(DEFAULT_SSL_SOCKET_FACTORY);
  202. HttpsURLConnection.setDefaultHostnameVerifier(DEFAULT_HOSTNAME_VERIFIER);
  203. }
  204. /** Deshabilita las comprobaciones de certificados en conexiones SSL, aceptádose entonces
  205. * cualquier certificado.
  206. * @throws KeyManagementException Si hay problemas en la gestión de claves SSL.
  207. * @throws NoSuchAlgorithmException Si el JRE no soporta algún algoritmo necesario.
  208. * @throws KeyStoreException Si no se puede cargar el KeyStore SSL.
  209. * @throws IOException Si hay errores en la carga del fichero KeyStore SSL.
  210. * @throws CertificateException Si los certificados del KeyStore SSL son inválidos.
  211. * @throws UnrecoverableKeyException Si una clave del KeyStore SSL es inválida. */
  212. public static void disableSslChecks() throws KeyManagementException,
  213. NoSuchAlgorithmException,
  214. KeyStoreException,
  215. UnrecoverableKeyException,
  216. CertificateException,
  217. IOException {
  218. final SSLContext sc = SSLContext.getInstance(SSL_CONTEXT);
  219. sc.init(getKeyManager(), DUMMY_TRUST_MANAGER, new java.security.SecureRandom());
  220. HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
  221. HttpsURLConnection.setDefaultHostnameVerifier(
  222. new HostnameVerifier() {
  223. @Override
  224. public boolean verify(final String hostname, final SSLSession session) {
  225. return true;
  226. }
  227. }
  228. );
  229. }
  230. /** Devuelve un KeyManager a utilizar cuando se desea deshabilitar las comprobaciones de certificados en las conexiones SSL.
  231. * @return KeyManager[] Se genera un KeyManager[] utilizando el keystore almacenado en las propiedades del sistema.
  232. * @throws KeyStoreException Si no se puede cargar el KeyStore SSL.
  233. * @throws NoSuchAlgorithmException Si el JRE no soporta algún algoritmo necesario.
  234. * @throws CertificateException Si los certificados del KeyStore SSL son inválidos.
  235. * @throws IOException Si hay errores en la carga del fichero KeyStore SSL.
  236. * @throws UnrecoverableKeyException Si una clave del KeyStore SSL es inválida. */
  237. private static KeyManager[] getKeyManager() throws KeyStoreException,
  238. NoSuchAlgorithmException,
  239. CertificateException,
  240. IOException,
  241. UnrecoverableKeyException {
  242. final String keyStore = System.getProperty(KEYSTORE);
  243. final String keyStorePassword = System.getProperty(KEYSTORE_PASS);
  244. final String keyStoreType = System.getProperty(KEYSTORE_TYPE);
  245. if (keyStore == null || keyStore.isEmpty()) {
  246. return null;
  247. }
  248. final File f = new File(keyStore);
  249. if (!f.isFile() || !f.canRead()) {
  250. LOGGER.warning("El KeyStore SSL no existe o no es legible: " + f.getAbsolutePath()); //$NON-NLS-1$
  251. return null;
  252. }
  253. final KeyStore keystore = KeyStore.getInstance(
  254. keyStoreType != null && !keyStoreType.isEmpty() ? keyStoreType : KEYSTORE_DEFAULT_TYPE
  255. );
  256. final InputStream fis = new FileInputStream(f);
  257. keystore.load(
  258. fis,
  259. keyStorePassword != null ? keyStorePassword.toCharArray() : null
  260. );
  261. fis.close();
  262. final KeyManagerFactory keyFac = KeyManagerFactory.getInstance(KEYMANAGER_INSTANCE);
  263. keyFac.init(
  264. keystore,
  265. keyStorePassword != null ? keyStorePassword.toCharArray() : null
  266. );
  267. return keyFac.getKeyManagers();
  268. }
  269. }