Browse Source

pkcs11 rsa key service implementation for yubihsm

pull/2/head
Kyle Spearrin 4 years ago
parent
commit
0353b7b785
  1. 1
      src/CryptoAgent/CryptoAgent.csproj
  2. 9
      src/CryptoAgent/CryptoAgentSettings.cs
  3. 9
      src/CryptoAgent/Dockerfile
  4. 197
      src/CryptoAgent/Services/Pkcs11RsaKeyService.cs
  5. 7
      src/CryptoAgent/Startup.cs

1
src/CryptoAgent/CryptoAgent.csproj

@ -26,6 +26,7 @@ @@ -26,6 +26,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.9" />
<PackageReference Include="MongoDB.Driver" Version="2.13.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.7" />
<PackageReference Include="Pkcs11Interop" Version="5.1.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.2.0" />

9
src/CryptoAgent/CryptoAgentSettings.cs

@ -57,6 +57,15 @@ @@ -57,6 +57,15 @@
public string AwsAccessKeySecret { get; set; }
public string AwsRegion { get; set; }
public string AwsKeyId { get; set; }
// pkcs11
// yubihsm2
public string Pkcs11Provider { get; set; }
public string Pkcs11LibraryPath { get; set; }
public string Pkcs11SlotTokenSerialNumber { get; set; }
public string Pkcs11LoginUserType { get; set; }
public string Pkcs11LoginPin { get; set; }
public string Pkcs11PrivateKeyLabel { get; set; }
public ulong? Pkcs11PrivateKeyId { get; set; }
// Other HSMs...
}

9
src/CryptoAgent/Dockerfile

