Skip to content

Instantly share code, notes, and snippets.

@JosXa
Created October 18, 2019 09:16
Show Gist options
  • Save JosXa/2e1925c58077fe97871bb56697128355 to your computer and use it in GitHub Desktop.
Save JosXa/2e1925c58077fe97871bb56697128355 to your computer and use it in GitHub Desktop.
using System;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.KeyVault;
namespace MyNamespace
{
public enum CertificateType
{
Pkcs12,
Pem
}
/// <summary>
/// Loosely based on https://github.com/Azure/azure-functions-host/issues/3907#issuecomment-473105492
/// </summary>
public class KeyVaultReferenceSecretRetriever
{
private static readonly bool isLocalEnvironment =
string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"));
public async Task<string> RetrieveConfigValue(string settingName)
{
if (isLocalEnvironment)
{
var referenceString = Environment.GetEnvironmentVariable(settingName);
if (referenceString == null)
{
throw new ArgumentException($"Setting for {settingName} could not be found.");
}
if (!referenceString.StartsWith("@"))
{
throw new ArgumentException(
$"Can only work with key vault references, but this isn't one: {referenceString}",
nameof(settingName));
}
// TODO: maybe there is a builtin somewhere that will parse these references for us...
var match = Regex.Match(referenceString, @"@Microsoft\.KeyVault\(SecretUri=(.*)\)");
var configValue = match.Groups[1].Value;
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
KeyVaultClient keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await keyVaultClient.GetSecretAsync(configValue).ConfigureAwait(false);
return secret.Value;
}
return Environment.GetEnvironmentVariable(settingName);
}
[ItemNotNull]
public async Task<X509Certificate> RetrieveCertificate(string settingName, CertificateType type)
{
string certificateAsString = await RetrieveConfigValue(settingName);
switch (type)
{
case CertificateType.Pem:
byte[] certBuffer = GetBytesFromPEM(certificateAsString, "CERTIFICATE");
return new X509Certificate2(certBuffer);
case CertificateType.Pkcs12:
// TODO: so far untested
return new X509Certificate(Encoding.ASCII.GetBytes(certificateAsString));
default:
throw new ArgumentException($"Unknown {nameof(CertificateType)}.");
}
}
byte[] GetBytesFromPEM(string pemString, string section)
{
var header = $"-----BEGIN {section}-----";
var footer = $"-----END {section}-----";
var start = pemString.IndexOf(header, StringComparison.Ordinal);
if (start < 0)
return null;
start += header.Length;
var end = pemString.IndexOf(footer, start, StringComparison.Ordinal) - start;
if (end < 0)
return null;
return Convert.FromBase64String(pemString.Substring(start, end));
}
}
}
@vinhdev17
Copy link

Hi JosXa, can you give me an example to implemented? Thanks

@JosXa
Copy link
Author

JosXa commented Dec 9, 2020

@avitryhard Hmm this is all I have right now. Do you have any concrete questions?

@vinhdev17
Copy link

I have one case as bellow:
I'm using EF core code first. While I'm updating database by using termimal in my computer (dotnet ef database update). I want to get connection string from keyvault.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment