translator translateclient traductor google example android google-api google-cloud-platform google-translate

android - translateclient - google translate api key



Restringir el uso de una clave de Android para una API de Google (4)

Mi pregunta es sobre cómo configurar correctamente el nombre del paquete y la huella digital del certificado SHA-1 en la Consola de desarrolladores de Google para restringir el uso de mi clave API de Android en mi aplicación.

Cuando no tengo nada configurado en la sección "Restringir el uso de sus aplicaciones de Android", mis solicitudes a la API de Google Translate funcionan correctamente. La API responde normalmente con el código de estado 200 y mi resultado esperado.

Pero cuando especifico el nombre de un paquete y la huella digital del certificado SHA-1 para mi aplicación usando la Consola de Desarrollador, obtengo 403 respuestas prohibidas como las siguientes:

HTTP/1.1 403 Forbidden Vary: Origin Vary: X-Origin Content-Type: application/json; charset=UTF-8 Date: Sun, 29 Nov 2015 21:01:39 GMT Expires: Sun, 29 Nov 2015 21:01:39 GMT Cache-Control: private, max-age=0 X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block Server: GSE Alternate-Protocol: 443:quic,p=1 Alt-Svc: quic=":443"; ma=604800; v="30,29,28,27,26,25" Content-Length: 729 { "error": { "errors": [ { "domain": "usageLimits", "reason": "ipRefererBlocked", "message": "There is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your API key configuration if request from this IP or referer should be allowed.", "extendedHelp": "https://console.developers.google.com" } ], "code": 403, "message": "There is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your API key configuration if request from this IP or referer should be allowed." } }

La solicitud tiene el siguiente aspecto. Tenga en cuenta que no hay un encabezado de referencia en la solicitud:

GET https://www.googleapis.com/language/translate/v2?key=XXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXX&source=en&target=es&q=test HTTP/1.1 User-Agent: Dalvik/2.1.0 (Linux; U; Android 5.1.1; Nexus 6 Build/LVY48H) Host: www.googleapis.com Connection: Keep-Alive Accept-Encoding: gzip

Supongo que el mensaje de error indica un nombre de paquete o un problema de huella digital SHA-1, a pesar de su mensaje sobre una "restricción por IP o por referencia". Si bien las teclas del navegador permiten la configuración de una restricción por referidor, estoy usando una clave de Android sin ningún lugar para establecer una restricción por IP o por referencia.

Estoy seguro de que ingresé el nombre del paquete correctamente en la Consola de desarrolladores de Google. Estoy leyendo el nombre del package atributo del package en la etiqueta de manifest en mi archivo de manifiesto de Android.

También estoy seguro de que tengo la huella digital SHA-1 configurada correctamente en la Consola de desarrolladores de Google. Estoy leyendo este valor de mi almacén de claves utilizando el comando keytool -list -v -keystore /path/to/my/keystore . Obtengo el mismo valor cuando lo leo del archivo APK usando keytool -list -printcert -jarfile myAppName.apk . Estoy instalando el mismo archivo APK usando adb.

Esto es lo que veo en la Consola de desarrolladores:

He probado esto en varios dispositivos con Android stock. Recibo la respuesta de error en wifi y en la red celular, ya sea que esté representando el tráfico o no.

Cuando elimino la restricción de la Consola de desarrolladores, la aplicación vuelve a funcionar correctamente.

¿Qué estoy haciendo mal aquí?

Nota: Several have been Several questions similar before , but no answers adequate . No quiero usar una clave de navegador o eliminar la restricción por completo. Quiero que la restricción de uso funcione correctamente.


Acceder a la API directamente desde su código en lugar de pasar por un SDK intermedio proporcionado por Google significa que no hay ningún mecanismo disponible para obtener de forma segura la huella digital del certificado de su aplicación y pasar esa huella digital a la API. Por otro lado, cuando está utilizando uno de los SDK de Android proporcionados en lugar de presionar directamente a la API, por ejemplo, cuando envía solicitudes utilizando el SDK de Google Maps de Android, el SDK puede manejar la obtención de la huella digital del certificado de su aplicación para que la aplicación la restricción funcionará según lo previsto.

La Google Developers Console es engañosa a este respecto porque, para algunas de sus API, permite a los desarrolladores configurar restricciones clave basadas en la huella digital del certificado de la aplicación de Android, pero luego no pone a disposición un SDK para Android que pueda verificar esa huella digital en tiempo de ejecución. Lo que queda con los desarrolladores, entonces, es la opción peor y más insegura de enviar los encabezados X-Android-Cert y X-Android-Package junto con sus solicitudes como se describe en la otra respuesta aquí.