@ -8,6 +8,15 @@ RUN apt-get update \ @@ -8,6 +8,15 @@ RUN apt-get update \
curl \
&& rm -rf /var/lib/apt/lists/*
# Install YubiHSM2 SDK
ADD https://developers.yubico.com/YubiHSM2/Releases/yubihsm2-sdk-2021-08-debian10-amd64.tar.gz ./
RUN tar -xzf yubihsm2-sdk-*.tar.gz \
&& rm yubihsm2-sdk-*.tar.gz \
&& dpkg -i yubihsm2-sdk/libyubihsm-http1_*_amd64.deb \
&& dpkg -i yubihsm2-sdk/libyubihsm1_*_amd64.deb \
&& dpkg -i yubihsm2-sdk/yubihsm-pkcs11_*_amd64.deb \
&& apt-get install -f
ENV ASPNETCORE_URLS http://+:5000
WORKDIR /app
EXPOSE 5000

197
src/CryptoAgent/Services/Pkcs11RsaKeyService.cs

@ -0,0 +1,197 @@ @@ -0,0 +1,197 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Net.Pkcs11Interop.Common;
using Net.Pkcs11Interop.HighLevelAPI;
namespace Bit.CryptoAgent.Services
{
public class Pkcs11RsaKeyService : IRsaKeyService
{
private readonly ICertificateProviderService _certificateProviderService;
private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly CryptoAgentSettings _settings;
private X509Certificate2 _certificate;
public Pkcs11RsaKeyService(
ICertificateProviderService certificateProviderService,
ICryptoFunctionService cryptoFunctionService,
CryptoAgentSettings settings)
{
_certificateProviderService = certificateProviderService;
_cryptoFunctionService = cryptoFunctionService;
_settings = settings;
}
public async Task<byte[]> EncryptAsync(byte[] data)
{
if (data == null)
{
return null;
}
var encData = await _cryptoFunctionService.RsaEncryptAsync(data, await GetPublicKeyAsync());
return encData;
}
public Task<byte[]> DecryptAsync(byte[] data)
{
if (data == null)
{
return null;
}
using var library = LoadLibrary();
using var session = CreateNewSession(library);
var privateKey = GetPrivateKey(session);
var mechanismParams = session.Factories.MechanismParamsFactory.CreateCkRsaPkcsOaepParams(
ConvertUtils.UInt64FromCKM(CKM.CKM_SHA_1),
ConvertUtils.UInt64FromCKG(CKG.CKG_MGF1_SHA1),
ConvertUtils.UInt64FromUInt32(CKZ.CKZ_DATA_SPECIFIED),
null);
var mechanism = session.Factories.MechanismFactory.Create(CKM.CKM_RSA_PKCS_OAEP, mechanismParams);
var plainData = session.Decrypt(mechanism, privateKey, data);
Cleanup(session, privateKey);
return Task.FromResult(plainData);
}
public Task<byte[]> SignAsync(byte[] data)
{
if (data == null)
{
return null;
}
using var library = LoadLibrary();
using var session = CreateNewSession(library);
var privateKey = GetPrivateKey(session);
var mechanism = session.Factories.MechanismFactory.Create(CKM.CKM_SHA256_RSA_PKCS);
var signature = session.Sign(mechanism, privateKey, data);
Cleanup(session, privateKey);
return Task.FromResult(signature);
}
public async Task<bool> VerifyAsync(byte[] data, byte[] signature)
{
if (data == null || signature == null)
{
return false;
}
return await _cryptoFunctionService.RsaVerifyAsync(data, signature, await GetPublicKeyAsync());
}
public async Task<byte[]> GetPublicKeyAsync()
{
var certificate = await GetCertificateAsync();
return certificate.GetRSAPublicKey().ExportSubjectPublicKeyInfo();
}
private async Task<X509Certificate2> GetCertificateAsync()
{
if (_certificate == null)
{
_certificate = await _certificateProviderService.GetCertificateAsync();
}
return _certificate;
}
private IObjectHandle GetPrivateKey(ISession session)
{
var attributes = new List<IObjectAttribute>
{
session.Factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY),
session.Factories.ObjectAttributeFactory.Create(CKA.CKA_TOKEN, true)
};
if (_settings.RsaKey.Pkcs11PrivateKeyId.HasValue)
{
attributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_ID,
_settings.RsaKey.Pkcs11PrivateKeyId.Value));
}
else
{
attributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_LABEL,
_settings.RsaKey.Pkcs11PrivateKeyLabel));
}
var objects = session.FindAllObjects(attributes);
if (objects.Count == 0)
{
throw new System.Exception("Private key not found.");
}
else if (objects.Count > 1)
{
throw new System.Exception("More than one private key was found. Use a more specific identifier.");
}
return objects.Single();
}
private IPkcs11Library LoadLibrary()
{
var libPath = _settings.RsaKey.Pkcs11LibraryPath;
if (string.IsNullOrWhiteSpace(libPath))
{
var provider = _settings.RsaKey.Pkcs11Provider?.ToLowerInvariant();
if (provider == "yubihsm2")
{
// TODO: Verify that this path works for Debian-installed YubiHSM SDKs
libPath = "/usr/lib/x86_64-linux-gnu/pkcs11/yubihsm_pkcs11.so";
}
}
var factories = new Pkcs11InteropFactories();
return factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, libPath, AppType.MultiThreaded);
}
private ISession CreateNewSession(IPkcs11Library library)
{
ISlot chosenSlot = null;
var slots = library.GetSlotList(SlotsType.WithOrWithoutTokenPresent);
foreach (var slot in slots)
{
var slotInfo = slot.GetSlotInfo();
if (slotInfo.SlotFlags.TokenPresent)
{
var tokenInfo = slot.GetTokenInfo();
if (tokenInfo.SerialNumber == _settings.RsaKey.Pkcs11SlotTokenSerialNumber)
{
chosenSlot = slot;
break;
}
}
}
if (chosenSlot == null)
{
return null;
}
var session = chosenSlot.OpenSession(SessionType.ReadWrite);
var userType = CKU.CKU_USER;
var userTypeSetting = _settings.RsaKey.Pkcs11LoginUserType?.ToLowerInvariant();
if (userTypeSetting == "so")
{
userType = CKU.CKU_SO;
}
else if (userTypeSetting == "context_specific")
{
userType = CKU.CKU_CONTEXT_SPECIFIC;
}
session.Login(userType, _settings.RsaKey.Pkcs11LoginPin);
return session;
}
private void Cleanup(ISession session, IObjectHandle privateKey)
{
session.DestroyObject(privateKey);
session.Logout();
}
}
}

7
src/CryptoAgent/Startup.cs

@ -128,9 +128,16 @@ namespace Bit.CryptoAgent @@ -128,9 +128,16 @@ namespace Bit.CryptoAgent
private void AddRsaKeyProvider(IServiceCollection services, CryptoAgentSettings settings)
{
var rsaKeyProvider = settings.RsaKey.Provider?.ToLowerInvariant();
if (rsaKeyProvider == "certificate" || rsaKeyProvider == "pkcs11")
{
if (rsaKeyProvider == "certificate")
{
services.AddSingleton<IRsaKeyService, LocalCertificateRsaKeyService>();
}
else if (rsaKeyProvider == "pkcs11")
{
services.AddSingleton<IRsaKeyService, Pkcs11RsaKeyService>();
}
var certificateProvider = settings.Certificate.Provider?.ToLowerInvariant();
if (certificateProvider == "store")

Loading…
Cancel
Save