Youtube API Key - android

When I try to use YouTube API for Search, I get this error:
There was a service error: 403 : The request did not specify any
Android package name or signing-certificate fingerprint. Please ensure
that the client is sending them or use the API Console to update your
key restrictions.
In the MainActivity I have this code:
youtube = new YouTube.Builder(new NetHttpTransport(), JSON_FACTORY, new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest httpRequest) throws IOException {
}
}).setYouTubeRequestInitializer(new YouTubeRequestInitializer(apiKey)).setApplicationName("Some Name").build();
In the cloud console I have an ApiKey for Android, with the package name set and the SHA-1 number obtained with keytool command.

At last I found a solution for this problem :)
After creating API_KEY in Google Developer Console and restrict it with "Package name" and "SHA-1 certificate fingerprint", You have to provide these data in every youtube api request. Below the steps:
1- get Package Name:
String packageName = context.getPackageName();
2- get SHA-1:
private String getSHA1(String packageName){
try {
Signature[] signatures = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures;
for (Signature signature: signatures) {
MessageDigest md;
md = MessageDigest.getInstance("SHA-1");
md.update(signature.toByteArray());
return BaseEncoding.base16().encode(md.digest());
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
3- Prepare youtube api http header:
youTube = new YouTube.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest request) throws IOException {
String packageName = context.getPackageName();
String SHA1 = getSHA1(packageName);
request.getHeaders().set("X-Android-Package", packageName);
request.getHeaders().set("X-Android-Cert",SHA1);
}
}).setApplicationName(appName).build();
4- Build your youtube api query as you like:
For example to search for video:
YouTube.Search.List query;
query = youTube.search().list("id, snippet");
query.setKey(YOUR_API_KEY);
query.setType("video");
query.setFields("items(id/videoId,snippet/title,snippet/description,snippet/thumbnails/default/url)");
query.setQ(search keywords);
SearchListResponse response = query.execute();
List<SearchResult> results = response.getItems();
then process returned search results.

After lot of trial and error, the thing which finally worked for me was changing API KEY restriction to None instead of Android from API Manager console and just save.
After doing the above step I am able to make search API call from my Android device using my API KEY.

Try to double check if you follow properly the setup when creating OAuth Credentials. And make sure you enable the YouTube Data API in your Developer Console.
Here the steps that you need to do.
In the Package name field, enter your Android app's package
name
In a terminal, run the Keytool
utility to
get the SHA1 fingerprint for your digitally signed .apk file's public
certificate.
keytool -exportcert -alias androiddebugkey -keystore path-to-debug-or-production-keystore -list -v
Paste the SHA1 fingerprint into the form where requested.
I also found here in this SO question answered by a Googler that a user has to go through OAuth2. Because Service accounts are not supported in Data API v3.

Related

Google Books API - keep getting error code "403" reason: "ipRefererBlocked"

