I am trying to design an app which downloads the appropriate sound files from my Google cloud storage account. The app does not access the users account but my own.
My reading has led me to believe that the most appropriate model is The Service account https://code.google.com/p/google-api-java-client/wiki/OAuth2#Service_Accounts
Unfortunately the developers have decided not to provide an example with Android. They do provide a nice example with just plain Java, which works http://samples.google-api-java-client.googlecode.com/hg/storage-serviceaccount-cmdline-sample/instructions.html?r=default
I tried to adapt this for Android and have run into problems.
GoogleCredential credential =
new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY).setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(STORAGE_SCOPE)
.setServiceAccountPrivateKeyFromP12File(new File("key.p12")).build();
In Google's example they pass the key.p12 file , However on Android when I put the file in the res/raw folder it seems the only way I have of accessing it is as an input stream. I can't find an elegant way to get a file to pass to JSON.
This all leads me to believe I must be doing something wrong. Should I be using the key.p12 Should I be using the "service account model". Is there an example out there /
Thanks Ryan
An Update I managed to achieve my goal of getting it to work but my solution feels clunky to me and I am sure it is not the way intended
What I did was add the key.p12 as a raw/resource which I opened as as input stream. Which I then converted to the private key using the libraries as seen in the example.
http://www.flexiprovider.de/examples/ExampleSMIMEsign.html
My code looks like this
Security.addProvider(new de.flexiprovider.core.FlexiCoreProvider());
// Next, we have to read the private PKCS #12 file, since the the
// private key used for signing is contained in this file:
DERDecoder dec = new DERDecoder(getResources().openRawResource(
R.raw.key));
PFX pfx = new PFX();
try {
pfx.decode(dec);
SafeBag safeBag = pfx.getAuthSafe().getSafeContents(0)
.getSafeBag(0);
PKCS8ShroudedKeyBag kBag = (PKCS8ShroudedKeyBag) safeBag
.getBagValue();
char[] password = "my password from google api".toCharArray();
privKey = kBag.getPrivateKey(password);
new AsyncLoadStorage(this).execute();
} catch (ASN1Exception e) {
But the whole thing is ugly and I would like a cleaner solution
Google documentation is actually pretty misleading. They haven't updated their console links (keep on asking you to look for "register apps" link, which doesn't exist), and their JAVA api for GCS doesn't work in Android (GcsService service = GcsServiceFactory.createGcsService();). Seems like they expect you to access GCS from App engine only.
So, here's what one needs to do:
This works without any problems for me:
String STORAGE_SCOPE = "https://www.googleapis.com/auth/devstorage.read_write";
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
Log.d("testing", "checking if I can create a credential");
httpTransport = AndroidHttp.newCompatibleTransport();
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(resources_.openRawResource(R.raw.gcs_privatekey),
"password".toCharArray());
PrivateKey key = (PrivateKey) keystore.getKey("privatekey", "password".toCharArray());
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountPrivateKey(key)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(Collections.singleton(STORAGE_SCOPE))
// .setServiceAccountUser(SERVICE_ACCOUNT_EMAIL)
// .setClientSecrets(CLIENT_ID, CLIENT_SECRET)
.build();
credential.refreshToken();
String URI = "https://storage.googleapis.com/" + BUCKET_NAME;
HttpRequestFactory requestFactory = httpTransport.createRequestFactory(credential);
GenericUrl url = new GenericUrl(URI);
HttpRequest request = requestFactory.buildGetRequest(url);
HttpResponse response = request.execute();
String content = response.parseAsString();
Log.d("testing", "response content is: " + content);
new Storage.Builder(httpTransport, JSON_FACTORY, credential)
.setApplicationName("appname").build();
Here is what I did to transform the inputStream key to a PrivateKey object :
PrivateKey serviceAccountPrivateKey = SecurityUtils.loadPrivateKeyFromKeyStore(SecurityUtils.getPkcs12KeyStore(), MyClass.class.getResourceAsStream("/privatekey.p12"), "notasecret", "privatekey", "notasecret");
// Build service account credential.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(ACCOUNT_ID)
.setServiceAccountScopes(Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL))
//.setServiceAccountPrivateKeyFromP12File(f)
.setServiceAccountPrivateKey(serviceAccountPrivateKey)
.build();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With