Skip to content

Commit

Permalink
[WIP] Add support for GCP KMS (#66)
Browse files Browse the repository at this point in the history
* encryption working

* now deceryption is working + add support for protection level

* added tests for google KMS

* added support for KMS in the API

* push images temporary from branch

* fix image tag
  • Loading branch information
omerlh authored Jan 8, 2019
1 parent 486e040 commit cd4dcc8
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 15 deletions.
12 changes: 2 additions & 10 deletions ci/codefresh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,24 +67,16 @@ steps:
title: Push Encryptor Api - lastest
candidate: ${{BuildingEncryptorDockerImage}}
tags:
- encryptor-latest
- encryptor-${{CF_BRANCH_TAG_NORMALIZED}}
credentials:
username: ${{DOCKERHUB_REGISTRY_USERNAME}}
password: ${{DOCKERHUB_REGISTRY_PASSWORD}}
when:
branch:
only:
- master
PushDecryptorImage:
type: push
title: Push Decryptor Api - lastest
when:
branch:
only:
- master
candidate: ${{BuildingDecryptorDockerImage}}
tags:
- decryptor-latest
- decryptor-${{CF_BRANCH_TAG_NORMALIZED}}
credentials:
username: ${{DOCKERHUB_REGISTRY_USERNAME}}
password: ${{DOCKERHUB_REGISTRY_PASSWORD}}
40 changes: 39 additions & 1 deletion src/decrypt-api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;

using Google.Apis.Auth.OAuth2;
using Google.Apis.CloudKMS.v1;
using Google.Apis.Services;
using System.IO;

namespace Kamus
{
public class Startup {
Expand Down Expand Up @@ -67,6 +71,8 @@ public void ConfigureServices (IServiceCollection services) {
var provider = Configuration.GetValue<string>("KeyManagement:Provider");
switch (provider)
{
case "GoogleKms":
return GetGoogleCloudKeyManagment();
case "AzureKeyVault":
return new EnvelopeEncryptionDecorator(
new AzureKeyVaultKeyManagement(s.GetService<IKeyVaultClient>(), Configuration),
Expand Down Expand Up @@ -139,6 +145,38 @@ public void Configure (IApplicationBuilder app, IHostingEnvironment env) {
app.UseAuthentication();

app.UseMvc ();
}

private IKeyManagement GetGoogleCloudKeyManagment()
{
var location = Configuration.GetValue<string>("KeyManagement:GoogleKms:Location");
var keyRingName = Configuration.GetValue<string>("KeyManagement:GoogleKms:KeyRingName");
var protectionLevel = Configuration.GetValue<string>("KeyManagement:GoogleKms:ProtectionLevel");
var credentialsPath = Configuration.GetValue<string>("KeyManagement:GoogleKms:CredentialsPath");

var serviceAccountCredential = ServiceAccountCredential.FromServiceAccountData(File.OpenRead(credentialsPath));
var credentials = GoogleCredential.FromServiceAccountCredential(serviceAccountCredential);
if (credentials.IsCreateScopedRequired)
{
credentials = credentials.CreateScoped(new[]
{
CloudKMSService.Scope.CloudPlatform
});
}

var kmsService = new CloudKMSService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials,
GZipEnabled = true
});


return new GoogleCloudKeyManagment(
kmsService,
serviceAccountCredential.ProjectId,
keyRingName,
location,
protectionLevel);
}
}
}
3 changes: 0 additions & 3 deletions src/encrypt-api/Controllers/EncryptController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ public class EncryptController : Controller
private readonly IKeyManagement mKeyManagement;
private readonly ILogger mAuditLogger = Log.ForContext<EncryptController>().AsAudit();
private readonly ILogger mLogger = Log.ForContext<EncryptController>();

//see: https://github.com/kubernetes/kubernetes/blob/d5803e596fc8aba17aa8c74a96aff9c73bb0f1da/staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount/util.go#L27
private const string ServiceAccountUsernamePrefix = "system:serviceaccount:";

public EncryptController(IKeyManagement keyManagement)
{
Expand Down
40 changes: 39 additions & 1 deletion src/encrypt-api/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.CloudKMS.v1;
using Google.Apis.Services;
using Kamus.KeyManagement;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
Expand Down Expand Up @@ -51,7 +55,9 @@ public void ConfigureServices (IServiceCollection services) {
{
var provider = Configuration.GetValue<string>("KeyManagement:Provider");
switch (provider)
{
{
case "GoogleKms":
return GetGoogleCloudKeyManagment();
case "AzureKeyVault":
return new EnvelopeEncryptionDecorator(
new AzureKeyVaultKeyManagement(s.GetService<IKeyVaultClient>(), Configuration),
Expand Down Expand Up @@ -117,5 +123,37 @@ public void Configure (IApplicationBuilder app, IHostingEnvironment env) {

app.UseMvc ();
}

private IKeyManagement GetGoogleCloudKeyManagment()
{
var location = Configuration.GetValue<string>("KeyManagement:GoogleKms:Location");
var keyRingName = Configuration.GetValue<string>("KeyManagement:GoogleKms:KeyRingName");
var protectionLevel = Configuration.GetValue<string>("KeyManagement:GoogleKms:ProtectionLevel");
var credentialsPath = Configuration.GetValue<string>("KeyManagement:GoogleKms:CredentialsPath");

var serviceAccountCredential = ServiceAccountCredential.FromServiceAccountData(File.OpenRead(credentialsPath));
var credentials = GoogleCredential.FromServiceAccountCredential(serviceAccountCredential);
if (credentials.IsCreateScopedRequired)
{
credentials = credentials.CreateScoped(new[]
{
CloudKMSService.Scope.CloudPlatform
});
}

var kmsService = new CloudKMSService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials,
GZipEnabled = true
});


return new GoogleCloudKeyManagment(
kmsService,
serviceAccountCredential.ProjectId,
keyRingName,
location,
protectionLevel);
}
}
}
94 changes: 94 additions & 0 deletions src/key-managment/GoogleCloudKeyManagment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Google;
using Google.Apis.CloudKMS.v1;
using Google.Apis.CloudKMS.v1.Data;
using Microsoft.AspNetCore.WebUtilities;

namespace Kamus.KeyManagement
{
public class GoogleCloudKeyManagment : IKeyManagement
{
private readonly CloudKMSService mKmsService;
private readonly string mProjectName;
private readonly string mKeyringName;
private readonly string mKeyringLocation;
private readonly string mProtectionLevel;

public GoogleCloudKeyManagment(
CloudKMSService kmsService,
string projectName,
string keyringName,
string keyringLocation,
string protectionLevel)
{
mKmsService = kmsService;
mProjectName = projectName;
mKeyringName = keyringName;
mKeyringLocation = keyringLocation;
mProtectionLevel = protectionLevel;
}


public async Task<string> Decrypt(string encryptedData, string serviceAccountId)
{
var safeId = ComputeKeyId(serviceAccountId);
var cryptoKeys = mKmsService.Projects.Locations.KeyRings.CryptoKeys;
var keyringId = $"projects/{mProjectName}/locations/{mKeyringLocation}/keyRings/{mKeyringName}";
var keyId = $"{keyringId}/cryptoKeys/{safeId}";

var result = await cryptoKeys.Decrypt(new DecryptRequest
{
Ciphertext = encryptedData
}, keyId).ExecuteAsync();

return result.Plaintext;
}

public async Task<string> Encrypt(string data, string serviceAccountId, bool createKeyIfMissing = true)
{
var safeId = ComputeKeyId(serviceAccountId);
var cryptoKeys = mKmsService.Projects.Locations.KeyRings.CryptoKeys;
var keyringId = $"projects/{mProjectName}/locations/{mKeyringLocation}/keyRings/{mKeyringName}";
var keyId = $"{keyringId}/cryptoKeys/{safeId}";
try
{
await cryptoKeys.Get(keyId).ExecuteAsync();
} catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.NotFound && createKeyIfMissing)
{
//todo: handle key rotation - currently set to never expired
var key = new CryptoKey
{
Purpose = "ENCRYPT_DECRYPT",
VersionTemplate = new CryptoKeyVersionTemplate
{
ProtectionLevel = mProtectionLevel
}
};

var request = cryptoKeys.Create(key, keyringId);
request.CryptoKeyId = safeId;
await request.ExecuteAsync();
}

var encryted = await cryptoKeys.Encrypt(new EncryptRequest
{
Plaintext = data
}, keyId).ExecuteAsync();

return encryted.Ciphertext;
}

private string ComputeKeyId(string serviceUserName)
{
return
WebEncoders.Base64UrlEncode(
SHA256.Create().ComputeHash(
Encoding.UTF8.GetBytes(serviceUserName)))
.Replace("_", "-");
}
}
}
1 change: 1 addition & 0 deletions src/key-managment/key-managment.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Google.Apis.CloudKMS.v1" Version="1.36.1.1443" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.1.1" />
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
Expand Down
71 changes: 71 additions & 0 deletions tests/integration/GoogleCloudKeyManagment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.CloudKMS.v1;
using Google.Apis.Services;
using Kamus.KeyManagement;
using Microsoft.Extensions.Configuration;
using Xunit;

namespace integration
{
public class GoogleCloudKeyManagmentTests
{
private readonly IKeyManagement mGoogleCloudKeyManagement;
private readonly CloudKMSService mCloudKmsService;
private readonly IConfiguration mConfiguration;

public GoogleCloudKeyManagmentTests()
{
mConfiguration = new ConfigurationBuilder()
.AddJsonFile("settings.json")
.AddEnvironmentVariables().Build();

var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(mConfiguration.GetValue<string>("KeyManagment:GoogleKms:Credentials"));
writer.Flush();
stream.Position = 0;
var serviceAccountCredential = ServiceAccountCredential.FromServiceAccountData(stream);
var credentials = GoogleCredential.FromServiceAccountCredential(serviceAccountCredential);
if (credentials.IsCreateScopedRequired)
{
credentials = credentials.CreateScoped(new[]
{
CloudKMSService.Scope.CloudPlatform
});
}

mCloudKmsService = new CloudKMSService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials,
GZipEnabled = true
});
var location = mConfiguration.GetValue<string>("KeyManagment:GoogleKms:Location");
var keyRingName = mConfiguration.GetValue<string>("KeyManagment:GoogleKms:KeyRingName");
var protectionLevel = mConfiguration.GetValue<string>("KeyManagment:GoogleKms:ProtectionLevel");

mGoogleCloudKeyManagement = new GoogleCloudKeyManagment(
mCloudKmsService,
serviceAccountCredential.ProjectId,
keyRingName,
location,
protectionLevel);
}



[Fact]
public async Task TestFullFlow()
{
var sa = "sa:namespace";
var data = "data";
var encrypted = await mGoogleCloudKeyManagement.Encrypt(data, sa);
var decrypted = await mGoogleCloudKeyManagement.Decrypt(encrypted, sa);

Assert.Equal(data, decrypted);

}
}
}
1 change: 1 addition & 0 deletions tests/integration/integration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<ProjectReference Include="..\..\src\key-managment\key-managment.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Apis.CloudKMS.v1" Version="1.36.1.1443" />
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.2" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="4.4.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
Expand Down

0 comments on commit cd4dcc8

Please sign in to comment.