Por lo tanto, para las API para las cuales no se ha publicado el SDK de Android para manejar la verificación de la huella digital del certificado de la aplicación, resulta que no hay una forma fácil y oculta de obtener algo como, por ejemplo, Google Play Services para manejar el orden de la huella digital del certificado de la aplicación usar correctamente la restricción de la clave de la aplicación, simplemente no hay forma de hacerlo.


Cuando use una API de Google REST solo, como Traducir, deberá usar GoogleAuthUtil , que generará un token para un usuario específico y un paquete / huella digital. Sin embargo, eso requiere el permiso GET_ACCOUNTS , del que los usuarios inteligentes desconfían.

También podría usar el AccountManager getAuthToken() , pero eso requeriría no solo el permiso GET_ACCOUNTS , sino también USE_CREDENTIALS .

Es mejor que uses una clave API y la ocultes un poco.


Todo lo que ha hecho en Google Developer Console para restringir el uso de su clave de API para la aplicación de Android está bien. Después de restringir, esta clave API solo aceptará solicitudes de su aplicación con el nombre del paquete y la huella digital del certificado SHA-1 especificada.

Entonces, ¿cómo sabe Google que esa solicitud se envió DESDE SU APLICACIÓN DE ANDROID? DEBE agregar el nombre del paquete de su aplicación y SHA-1 en el encabezado de cada solicitud (obviamente). Y no necesita el permiso GoogleAuthUtil y GET_ACCOUNTS .

PRIMERO, obtenga la firma SHA de su aplicación (necesitará la biblioteca Guava ):

/** * Gets the SHA1 signature, hex encoded for inclusion with Google Cloud Platform API requests * * @param packageName Identifies the APK whose signature should be extracted. * @return a lowercase, hex-encoded */ public static String getSignature(@NonNull PackageManager pm, @NonNull String packageName) { try { PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); if (packageInfo == null || packageInfo.signatures == null || packageInfo.signatures.length == 0 || packageInfo.signatures[0] == null) { return null; } return signatureDigest(packageInfo.signatures[0]); } catch (PackageManager.NameNotFoundException e) { return null; } } private static String signatureDigest(Signature sig) { byte[] signature = sig.toByteArray(); try { MessageDigest md = MessageDigest.getInstance("SHA1"); byte[] digest = md.digest(signature); return BaseEncoding.base16().lowerCase().encode(digest); } catch (NoSuchAlgorithmException e) { return null; } }

Luego, agregue el nombre del paquete y la firma del certificado SHA al encabezado de la solicitud:

java.net.URL url = new URL(REQUEST_URL); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); try { connection.setDoInput(true); connection.setDoOutput(true); connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); connection.setRequestProperty("Accept", "application/json"); // add package name to request header String packageName = mActivity.getPackageName(); connection.setRequestProperty("X-Android-Package", packageName); // add SHA certificate to request header String sig = getSignature(mActivity.getPackageManager(), packageName); connection.setRequestProperty("X-Android-Cert", sig); connection.setRequestMethod("POST"); // ADD YOUR REQUEST BODY HERE // .................... } catch (Exception e) { e.printStackTrace(); } finally { connection.disconnect(); }

De otra manera, si está utilizando la API de Google Vision, puede compilar su solicitud con Guava :

