Digest autenticación en Android mediante HttpURLConnection

Como la pregunta allready dice, estoy tratando de digerir la autenticación en android.
Hasta ahora he utilizado el DefaultHttpClient y su método de autenticación (usando UsernamePasswordCredentials y así sucesivamente), pero está obsoleto desde Android 5 y será eliminado en Android 6.
Así que estoy a punto de cambiar de DefaultHttpClient a HttpUrlConnection .
Ahora estoy tratando de lograr la autenticación digest, que debería funcionar bastante simple como se explica aquí :

 Authenticator.setDefault(new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }); 

Pero el getPasswordAuthentication nunca se llama por alguna razón.
Durante mi búsqueda de este problema he encontrado diferentes puestos, diciendo que la autenticación digest no es compatible con la HttpUrlConnection en android, pero esos puestos son de 2010-2012, por lo que no estoy seguro de si esto sigue siendo cierto. También estamos utilizando HttpUrlConnection con digest autenticación en nuestra aplicación de escritorio de Java, donde funciona.

También encontré algunos posts, hablando de OkHttp . OkHttp parece ser utilizado por Android bajo el capó (para ser más específico el HttpUrlConnectionImpl ). Pero este HttpUrlConnectionImpl es un poco extraño, ni siquiera se muestra en la jerarquía de tipo Eclipse y no soy capaz de depurarlo. También debe ser un com.squareup.okhttp.internal.huc.HttpUrlConnectionImpl , mientras que en android es un com.android.okhttp.internal.http.HttpUrlConnectionImpl .

Así que no soy capaz de hacer digerir la autenticación con este HttpUrlConnection en android.
¿Puede alguien decirme cómo hacerlo sin bibliotecas externas?

EDITAR:
El servidor solicita la autenticación de resumen:

 WWW-Authenticate: Digest realm="Realm Name",domain="/domain",nonce="nonce",algorithm=MD5,qop="auth" 

Así que Basic-Authentication shouldn 'trabajo, como el servidor está pidiendo digerir.

La respuesta es, que HttpUrlConnection no admite el resumen.

Por lo tanto, tiene que implementar RFC2617 por ti mismo.

Puede utilizar el código siguiente como implementación de línea de base: HTTP Digest Auth para Android .

Los pasos involucran (ver RFC2617 para referencia):

  • Si obtiene una respuesta 401, iterar sobre todos WWW-Authenticate encabezados WWW-Authenticate y analizarlos:
    • Compruebe si el algoritmo es MD5 o no definido, (opcionalmente seleccione la opción auth qop), de lo contrario ignore el desafío y vaya al siguiente encabezado.
    • Obtenga las credenciales utilizando Authenticator.requestPasswordAuthentication .
    • Calcule H (A1) usando el nombre de usuario, reino y contraseña.
    • Almacene la URL raíz canónica, dominio, HA1, nombre de usuario, nonce (+ opcionalmente algoritmo, opaco y el cliente seleccionó la opción qop si está presente).
    • Vuelva a intentar la solicitud.
  • En cada solicitud, iterar sobre todos los dominios que tiene la información de sesión almacenada por la URL raíz canónica:
    • Calcular H (A2) utilizando el método de solicitud y la ruta.
    • Calcular H (A3) utilizando HA1, nonce (+ opcionalmente nc, cnonce, qop) y HA2.
    • Construir y agregar el encabezado de Authorization a su HttpUrlConnection .
  • Implementar algún tipo de poda de sesión.

Al usar Authenticator , puede asegurarse de que tan pronto como HttpUrlConnection admita la digestión de forma nativa, su código no se está utilizando más (porque no recibirá el 401 en primer lugar).

Esto es sólo un resumen rápido sobre cómo implementarlo, para que usted pueda tener una idea.

Si desea ir más lejos, probablemente le gustaría implementar SHA256 también: RFC7616

Es correcto que HttpUrlConnection no admite la autenticación Digest. Si su cliente debe autenticarse con Digest, tiene algunas opciones:

  • Escriba su propia implementación HTTP Digest. Esto puede ser una buena opción si sabe qué servidores necesita autenticar y puede ignorar las partes de la especificación de resumen que no necesita. A continuación se muestra un ejemplo en el que se implementa un subconjunto de digest: https://gist.github.com/slightfoot/5624590 .
  • Utilice el lib externo bare-bones-digest , que es un Digest lib para Android. Puede usarlo para analizar desafíos de Digest y generar respuestas a ellos. Es compatible con los casos de uso común de digerir y algunos de los raramente utilizados y se puede utilizar en la parte superior de HttpURLConnection .
  • Utilice OkHttp junto con okhttp-digest , que es un complemento que añade soporte Http Digest a OkHttp. Apoyo Digest con OkHttp es fácil, sólo tiene que agregar okhttp-digest como un autenticador y tendrá soporte transparente Http digest. Si ya usa OkHttp o está bien con el cambio a esta puede ser una opción atractiva.
  • Utilice el Apache HttpClient que admite Digest. La pregunta afirma explícitamente que HttpClient no es una opción por lo que lo incluyo en su mayor parte por el bien de la finalización. Google no recomienda el uso de HttpClient y lo ha despreciado.

