Search code examples
javagoogle-app-enginegoogle-cloud-storagegoogle-api-java-clientservice-accounts

How to use the Google cloud platform service account key generated using serviceAccounts.keys.create API, at the client side in Java?


I am using the service account key generated in GAE using the serviceAccounts.keys.create Google IAM API at the client side to upload files to GCS. This API returns privateKeyData field which I am using in the client side code as follows:

public class GCSTest {
    public static final String CLOUD_STORAGE_SCOPE =
            "https://www.googleapis.com/auth/cloud-platform";
    private GoogleCredential credential = null;
    private HttpTransport HTTP_TRANSPORT = null;
    private Storage storage = null;
    private final JsonFactory json_factory = JacksonFactory
            .getDefaultInstance();
    public String APPLICATION_NAME = "GCSTest";
    private String privKeyString =
            <copy the privateKeyData from response of serviceAccounts.keys.create>;

    public void startTests() {
        initStorageService();
        writeObject();
    }

    private void initStorageService() {
        List<String> scopeList = new ArrayList<String>();
        scopeList.add(CLOUD_STORAGE_SCOPE);
        HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

        byte[] privKeyBytes = Base64.decodeBase64(privKeyString.getBytes());
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privKeyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = kf.generatePrivate(spec);
        credential =
            new GoogleCredential.Builder()
                .setTransport(HTTP_TRANSPORT)
                .setJsonFactory(json_factory)
                .setServiceAccountId(
                        "<service_account_name>@<project-id>.iam.gserviceaccount.com")
                .setServiceAccountScopes(scopeList)
                .setServiceAccountPrivateKey(privateKey)
                .setServiceAccountPrivateKeyId(
                        "<key_id>")
                .build();

        storage =
            new Storage.Builder(HTTP_TRANSPORT, json_factory,
                credential).setApplicationName(APPLICATION_NAME)
                .build();
    }
    private void writeObject() {
        String fileName = "StorageSample";
        Path tempPath = Files.createTempFile(fileName, ".txt");;
        Files.write(tempPath, "Sample file".getBytes());
        File tempFile = tempPath.toFile();
        tempFile.deleteOnExit();

        InputStreamContent contentStream =
                new InputStreamContent("text/plain", new FileInputStream(
                        tempFile));
        contentStream.setLength(tempFile.length());
        StorageObject objectMetadata = new StorageObject().setName(fileName);
        Storage.Objects.Insert insert = 
            storage.objects().insert(<bucket_name>,
                        objectMetadata, contentStream);;
        insert.setName(fileName);
        insert.execute();
    }
}

However, I get java.security.InvalidKeyException: invalid key format exception when storage.objects().insert is called. Is this the correct way of using the service account key to generate the GoogleCredential? P.S: I know about the JSON or P12 key file. I don't want to use it since it doesn't fit my use case.


Solution

  • Turns out that the password for the private key string generated using serviceAccounts.keys.create is notasecret. The following code gets the PrivateKey from the privateKeyData field from the API response. This PrivateKey can be used to obtain GoogleCredential

    byte[] privKeyBytes = Base64.decodeBase64(privKeyString.getBytes());
    KeyStore ks = KeyStore.getInstance("PKCS12");
    ks.load(new ByteArrayInputStream(privKeyBytes), "notasecret".toCharArray());
    PrivateKey privateKey = (PrivateKey) ks.getKey("privatekey", "notasecret".toCharArray());