0

I've been trying to understand how to use the Google Cloud Key Management System and have worked through the quick start tutorials and documentation. I created my key rings and their individual keys and tested them using the SDK on my laptop and they work fine.

I then tried working through the Envelope Encryption example as shown at https://deliciousbrains.com/php-encryption-methods/ using my WAMP dev environment on my laptop. I've set up a service account and Environment Variable as described in https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-php.

However, when I try and run the code in my browser I get the following message:

Fatal error: Uncaught Google_Service_Exception: { "error": { "code": 401, "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.", "errors": [ { "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.", "domain": "global", "reason": "unauthorized" } ], "status": "UNAUTHENTICATED" } } in C:\Bitnami\wampstack-7.2.11-0\apache2\htdocs\racingaway\vendor\google\apiclient\src\Google\Http\REST.php:118 Stack trace: #0 C:\Bitnami\wampstack-7.2.11-0\apache2\htdocs\racingaway\vendor\google\apiclient\src\Google\Http\REST.php(94): Google_Http_REST::decodeHttpResponse(Object(GuzzleHttp\Psr7\Response), Object(GuzzleHttp\Psr7\Request), 'Goo in C:\Bitnami\wampstack-7.2.11-0\apache2\htdocs\racingaway\vendor\google\apiclient\src\Google\Http\REST.php on line 118

I have tried following the link in the above message (https://developers.google.com/identity/sign-in/web/devconsole-project), this takes me to https://developers.google.com/identity/sign-in/web/sign-in#before_you_begin, which appears to be about setting up an OAuth client for an end user. However, what I want to do is a server to server authentication. I am now rather confused and would greatly appreciate some sage advice as to what I need to do to get my test application to work.

Code I have used is below:

<?php

use Google_Service_CloudKMS as Kms;
use Google_Service_CloudKMS_DecryptRequest as DecryptRequest;
use Google_Service_CloudKMS_EncryptRequest as EncryptRequest;

class KeyManager
{
    private $kms;
    private $encryptRequest;
    private $decryptRequest;
    private $projectId;
    private $locationId;
    private $keyRingId;
    private $cryptoKeyId;

public function __construct(Kms $kms, EncryptRequest $encryptRequest, DecryptRequest $decryptRequest, $projectId, $locationId, $keyRingId, $cryptoKeyId)
{
    $this->kms            = $kms;
    $this->encryptRequest = $encryptRequest;
    $this->decryptRequest = $decryptRequest;
    $this->projectId      = $projectId;
    $this->locationId     = $locationId;
    $this->keyRingId      = $keyRingId;
    $this->cryptoKeyId    = $cryptoKeyId;
}

public function encrypt($data)
{
    $key        = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
    $nonce      = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
    $ciphertext = sodium_crypto_secretbox($data, $nonce, $key);

    return [
        'data'   => base64_encode($nonce . $ciphertext),
        'secret' => $this->encryptKey($key),
    ];
}

public function decrypt($secret, $data)
{
    $decoded    = base64_decode($data);
    $key        = $this->decryptSecret($secret);
    $nonce      = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');

    return sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
}

private function encryptKey($key)
{
    $this->encryptRequest->setPlaintext(base64_encode($key));

    $response = $this->kms->projects_locations_keyRings_cryptoKeys->encrypt(
        $this->getResourceName(),
        $this->encryptRequest
    );

    return $response['ciphertext'];
}

private function decryptSecret($secret)
{
    $this->decryptRequest->setCiphertext($secret);

    $response = $this->kms->projects_locations_keyRings_cryptoKeys->decrypt(
        $this->getResourceName(),
        $this->decryptRequest
    );

    return base64_decode($response['plaintext']);
}

public function getResourceName()
{
    return sprintf(
        'projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s',
        $this->projectId,
        $this->locationId,
        $this->keyRingId,
        $this->cryptoKeyId
    );
}

}

The above class is used by the code below:

<?php

require_once( "get_paths.php" );
require_once( GetIncludePath()."class.keymanager.php" );

use Google_Service_CloudKMS as Kms;
use Google_Service_CloudKMS_DecryptRequest as DecryptRequest;
use Google_Service_CloudKMS_EncryptRequest as EncryptRequest;

$client = new Google_Client();
$client->setAuthConfig(getenv('GOOGLE_APPLICATION_CREDENTIALS'));
$client->addScope('https://www.googleapis.com/auth/cloud-platform');

$keyManager = new KeyManager(
    new Kms($client),
    new EncryptRequest(),
    new DecryptRequest(),
 //   $projectId,
     "snappy-thought-226213",
  //  $locationId,
     "europe-west2",
  //  $keyRingId,
    "racingaway_key_ring",
  //  $cryptoKeyId
    "racingaway_key_2"
);


$encrypted = $keyManager->encrypt('This is a secret!');
var_dump($encrypted);

?>
  • I agree that the authentication link in the docs is not what you want, because you want service account authentication (you are having your backend do requests on its own authority). The information in https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-php is what you need. Unfortunately, there isn't enough info in your error to determine the problem. Is it possible for you to dump the HTTP request being sent? I'm not familiar with the PHP libraries you are using. – Tim Dierks Jan 02 '19 at 14:45
  • Thanks Tim, I've been through the material in the link you mentioned but am none the wiser. What I've done so far is: set up a Project, create a Service Account for it and give it the role of Project Owner and create a key for it and dow – NumptyDumpty Jan 02 '19 at 22:31
  • nloaded the JSON credentials file and saved this to an accessible location and set an environment variable to point to it. I have also set up a Key Ring and attached a Key to it with no members assigned to either. I had tried assigning the service account's auto generated email address as a Member to the Key Ring and to the Key but am not permitted by the console to do this. – NumptyDumpty Jan 02 '19 at 22:45

0 Answers0