¿Ha intentado establecer la cabecera manualmente como:

 String basic = "Basic " + new String(Base64.encode("username:password".getBytes(),Base64.NO_WRAP )); connection.setRequestProperty ("Authorization", basic); 

También tenga en cuenta algunos problemas en Jellybeans y un error cuando intenta realizar una solicitud de correos: HTTP Basic Authentication issue en Android Jelly Bean 4.1 usando HttpURLConnection

EDIT: para la autenticación Digest

Echa un vistazo aquí https://code.google.com/p/android/issues/detail?id=9579

Especialmente esto podría funcionar:

 try { HttpClient client = new HttpClient( new MultiThreadedHttpConnectionManager()); client.getParams().setAuthenticationPreemptive(true); Credentials credentials = new UsernamePasswordCredentials("username", "password"); client.getState().setCredentials(AuthScope.ANY, credentials); List<String> authPrefs = new ArrayList<String>(2); authPrefs.add(AuthPolicy.DIGEST); authPrefs.add(AuthPolicy.BASIC); client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); GetMethod getMethod = new GetMethod("your_url"); getMethod.setRequestHeader("Accept", "application/xml"); client.executeMethod(getMethod); int status = getMethod.getStatusCode(); getMethod.setDoAuthentication(true); System.out.println("status: " + status); if (status == HttpStatus.SC_OK) { String responseBody = getMethod.getResponseBodyAsString(); String resp = responseBody.replaceAll("\n", " "); System.out.println("RESPONSE \n" + resp); } } catch (Exception e) { e.printStackTrace(); } 

Finalmente reemplazé el DefaultHttpClient obsoleto con mi propia implementación de HttpUrlConnection y implementé la digest atuhentication mismo, usando esto como una plantilla.
El código de finaly se parece a esto:

 // requestMethod: "GET", "POST", "PUT" etc. // Headers: A map with the HTTP-Headers for the request // Data: Body-Data for Post/Put int statusCode = this.requestImpl(requestMethod, headers, data); if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED && hasUserNameAndPassword) { String auth = getResponseHeaderField("WWW-Authenticate"); // Server needs Digest authetication if(auth.startsWith("Digest")){ // Parse the auth Header HashMap<String, String> authFields = parseWWWAuthenticateHeader(auth); // Generate Auth-Value for request String requestAuth = generateDigestAuth(authFields); headers.put("Authorization", authStr); statusCode = this.requestImpl(requestMethod, headers, data); } } 

Tan básicamente hago una petición y si vuelve 401, miro, si el servidor quiere digest authentication y si tengo nombre de usuario y contraseña. Si ese es el caso, analizar el encabezado de autenticación de la respuesta, que contiene todas las informaciones necesarias sobre la autenticación.
Para analizar el encabezado de autenticación utilizo algún tipo de StateMachine que se describe aquí .
Después de analizar el encabezado auth de respuesta, genero el encabezado auth de solicitud utilizando las informaciones de la respuesta:

  String digestAuthStr = null; String uri = getURL().getPath(); String nonce = authFields.get("nonce"); String realm = authFields.get("realm"); String qop = authFields.get("qop"); String algorithm = authFields.get("algorithm"); String cnonce = generateCNonce(); String nc = "1"; String ha1 = toMD5DigestString(concatWithSeparator(":", username, realm, password)); String ha2 = toMD5DigestString(concatWithSeparator(":", requestMethod, uri)); String response = null; if (!TextUtils.isEmpty(ha1) && !TextUtils.isEmpty(ha2)) response = toMD5DigestString(concatWithSeparator(":", ha1, nonce, nc, cnonce, qop, ha2)); if (response != null) { StringBuilder sb = new StringBuilder(128); sb.append("Digest "); sb.append("username").append("=\"").append(username).append("\", "); sb.append("realm").append("=\"").append(realm).append("\", "); sb.append("nonce").append("=\"").append(nonce).append("\", "); sb.append("uri").append("=\"").append(uri).append("\", "); sb.append("qop").append("=\"").append(qop).append("\", "); sb.append("nc").append("=\"").append(nc).append("\", "); sb.append("cnonce").append("=\"").append(cnonce).append("\""); sb.append("response").append("=\"").append(response).append("\""); sb.append("algorithm").append("=\"").append(algorithm).append("\""); digestAuthStr = sb.toString(); } 

Para generar el Client-Nonce estoy usando el código siguiente:

 private static String generateCNonce() { String s = ""; for (int i = 0; i < 8; i++) s += Integer.toHexString(new Random().nextInt(16)); return s; } 

Espero que esto ayude a alguien. Si el código contiene algún error, por favor avíseme para poder solucionarlo. Pero ahora parece funcionar.

  • ¿Es imposible hacer un juego Android sin tartamudear? Me estoy volviendo loco aqui
  • AscyncTask - No en el hilo principal
  • Clueless Acerca de (posible) pérdida de memoria de Android
  • Google Maps no se muestra de repente en Android
  • Cómo escapar% en String.Format?
  • Costo de invocar un método en Android
  • ¿Hay un HttpClient que maneja las solicitudes de almacenamiento en caché por sí solo?
  • Ormlite configuración sin utilizar las actividades de base
  • Cómo comparar el tiempo en java / android (dado entrada en cadenas)?
  • Deserializar el mapa JSON usando Jackson no da el mapa correcto
  • Recepción de SMS en Android Manifest.xml
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.