I am using this as my request url:
`String isbnUrl = "https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn + "&key=" + myAPIKEY;`
Can anyone tell me why I keep getting this response:
{
"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."
}
}
I have gone through the process of getting an API for my Android app using the debug keystore and release keystore and can't seem to get it to work I have tried adding my key as a header as suggested as an answer here: Google Books API 403 Access Not Configured.
I thought this was the answer but then realized by accident that it was the same as not supplying a key at all. I came to this realization after entering the wrong String as the key and it still worked.
In the developer console I am seeing that it receives the request from my API under usage response code section: Client errors (4xx).
I would really appreciate any help if anyone has figured out how to get this API to work the way Google wants by including the key.
Problem is when setting up your API key restriction for android app, you specified the package name and SHA-1 certificate fingerprint. Therefore your API key will only accept request from your app with package name and SHA-1 certificate fingerprint specified.
So when you send an request to Google, you MUST add these information in the header of each request with following keys:
Key: "X-Android-Package", value: your app package name
Key: "X-Android-Cert", value: SHA-1 certificate of your apk
FIRST, get your app SHA signature (you will need Guava library):
/**
* 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;
}
}
Then, add package name and SHA certificate signature to request header:
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();
}
You can see full answer here.
Enjoy coding :D

Restrict access to my API Endpoint to Android App

I want to restrict my API endpoints access only to my android app, but without google_account/password.
I've the choice of those methods : https://developers.google.com/accounts/docs/OAuth2
For test, I succeeded to authenticate my android app to my API with this method: https://cloud.google.com/appengine/docs/python/endpoints/consume_android
==> This method allow you to authenticate your app with combo:
Login/password (Google account)
SHA1 and package name of your android APP
So if someone get my code (Decompiling apk) and modify my android code, they can't access to my API because SHA1 fingerprint of my app will change. (I tested it, and it works =) )
This method works fine, but I don't want Google login/password for authentication..
So I tried this method: https://developers.google.com/accounts/docs/OAuth2ServiceAccount
I successfully authenticate my android app, BUT, if my android code is modified by someone else(So the SHA1 changed), my android app can still connect to my API !! So if someone get my package and decompile it, he'll changed freely code and successfully access to my API..
Here is my API Code:
#ApiMethod( name = "ListCampagnes", httpMethod = ApiMethod.HttpMethod.GET, path="list", clientIds = {CONSTANTES.ANDROID_CLIENT_ID, CONSTANTES.WEB_CLIENT_ID, CONSTANTES.SERVICE_CLIENT_ID, com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID}, audiences = {CONSTANTES.ANDROID_AUDIENCE})
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
Here is my android code:
GoogleCredential credentialToAppengine;
try {
String p12Password = "notasecret";
KeyStore keystore = KeyStore.getInstance("PKCS12");
InputStream keyFileStream = getAssets().open("59ce5a08e110.p12");
keystore.load(keyFileStream, p12Password.toCharArray());
PrivateKey key = (PrivateKey)keystore.getKey("privatekey", p12Password.toCharArray());
credentialToAppengine = new GoogleCredential.Builder().setTransport(AndroidHttp.newCompatibleTransport()).setJsonFactory(new JacksonFactory()).setServiceAccountId("301991144702-3v9ikfp4lsmokee1utkucj35847eddvg#developer.gserviceaccount.com").setServiceAccountPrivateKey(key).setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/userinfo.email")).build();
} catch (GeneralSecurityException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
Do I try an other method for authenticate my android App ? Or did I missing something in my API code ?
Thanks a looot in advance,
Authenticate Android End point without Google User Account is just impossible ! I tried every ways but still doesn't works !
So here is my way to resolv this problem, without any user interaction (Maybe not the right but that works, and you've got strong authentication (SHA1 + Google Account)):
HERE IS MY ANDROID CODE
Get and Build Valid Credential
//Get all accounts from my Android Phone
String validGoogleAccount = null;
Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
if (emailPattern.matcher(account.name).matches()) {
//Just store mail if countain gmail.com
if (account.name.toString().contains("gmail.com")&&account.type.toString().contains("com.google")){
validGoogleAccount=account.name.toString();
}
}
}
//Build Credential with valid google account
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this,"server:client_id:301991144702-5qkqclsogd0b4fnkhrja7hppshrvp4kh.apps.googleusercontent.com");
credential.setSelectedAccountName(validGoogleAccount);
Use this credential for secure calls
Campagneendpoint.Builder endpointBuilder = new Campagneendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential);
HERE IS MY API BACKEND CODE:
API Annotation
#Api(
scopes=CONSTANTES.EMAIL_SCOPE,
clientIds = {CONSTANTES.ANDROID_CLIENT_ID,
CONSTANTES.WEB_CLIENT_ID,
com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
audiences = {CONSTANTES.ANDROID_AUDIENCE},
name = "campagneendpoint",
version = "v1"
)
Method code:
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
For the moment, it only works on Android (I don't know how we gonna do on IOS..)..
Hope It will help you !

Facebook hash key not working

So using the keytool command I was able to generate a hash key for my Android app which uses Facebook login. For purposes of this question, the hash the keytool outputted was "abcdefg=". But when I try to sign on to Facebook from my app, the error says "Key hash abcdefg does not match any stored key hashes" and shows the same exact key I got from keytool just without the equal sign at the end. Why is it not working? Also, when I try to manually type in the key hash on my Facebook developer console (instead of copy/paste), it won't take the key without the equals sign because it only takes keys whose character count is divisible by 4 (my key with equals sign has 28 chars, the key without has only 27 chars). Can someone help?
updateLanguage(getApplicationContext(), "en");
printHashKey(getApplicationContext(),"ur application package name here");
Note1: Create apk file with release keystore.then run on device the keyhash will print logcat. copy the keyhash and place it on facebook app edit setting page.
Note2: correct keyhash generate only on device. dont use simulator for getting keyhash.
public static String printHashKey(Context context, String packagename)
{
String TAG = packagename;
try
{
Log.d(TAG, "keyHash: start");
PackageInfo info = context.getPackageManager().getPackageInfo(TAG,PackageManager.GET_SIGNATURES);
for (Signature signature: info.signatures)
{
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
String keyHash = Base64.encodeToString(md.digest(), Base64.DEFAULT);
Log.d(TAG, "keyHash: " + keyHash);
return keyHash;
}
Log.d(TAG, "keyHash: end");
}
catch (NameNotFoundException e)
{
Log.d(TAG, "keyHash: name:"+e);
}
catch (NoSuchAlgorithmException e)
{
Log.d(TAG, "keyHash: name:"+e);
}
return "error";
}
public static void updateLanguage(Context context, String code)
{
Locale locale = new Locale(code);
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
}

What exactly is Signature class in Android and how to get signing certificate? [duplicate]

As all we do I have application which is signed by debug.keystore (by default) when it is in development mode (build). When it goes production we sign it with our private key.
Is there any way to determine at runtime that current package is signed with debug.keystore (is in development mode) or is signed with our private key (is in production mode).
I have tried something like
PackageManager packageManager = getPackageManager();
try {
Signature[] signs = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature signature : signs) {
Log.d(TAG, "sign = " + signature.toCharsString());
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
I don't know what to do next? Is this right way of doing this? How to obtain comparable debug.keystore signature?
I know that exists MD5 Fingerprint keytool -list -keystore ~/.android/debug.keystore but in Signature class there is not "md5 fingerprint"-like method.
I want to do this because of MapView Key, Logging, LicenseChecker and stuff like this.
The signature in PackageInfo does not seem to be well named since tha field does not contain the package signature but the signer X509 certificate chain. Note that (most of the time) this chain seems to be limited to one single self-signed certificate.
According to the Android developer page Signing Your Applications the debug signature certificate is generated with this DN: CN=Android Debug,O=Android,C=US
Therefore it is easy to test if the application has been signed in debug mode:
private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US");
/* ... */
Signature raw = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray()));
boolean debug = cert.getSubjectX500Principal().equals(DEBUG_DN);
Based on Jcs' answer, we use this to find out at runtime who built the running package:
private enum BuildSigner {
unknown,
Joe,
Carl,
Linda
}
private BuildSigner whoBuiltThis() {
try {
PackageManager packageManager = getPackageManager();
PackageInfo info = packageManager.getPackageInfo(getPackageName(),
PackageManager.GET_SIGNATURES);
Signature[] signs = info.signatures;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(signs[0].toByteArray()));
PublicKey key = cert.getPublicKey();
int modulusHash = ((RSAPublicKey)key).getModulus().hashCode();
switch (modulusHash) {
case 123456789:
return BuildSigner.Joe;
case 424242424:
return BuildSigner.Carl;
case -975318462:
return BuildSigner.Linda;
}
} catch (Exception e) {
}
return BuildSigner.unknown;
}
For any involved certificate, you then just have to find the hash once and add it to the list.
The simplest way to "find the hash once" may be to just add a popup toast before the switch statement that displays modulusHash, compile your app, run it, write down the hash, remove the toast code and add the hash to the list.
Alternatively, when I implemented this, I created a little boilerplate app with a single activity and a single TextView with the ID tv in the main layout, put this into the activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int hash = 0;
try{
PackageManager packageManager = getPackageManager();
PackageInfo info = packageManager.getPackageInfo(
"com.stackexchange.marvin", PackageManager.GET_SIGNATURES);
Signature[] signs = info.signatures;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(signs[0].toByteArray()));
PublicKey key = cert.getPublicKey();
hash = ((RSAPublicKey) key).getModulus().hashCode();
}catch(Exception e){}
TextView tv = ((TextView)findViewById(R.id.tv));
tv.setText("The Stack Exchange app's signature hash is " + hash + ".");
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24);
}
(change com.stackexchange.marvin to your app's name), compiled this mini-app, and sent the APK to all involved developers, asking them to run it on their dev device and let me know the displayed hash.

Android compare signature of current package with debug.keystore

As all we do I have application which is signed by debug.keystore (by default) when it is in development mode (build). When it goes production we sign it with our private key.
Is there any way to determine at runtime that current package is signed with debug.keystore (is in development mode) or is signed with our private key (is in production mode).
I have tried something like
PackageManager packageManager = getPackageManager();
try {
Signature[] signs = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature signature : signs) {
Log.d(TAG, "sign = " + signature.toCharsString());
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
I don't know what to do next? Is this right way of doing this? How to obtain comparable debug.keystore signature?
I know that exists MD5 Fingerprint keytool -list -keystore ~/.android/debug.keystore but in Signature class there is not "md5 fingerprint"-like method.
I want to do this because of MapView Key, Logging, LicenseChecker and stuff like this.
The signature in PackageInfo does not seem to be well named since tha field does not contain the package signature but the signer X509 certificate chain. Note that (most of the time) this chain seems to be limited to one single self-signed certificate.
According to the Android developer page Signing Your Applications the debug signature certificate is generated with this DN: CN=Android Debug,O=Android,C=US
Therefore it is easy to test if the application has been signed in debug mode:
private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US");
/* ... */
Signature raw = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray()));
boolean debug = cert.getSubjectX500Principal().equals(DEBUG_DN);
Based on Jcs' answer, we use this to find out at runtime who built the running package:
private enum BuildSigner {
unknown,
Joe,
Carl,
Linda
}
private BuildSigner whoBuiltThis() {
try {
PackageManager packageManager = getPackageManager();
PackageInfo info = packageManager.getPackageInfo(getPackageName(),
PackageManager.GET_SIGNATURES);
Signature[] signs = info.signatures;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(signs[0].toByteArray()));
PublicKey key = cert.getPublicKey();
int modulusHash = ((RSAPublicKey)key).getModulus().hashCode();
switch (modulusHash) {
case 123456789:
return BuildSigner.Joe;
case 424242424:
return BuildSigner.Carl;
case -975318462:
return BuildSigner.Linda;
}
} catch (Exception e) {
}
return BuildSigner.unknown;
}
For any involved certificate, you then just have to find the hash once and add it to the list.
The simplest way to "find the hash once" may be to just add a popup toast before the switch statement that displays modulusHash, compile your app, run it, write down the hash, remove the toast code and add the hash to the list.
Alternatively, when I implemented this, I created a little boilerplate app with a single activity and a single TextView with the ID tv in the main layout, put this into the activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int hash = 0;
try{
PackageManager packageManager = getPackageManager();
PackageInfo info = packageManager.getPackageInfo(
"com.stackexchange.marvin", PackageManager.GET_SIGNATURES);
Signature[] signs = info.signatures;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(signs[0].toByteArray()));
PublicKey key = cert.getPublicKey();
hash = ((RSAPublicKey) key).getModulus().hashCode();
}catch(Exception e){}
TextView tv = ((TextView)findViewById(R.id.tv));
tv.setText("The Stack Exchange app's signature hash is " + hash + ".");
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24);
}
(change com.stackexchange.marvin to your app's name), compiled this mini-app, and sent the APK to all involved developers, asking them to run it on their dev device and let me know the displayed hash.

Categories

Resources