try { HttpTransport httpTransport = AndroidHttp.newCompatibleTransport(); JsonFactory jsonFactory = GsonFactory.getDefaultInstance(); VisionRequestInitializer requestInitializer = new VisionRequestInitializer(CLOUD_VISION_API_KEY) { /** * We override this so we can inject important identifying fields into the HTTP * headers. This enables use of a restricted cloud platform API key. */ @Override protected void initializeVisionRequest(VisionRequest<?> visionRequest) throws IOException { super.initializeVisionRequest(visionRequest); String packageName = mActivity.getPackageName(); visionRequest.getRequestHeaders().set("X-Android-Package", packageName); String sig = getSignature(mActivity.getPackageManager(), packageName); visionRequest.getRequestHeaders().set("X-Android-Cert", sig); } }; Vision.Builder builder = new Vision.Builder(httpTransport, jsonFactory, null); builder.setVisionRequestInitializer(requestInitializer); Vision vision = builder.build(); BatchAnnotateImagesRequest batchAnnotateImagesRequest = new BatchAnnotateImagesRequest(); batchAnnotateImagesRequest.setRequests(new ArrayList<AnnotateImageRequest>() {{ AnnotateImageRequest annotateImageRequest = new AnnotateImageRequest(); // Add the image Image base64EncodedImage = new Image(); // Convert the bitmap to a JPEG // Just in case it''s a format that Android understands but Cloud Vision ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); requestImage.compress(Bitmap.CompressFormat.JPEG, IMAGE_JPEG_QUALITY, byteArrayOutputStream); byte[] imageBytes = byteArrayOutputStream.toByteArray(); // Base64 encode the JPEG base64EncodedImage.encodeContent(imageBytes); annotateImageRequest.setImage(base64EncodedImage); // add the features we want annotateImageRequest.setFeatures(new ArrayList<Feature>() {{ Feature labelDetection = new Feature(); labelDetection.setType(TYPE_TEXT_DETECTION); add(labelDetection); }}); // Add the list of one thing to the request add(annotateImageRequest); }}); Vision.Images.Annotate annotateRequest = vision.images().annotate(batchAnnotateImagesRequest); // Due to a bug: requests to Vision API containing large images fail when GZipped. annotateRequest.setDisableGZipContent(true); Log.d("TAG_SERVER", "created Cloud Vision request object, sending request"); BatchAnnotateImagesResponse response = annotateRequest.execute(); return convertResponseToString(response); } catch (GoogleJsonResponseException e) { Log.d("TAG_SERVER", "failed to make API request because " + e.getContent()); } catch (IOException e) { Log.d("TAG_SERVER", "failed to make API request because of other IOException " + e.getMessage()); }

Agregue las siguientes dependencias a su gradle:

compile ''com.google.apis:google-api-services-vision:v1-rev2-1.21.0'' compile ''com.google.api-client:google-api-client-android:1.20.0'' exclude module: ''httpclient'' compile ''com.google.http-client:google-http-client-gson:1.20.0'' exclude module: ''httpclient''

Espero que esto ayude :)


Restricción de paquete y firma de URL

Cuando me encontré con esta publicación cuando tuve problemas para restringir el acceso a la codificación geográfica inversa y la API de mapas estáticos, también quiero compartir mis hallazgos.

Tenga en cuenta que no todos los servicios de Google permiten las mismas restricciones.

Utilizamos la firma de URL y la restricción del paquete de Android / iOS. Enlace a la documentación de Google

Obtener huella digital apk

Hay varias formas de obtener la huella digital del apk de Android.

Con almacén de claves

keytool -list -v keystore mystore.keystore

Con apk

extract *.apk navigate to folder META-INF keytool.exe" -printcert -file *.RSA

Código de ejemplo de C # (Xamarin) para comenzar

En mi código productivo, tengo una clase base para Headerinfo y proporciono una instancia a la clase Geoprovider. Con este enfoque, el código para los servicios de Google se comparte al 100% entre Windows, Android y el paquete ios => nuget.

Encabezados de Android

httpWebRequest.Headers["x-android-package"] = "packageName"; httpWebRequest.Headers["x-android-package"] = "signature";

Encabezados IOS

httpWebRequest.Headers["x-ios-bundle-identifier"] = "bundleIdentifier";

Código de ejemplo para buscar un mapa estático

public byte[] GenerateMap(double latitude, double longitude, int zoom, string size, string mapType) { string lat = latitude.ToString(CultureInfo.InvariantCulture); string lng = longitude.ToString(CultureInfo.InvariantCulture); string url = $"https://maps.googleapis.com/maps/api/staticmap?center={lat},{lng}&zoom={zoom}&size={size}&maptype={mapType}&markers={lat},{lng}&key={_apiKey}"; // get the secret from your firebase console don''t create always an new instance in productive code string signedUrl = new GoogleUrlSigner("mysecret").Sign(url); HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(signedUrl); //Add your headers httpWebRequest.Headers... // get the response for the request HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); // do whatever you want to do with the response }

código de muestra para la firma de URL proporcionado por google

https://developers.google.com/maps/documentation/geocoding/get-api-key

internal class GoogleUrlSigner { private readonly string _secret; public GoogleUrlSigner(string secret) { _secret = secret; } internal string Sign(string url) { ASCIIEncoding encoding = new ASCIIEncoding(); // converting key to bytes will throw an exception, need to replace ''-'' and ''_'' characters first. string usablePrivateKey = _secret.Replace("-", "+").Replace("_", "/"); byte[] privateKeyBytes = Convert.FromBase64String(usablePrivateKey); Uri uri = new Uri(url); byte[] encodedPathAndQueryBytes = encoding.GetBytes(uri.LocalPath + uri.Query); // compute the hash HMACSHA1 algorithm = new HMACSHA1(privateKeyBytes); byte[] hash = algorithm.ComputeHash(encodedPathAndQueryBytes); // convert the bytes to string and make url-safe by replacing ''+'' and ''/'' characters string signature = Convert.ToBase64String(hash).Replace("+", "-").Replace("/", "_"); // Add the signature to the existing URI. return uri.Scheme + "://" + uri.Host + uri.LocalPath + uri.Query + "&signature=" + signature; } }