Fix docker and store PK in KeyVault only (#27)

-The private key is now only stored in Key Vault. Key generation is back to service, as it slows down operation quite a bit and enforces delete rights for an Approver.
-Fix the docker builds and according build settings
-Other improvements: Use only a single CosmosDB collection (cost)
-license update
-CA certs can contain a CRL distribution point (however service side doesn't support it)
This commit is contained in:
Martin Regen 2019-01-23 10:14:29 +01:00 коммит произвёл GitHub
Родитель 62198c68cd
Коммит d4e5505bca
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
35 изменённых файлов: 484 добавлений и 231 удалений

Просмотреть файл

@ -1,15 +0,0 @@
language: csharp
mono: none
dotnet: 2.1
os: linux
sudo: false
dist: trusty
addons:
apt:
sources:
packages:
install:
script:
- dotnet restore
- dotnet build -c Debug
- dotnet build -c Release

Просмотреть файл

@ -7,7 +7,7 @@ WORKDIR /src
COPY src/Microsoft.Azure.IIoT.OpcUa.Services.Vault.csproj src/
COPY NuGet.Config NuGet.Config
RUN dotnet restore --configfile NuGet.Config -nowarn:msb3202,nu1503 src/Microsoft.Azure.IIoT.OpcUa.Services.Vault.csproj
COPY . .
COPY src/ src/
WORKDIR /src/src
RUN dotnet build Microsoft.Azure.IIoT.OpcUa.Services.Vault.csproj -c Release -o /app

Просмотреть файл

@ -7,7 +7,7 @@ WORKDIR /src
COPY src/Microsoft.Azure.IIoT.OpcUa.Services.Vault.csproj src/
COPY NuGet.Config NuGet.Config
RUN dotnet restore --configfile NuGet.Config -nowarn:msb3202,nu1503 src/Microsoft.Azure.IIoT.OpcUa.Services.Vault.csproj
COPY . .
COPY src/ src/
WORKDIR /src/src
RUN dotnet build Microsoft.Azure.IIoT.OpcUa.Services.Vault.csproj -c Release -o /app

Просмотреть файл

@ -7,7 +7,7 @@ WORKDIR /src
COPY src/Microsoft.Azure.IIoT.OpcUa.Services.Vault.csproj src/
COPY NuGet.Config NuGet.Config
RUN dotnet restore --configfile NuGet.Config -nowarn:msb3202,nu1503 src/Microsoft.Azure.IIoT.OpcUa.Services.Vault.csproj
COPY . .
COPY src/ src/
WORKDIR /src/src
RUN dotnet build Microsoft.Azure.IIoT.OpcUa.Services.Vault.csproj -c Release -o /app

23
Dockerfile.app Normal file
Просмотреть файл

@ -0,0 +1,23 @@
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 56310
EXPOSE 44342
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY app/Microsoft.Azure.IIoT.OpcUa.Services.Vault.App.csproj app/
COPY api-csharp/Microsoft.Azure.IIoT.OpcUa.Api.Vault.csproj api-csharp/
RUN dotnet restore app/Microsoft.Azure.IIoT.OpcUa.Services.Vault.App.csproj
COPY app app/
COPY api-csharp api-csharp/
WORKDIR /src/app
RUN dotnet build Microsoft.Azure.IIoT.OpcUa.Services.Vault.App.csproj -c Release -o /app
FROM build AS publish
RUN dotnet publish Microsoft.Azure.IIoT.OpcUa.Services.Vault.App.csproj -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Microsoft.Azure.IIoT.OpcUa.Services.Vault.App.dll"]

22
Dockerfile.module Normal file
Просмотреть файл

@ -0,0 +1,22 @@
FROM microsoft/dotnet:2.2-runtime AS base
WORKDIR /module
EXPOSE 58850-58852
FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY module/Microsoft.Azure.IIoT.OpcUa.Modules.Vault.csproj module/
COPY api-csharp/Microsoft.Azure.IIoT.OpcUa.Api.Vault.csproj api-csharp/
RUN dotnet restore module/Microsoft.Azure.IIoT.OpcUa.Modules.Vault.csproj
COPY module module/
COPY api-csharp api-csharp/
WORKDIR /src/module
RUN dotnet build Microsoft.Azure.IIoT.OpcUa.Modules.Vault.csproj -c Release -o /module
FROM build AS publish
RUN dotnet publish Microsoft.Azure.IIoT.OpcUa.Modules.Vault.csproj -c Release -o /module
FROM base AS final
WORKDIR /module
COPY --from=publish /module .
ENTRYPOINT ["dotnet", "Microsoft.Azure.IIoT.OpcUa.Modules.Vault.dll"]

Просмотреть файл

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nugetv3" value="https://api.nuget.org/v3/index.json" />
<add key="myget" value="https://www.myget.org/F/azureiiot/api/v3/index.json" />
</packageSources>
</configuration>
<packageSources>
<add key="nugetv3" value="https://api.nuget.org/v3/index.json" />
<add key="myget" value="https://www.myget.org/F/azureiiot/api/v3/index.json" />
</packageSources>
</configuration>

Просмотреть файл

@ -1,5 +1,7 @@
## Azure Industrial IoT Services
[![Build status](https://msazure.visualstudio.com/One/_apis/build/status/Custom/Azure_IOT/Industrial/Components/ci-azure-iiot-opc-vault-service)](https://msazure.visualstudio.com/One/_build/latest?definitionId=44197)
### OPC Unified Architecture (OPC UA) Certificate Management Service
The certificate management service for OPC UA facilitates a CA certificate cloud service for OPC UA devices

Просмотреть файл

@ -1,20 +0,0 @@
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 56310
EXPOSE 44342
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY Microsoft.Azure.IIoT.OpcUa.Services.Vault.App.csproj Vault.App/
RUN dotnet restore Vault.App/Microsoft.Azure.IIoT.OpcUa.Services.Vault.App.csproj
COPY . .
WORKDIR /src/Vault.App
RUN dotnet build Microsoft.Azure.IIoT.OpcUa.Services.Gds.Vault.csproj -c Release -o /app
FROM build AS publish
RUN dotnet publish Microsoft.Azure.IIoT.OpcUa.Services.Gds.Vault.csproj -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Microsoft.Azure.IIoT.OpcUa.Services.Vault.App.dll"]

Просмотреть файл

@ -5,18 +5,13 @@ VisualStudioVersion = 15.0.27130.2036
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8289AFAE-0933-40EF-9D19-6FFA2DB04837}"
ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
.travis.yml = .travis.yml
common.props = common.props
CONTRIBUTING.md = CONTRIBUTING.md
DEVELOPMENT.md = DEVELOPMENT.md
src\Dockerfile = src\Dockerfile
app\Dockerfile = app\Dockerfile
src\Dockerfile.Windows = src\Dockerfile.Windows
LICENSE = LICENSE
Directory.Build.props = Directory.Build.props
license.txt = license.txt
NuGet.Config = NuGet.Config
project.props = project.props
version.props = version.props
EndProjectSection
@ -33,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.IIoT.OpcUa.
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{E9C6A792-1D70-4DE9-B5FB-8D8BCAA223EE}"
ProjectSection(SolutionItems) = preProject
CONTRIBUTING.md = CONTRIBUTING.md
DEVELOPMENT.md = DEVELOPMENT.md
docs\howto-deploy-services.md = docs\howto-deploy-services.md
docs\howto-run-services-locally.md = docs\howto-run-services-locally.md
README.md = README.md
@ -45,6 +42,22 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deploy", "deploy", "{A3FF1E
deploy\KeyVault.Secret.Groups.json = deploy\KeyVault.Secret.Groups.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cloud", "cloud", "{ECDA661E-A357-4472-B98F-3E0D807965A9}"
ProjectSection(SolutionItems) = preProject
deploy\cloud\run.ps1 = deploy\cloud\run.ps1
deploy\cloud\template.json = deploy\cloud\template.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker", "docker", "{1724C572-75C3-4A4C-A710-3D8C3A3223BF}"
ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore
Dockerfile = Dockerfile
Dockerfile.app = Dockerfile.app
Dockerfile.module = Dockerfile.module
Dockerfile.Windows = Dockerfile.Windows
Dockerfile.Windows-Server2016 = Dockerfile.Windows-Server2016
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -75,6 +88,9 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{ECDA661E-A357-4472-B98F-3E0D807965A9} = {A3FF1EEF-11DA-470B-9C98-B043B3CB4DE7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F947AAE7-2D6B-43C0-B5E6-824B8E4D7992}
EndGlobalSection

Просмотреть файл

@ -1 +1,14 @@
[{"Id":"Default","CertificateType":"RsaSha256ApplicationCertificateType","SubjectName":"CN=Azure Industrial IoT CA, O=Microsoft Corp.","BaseStorePath":"/default","DefaultCertificateLifetime":12,"DefaultCertificateKeySize":2048,"DefaultCertificateHashSize":256,"CACertificateLifetime":60,"CACertificateKeySize":2048,"CACertificateHashSize":256}]
[
{
"Id": "Default",
"CertificateType": "RsaSha256ApplicationCertificateType",
"SubjectName": "CN=Azure Industrial IoT CA, O=Microsoft Corp.",
"BaseStorePath": "/default",
"DefaultCertificateLifetime": 12,
"DefaultCertificateKeySize": 2048,
"DefaultCertificateHashSize": 256,
"CACertificateLifetime": 60,
"CACertificateKeySize": 2048,
"CACertificateHashSize": 256
}
]

Просмотреть файл

@ -703,7 +703,7 @@ Function GetAzureADApplicationConfig() {
$requiredResourcesAccess = `
New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]
$requiredPermissions = GetRequiredPermissions -servicePrincipal $serviceServicePrincipal `
-requiredDelegatedPermissions "user_impersonation" # "Directory.Read.All|User.Read"
-requiredDelegatedPermissions "user_impersonation"
$requiredResourcesAccess.Add($requiredPermissions)
$requiredPermissions = GetRequiredPermissions -appId "00000002-0000-0000-c000-000000000000" `
-requiredDelegatedPermissions "User.Read"
@ -721,7 +721,7 @@ Function GetAzureADApplicationConfig() {
$requiredResourcesAccess = `
New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]
$requiredPermissions = GetRequiredPermissions -servicePrincipal $serviceServicePrincipal `
-requiredDelegatedPermissions "user_impersonation" # "Directory.Read.All|User.Read"
-requiredDelegatedPermissions "user_impersonation"
$requiredResourcesAccess.Add($requiredPermissions)
$requiredPermissions = GetRequiredPermissions -appId "00000002-0000-0000-c000-000000000000" `
-requiredDelegatedPermissions "User.Read"
@ -936,8 +936,8 @@ Write-Host "--secret="$aadConfig.ModuleSecret
Write-Host "--tenantid="$aadConfig.TenantId
# prepare the GDS module docker image
cd ..\module\docker\linux
.\dockerbuild.bat
cd ..
docker build --file .\Dockerfile.module -t edgeopcvault .
cd $deploydir
# create batch file for user to start GDS docker container

Просмотреть файл

@ -1,7 +1,10 @@
MIT License
Azure Industrial IoT Certificate Management Service
Copyright (c) Microsoft Corporation. All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
@ -19,3 +22,5 @@
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

Просмотреть файл

@ -1,6 +0,0 @@
FROM microsoft/dotnet:2.2-runtime
COPY ./publish /publish
WORKDIR /publish
ENTRYPOINT ["dotnet", "Microsoft.Azure.IIoT.OpcUa.Modules.Vault.dll"]

Просмотреть файл

@ -1,4 +0,0 @@
dotnet build ..\..\Microsoft.Azure.IIoT.OpcUa.Modules.Vault.csproj
dotnet publish ..\..\Microsoft.Azure.IIoT.OpcUa.Modules.Vault.csproj -o ./docker/linux/publish
docker build -t edgeopcvault .

Просмотреть файл

@ -1,4 +0,0 @@
rem start docker with mapped logs
rem push image: docker push mregen/edgeopcvault:latest
docker run -it -p 58850-58852:58850-58852 -e 58850-58852 -h %COMPUTERNAME% -v "/c/GDS:/root/.local/share/Microsoft/GDS" edgeopcvault:latest --vault="https://vault012-service.azurewebsites.net" --resource="46f44d87-87a2-4d91-ad34-f0ed5d6031ed" --clientid="f5a38dd7-4282-49eb-b1f3-d50b72462588" --secret="ydZ0rxTzsDrik09c4sFRKmF0jgNO0yAB+93vcdRLCs4=" --tenantid="660722d6-c658-431c-8b2e-a157f3134da5"

Просмотреть файл

@ -27,6 +27,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
const int _defaultRecordsPerQuery = 10;
private readonly ILogger _log;
private readonly string _endpoint;
private readonly string _dataBaseId;
private readonly string _collectionId;
private readonly SecureString _authKeyOrResourceToken;
private readonly ILifetimeScope _scope = null;
@ -37,6 +39,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
{
_scope = scope;
_endpoint = config.CosmosDBEndpoint;
_dataBaseId = config.CosmosDBDatabase;
_collectionId = config.CosmosDBCollection;
_authKeyOrResourceToken = new SecureString();
foreach (char ch in config.CosmosDBToken)
{
@ -389,8 +393,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
#region Private Members
private void Initialize()
{
_db = new DocumentDBRepository(_endpoint, _authKeyOrResourceToken);
_applications = new DocumentDBCollection<Application>(_db);
_db = new DocumentDBRepository(_endpoint, _dataBaseId, _authKeyOrResourceToken);
_applications = new DocumentDBCollection<Application>(_db, _collectionId);
}
private string CreateServerQuery(uint startingRecordId, uint maxRecordsToQuery)

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -42,11 +42,16 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
);
Task<Opc.Ua.Gds.Server.X509Certificate2KeyPair> NewKeyPairRequestAsync(
string id,
string requestId,
string applicationUri,
string subjectName,
string[] domainNames,
string privateKeyFormat,
string privateKeyPassword
);
Task<byte[]> LoadPrivateKeyAsync(string id, string requestId, string privateKeyFormat);
Task AcceptPrivateKeyAsync(string id, string requestId);
Task DeletePrivateKeyAsync(string id, string requestId);
}
}

Просмотреть файл

@ -216,6 +216,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
/// <inheritdoc/>
public async Task<Opc.Ua.Gds.Server.X509Certificate2KeyPair> NewKeyPairRequestAsync(
string id,
string requestId,
string applicationUri,
string subjectName,
string[] domainNames,
@ -229,7 +230,9 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
ApplicationNames = new Opc.Ua.LocalizedTextCollection(),
ApplicationUri = applicationUri
};
return await certificateGroup.NewKeyPairRequestAsync(app, subjectName, domainNames, privateKeyFormat, privateKeyPassword).ConfigureAwait(false);
var keyPair = await certificateGroup.NewKeyPairRequestAsync(app, subjectName, domainNames, privateKeyFormat, privateKeyPassword).ConfigureAwait(false);
await certificateGroup.ImportCertKeySecret(id, requestId, keyPair.PrivateKey, keyPair.PrivateKeyFormat);
return keyPair;
}
/// <inheritdoc/>
@ -252,6 +255,27 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
return crlList;
}
/// <inheritdoc/>
public async Task<byte[]> LoadPrivateKeyAsync(string id, string requestId, string privateKeyFormat)
{
var certificateGroup = await KeyVaultCertificateGroupProvider.Create(_keyVaultServiceClient, id).ConfigureAwait(false);
return await certificateGroup.LoadCertKeySecret(id, requestId, privateKeyFormat);
}
/// <inheritdoc/>
public async Task AcceptPrivateKeyAsync(string id, string requestId)
{
var certificateGroup = await KeyVaultCertificateGroupProvider.Create(_keyVaultServiceClient, id).ConfigureAwait(false);
await certificateGroup.AcceptCertKeySecret(id, requestId);
}
/// <inheritdoc/>
public async Task DeletePrivateKeyAsync(string id, string requestId)
{
var certificateGroup = await KeyVaultCertificateGroupProvider.Create(_keyVaultServiceClient, id).ConfigureAwait(false);
await certificateGroup.DeleteCertKeySecret(id, requestId);
}
/// <inheritdoc/>
public async Task<KeyVaultTrustListModel> GetTrustListAsync(string id, int? maxResults = null, string nextPageLink = null)
{

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -32,10 +32,12 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
public readonly ExpandedNodeId DefaultHttpsGroupId;
public readonly ExpandedNodeId DefaultUserTokenGroupId;
private readonly ILogger _log;
internal IApplicationsDatabase ApplicationsDatabase;
internal ICertificateGroup CertificateGroup;
internal IApplicationsDatabase _applicationsDatabase;
internal ICertificateGroup _certificateGroup;
private readonly string _endpoint;
private readonly string _dataBaseId;
private readonly string _collectionId;
private readonly ILogger _log;
private SecureString _authKeyOrResourceToken;
public CosmosDBCertificateRequest(
@ -44,9 +46,11 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
IServicesConfig config,
ILogger logger)
{
ApplicationsDatabase = database;
CertificateGroup = certificateGroup;
_applicationsDatabase = database;
_certificateGroup = certificateGroup;
_dataBaseId = config.CosmosDBDatabase;
_endpoint = config.CosmosDBEndpoint;
_collectionId = config.CosmosDBCollection;
_authKeyOrResourceToken = new SecureString();
foreach (char ch in config.CosmosDBToken)
{
@ -66,8 +70,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
#region ICertificateRequest
public Task Initialize()
{
var db = new DocumentDBRepository(_endpoint, _authKeyOrResourceToken);
CertificateRequests = new DocumentDBCollection<CosmosDB.Models.CertificateRequest>(db);
var db = new DocumentDBRepository(_endpoint, _dataBaseId, _authKeyOrResourceToken);
_certificateRequests = new DocumentDBCollection<CosmosDB.Models.CertificateRequest>(db, _collectionId);
return Task.CompletedTask;
}
@ -75,9 +79,9 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
{
try
{
var onBehalfOfCertificateGroup = await CertificateGroup.OnBehalfOfRequest(request);
var onBehalfOfCertificateGroup = await _certificateGroup.OnBehalfOfRequest(request);
var certRequest = (CosmosDBCertificateRequest)this.MemberwiseClone();
certRequest.CertificateGroup = onBehalfOfCertificateGroup;
certRequest._certificateGroup = onBehalfOfCertificateGroup;
return certRequest;
}
catch (Exception ex)
@ -95,7 +99,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
byte[] certificateSigningRequest,
string authorityId)
{
Application application = await ApplicationsDatabase.GetApplicationAsync(applicationId);
Application application = await _applicationsDatabase.GetApplicationAsync(applicationId);
if (application == null)
{
throw new ServiceResultException(StatusCodes.BadNotFound, "The ApplicationId does not refer to a valid application.");
@ -133,7 +137,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
if (isNew)
{
await CertificateRequests.CreateAsync(request);
await _certificateRequests.CreateAsync(request);
}
return request.RequestId.ToString();
@ -149,7 +153,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
string privateKeyPassword,
string authorityId)
{
Application application = await ApplicationsDatabase.GetApplicationAsync(applicationId);
Application application = await _applicationsDatabase.GetApplicationAsync(applicationId);
if (application == null)
{
throw new ServiceResultException(StatusCodes.BadNodeIdUnknown);
@ -239,7 +243,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
if (isNew)
{
await CertificateRequests.CreateAsync(request);
await _certificateRequests.CreateAsync(request);
}
return request.RequestId.ToString();
@ -256,7 +260,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
do
{
retryUpdate = false;
CertificateRequest request = await CertificateRequests.GetAsync(reqId);
CertificateRequest request = await _certificateRequests.GetAsync(reqId);
if (request == null)
{
@ -268,7 +272,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
throw new ServiceResultException(StatusCodes.BadInvalidState);
}
Application application = await ApplicationsDatabase.GetApplicationAsync(request.ApplicationId);
Application application = await _applicationsDatabase.GetApplicationAsync(request.ApplicationId);
if (application == null)
{
throw new ServiceResultException(StatusCodes.BadNodeIdUnknown, "Unknown application id");
@ -291,14 +295,13 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
{
try
{
certificate = await CertificateGroup.SigningRequestAsync(
certificate = await _certificateGroup.SigningRequestAsync(
request.CertificateGroupId,
application.ApplicationUri,
request.SigningRequest
);
request.Certificate = certificate.RawData;
request.PrivateKey = null;
}
catch (Exception e)
{
@ -317,8 +320,9 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
X509Certificate2KeyPair newKeyPair = null;
try
{
newKeyPair = await CertificateGroup.NewKeyPairRequestAsync(
newKeyPair = await _certificateGroup.NewKeyPairRequestAsync(
request.CertificateGroupId,
requestId,
application.ApplicationUri,
request.SubjectName,
request.DomainNames,
@ -337,15 +341,14 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
}
request.Certificate = newKeyPair.Certificate.RawData;
request.PrivateKey = newKeyPair.PrivateKey;
// ignore private key, it is stored in KeyVault
}
}
request.ApproveRejectTime = DateTime.UtcNow;
try
{
await CertificateRequests.UpdateAsync(reqId, request, request.ETag);
await _certificateRequests.UpdateAsync(reqId, request, request.ETag);
}
catch (DocumentClientException dce)
{
@ -362,13 +365,12 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
string requestId)
{
Guid reqId = GetIdFromString(requestId);
bool retryUpdate;
do
{
retryUpdate = false;
CertificateRequest request = await CertificateRequests.GetAsync(reqId);
CertificateRequest request = await _certificateRequests.GetAsync(reqId);
if (request == null)
{
throw new ServiceResultException(StatusCodes.BadNodeIdUnknown);
@ -379,6 +381,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
throw new ServiceResultException(StatusCodes.BadInvalidState);
}
await _certificateGroup.AcceptPrivateKeyAsync(request.CertificateGroupId, requestId);
request.State = CertificateRequestState.Accepted;
// erase information which is not required anymore
@ -388,7 +392,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
request.AcceptTime = DateTime.UtcNow;
try
{
await CertificateRequests.UpdateAsync(request.RequestId, request, request.ETag);
await _certificateRequests.UpdateAsync(request.RequestId, request, request.ETag);
}
catch (DocumentClientException dce)
{
@ -398,6 +402,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
}
}
} while (retryUpdate);
}
public async Task DeleteAsync(string requestId)
@ -409,7 +414,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
{
retryUpdate = false;
CertificateRequest request = await CertificateRequests.GetAsync(reqId);
CertificateRequest request = await _certificateRequests.GetAsync(reqId);
if (request == null)
{
throw new ServiceResultException(StatusCodes.BadNodeIdUnknown);
@ -417,6 +422,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
request.State = CertificateRequestState.Deleted;
await _certificateGroup.DeletePrivateKeyAsync(request.CertificateGroupId, requestId);
// erase information which is not required anymore
request.SigningRequest = null;
request.PrivateKeyFormat = null;
@ -424,7 +431,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
request.DeleteTime = DateTime.UtcNow;
try
{
await CertificateRequests.UpdateAsync(request.RequestId, request, request.ETag);
await _certificateRequests.UpdateAsync(request.RequestId, request, request.ETag);
}
catch (DocumentClientException dce)
{
@ -434,6 +441,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
}
}
} while (retryUpdate);
}
public async Task RevokeAsync(string requestId)
@ -444,7 +452,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
do
{
retryUpdate = false;
CertificateRequest request = await CertificateRequests.GetAsync(reqId);
CertificateRequest request = await _certificateRequests.GetAsync(reqId);
if (request == null)
{
@ -466,12 +474,11 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
request.PrivateKeyFormat = null;
request.SigningRequest = null;
request.PrivateKeyPassword = null;
request.PrivateKey = null;
try
{
var cert = new X509Certificate2(request.Certificate);
var crl = await CertificateGroup.RevokeCertificateAsync(request.CertificateGroupId, cert);
var crl = await _certificateGroup.RevokeCertificateAsync(request.CertificateGroupId, cert);
}
catch (Exception e)
{
@ -486,7 +493,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
try
{
await CertificateRequests.UpdateAsync(reqId, request, request.ETag);
await _certificateRequests.UpdateAsync(reqId, request, request.ETag);
}
catch (DocumentClientException dce)
{
@ -502,7 +509,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
{
Guid reqId = GetIdFromString(requestId);
CertificateRequest request = await CertificateRequests.GetAsync(reqId);
CertificateRequest request = await _certificateRequests.GetAsync(reqId);
if (request == null)
{
throw new ServiceResultException(StatusCodes.BadNodeIdUnknown);
@ -514,13 +521,13 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
throw new ServiceResultException(StatusCodes.BadInvalidState);
}
await CertificateRequests.DeleteAsync(request.RequestId);
await _certificateRequests.DeleteAsync(request.RequestId);
}
public async Task RevokeGroupAsync(string groupId, bool? allVersions)
{
// TODO: implement all versions to renew all CSR for all CA versions
var deletedRequests = await CertificateRequests.GetAsync(x => x.State == CertificateRequestState.Deleted);
var deletedRequests = await _certificateRequests.GetAsync(x => x.State == CertificateRequestState.Deleted);
if (deletedRequests == null ||
deletedRequests.Count() == 0)
{
@ -543,11 +550,11 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
{
// skip
}
}
}
await CertificateGroup.RevokeCertificatesAsync(groupId, certCollection);
await _certificateGroup.RevokeCertificatesAsync(groupId, certCollection);
foreach (var reqId in deletedRequests)
{
@ -555,7 +562,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
do
{
retryUpdate = false;
CertificateRequest request = await CertificateRequests.GetAsync(reqId.RequestId);
CertificateRequest request = await _certificateRequests.GetAsync(reqId.RequestId);
if (request == null)
{
@ -579,11 +586,10 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
request.PrivateKeyFormat = null;
request.SigningRequest = null;
request.PrivateKeyPassword = null;
request.PrivateKey = null;
try
{
await CertificateRequests.UpdateAsync(reqId.RequestId, request, request.ETag);
await _certificateRequests.UpdateAsync(reqId.RequestId, request, request.ETag);
}
catch (DocumentClientException dce)
{
@ -603,13 +609,13 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
{
Guid reqId = GetIdFromString(requestId);
Application application = await ApplicationsDatabase.GetApplicationAsync(applicationId);
Application application = await _applicationsDatabase.GetApplicationAsync(applicationId);
if (application == null)
{
throw new ServiceResultException(StatusCodes.BadNodeIdUnknown);
}
CertificateRequest request = await CertificateRequests.GetAsync(reqId);
CertificateRequest request = await _certificateRequests.GetAsync(reqId);
if (request == null)
{
throw new ServiceResultException(StatusCodes.BadNodeIdUnknown);
@ -632,6 +638,9 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
throw new ServiceResultException(StatusCodes.BadNodeIdUnknown);
}
// get private key
byte[] privateKey = await _certificateGroup.LoadPrivateKeyAsync(request.CertificateGroupId, requestId, request.PrivateKeyFormat);
return new FinishRequestResultModel(
request.State,
applicationId,
@ -640,7 +649,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
request.CertificateTypeId,
request.Certificate,
request.PrivateKeyFormat,
request.PrivateKey,
privateKey,
request.AuthorityId);
}
@ -650,7 +659,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
{
Guid reqId = GetIdFromString(requestId);
CertificateRequest request = await CertificateRequests.GetAsync(reqId);
CertificateRequest request = await _certificateRequests.GetAsync(reqId);
if (request == null)
{
throw new ServiceResultException(StatusCodes.BadNodeIdUnknown);
@ -682,7 +691,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
}
public async Task<(string,ReadRequestResultModel[])> QueryPageAsync(
public async Task<(string, ReadRequestResultModel[])> QueryPageAsync(
string appId,
CertificateRequestState? state,
string nextPageLink,
@ -691,19 +700,19 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
IEnumerable<CertificateRequest> requests;
if (appId == null && state == null)
{
(nextPageLink, requests) = await CertificateRequests.GetPageAsync(x => x.State < CertificateRequestState.Deleted, nextPageLink, maxResults);
(nextPageLink, requests) = await _certificateRequests.GetPageAsync(x => x.State < CertificateRequestState.Deleted, nextPageLink, maxResults);
}
else if (appId != null && state != null)
{
(nextPageLink, requests) = await CertificateRequests.GetPageAsync(x => x.ApplicationId == appId && x.State == state, nextPageLink, maxResults);
(nextPageLink, requests) = await _certificateRequests.GetPageAsync(x => x.ApplicationId == appId && x.State == state, nextPageLink, maxResults);
}
else if (appId != null)
{
(nextPageLink, requests) = await CertificateRequests.GetPageAsync(x => x.ApplicationId == appId, nextPageLink, maxResults);
(nextPageLink, requests) = await _certificateRequests.GetPageAsync(x => x.ApplicationId == appId, nextPageLink, maxResults);
}
else
{
(nextPageLink, requests) = await CertificateRequests.GetPageAsync(x => x.State == state, nextPageLink, maxResults);
(nextPageLink, requests) = await _certificateRequests.GetPageAsync(x => x.State == state, nextPageLink, maxResults);
}
List<ReadRequestResultModel> result = new List<ReadRequestResultModel>();
foreach (CertificateRequest request in requests)
@ -871,8 +880,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault
#endregion
#region Private Fields
private DateTime queryCounterResetTime = DateTime.UtcNow;
private IDocumentDBCollection<CertificateRequest> CertificateRequests;
private DateTime _queryCounterResetTime = DateTime.UtcNow;
private IDocumentDBCollection<CertificateRequest> _certificateRequests;
#endregion
}
}

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -33,9 +33,13 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.CosmosDB
public DocumentDBCollection(IDocumentDBRepository db, string collectionId)
{
if (string.IsNullOrEmpty(collectionId))
{
throw new ArgumentNullException("collectionId must be set");
}
if (db == null)
{
throw new ArgumentNullException(nameof(db));
}
this.CollectionId = collectionId;
this.db = db;
@ -217,4 +221,4 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.CosmosDB
}
}
}
}
}

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -15,16 +15,18 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.CosmosDB
public class DocumentDBRepository : IDocumentDBRepository
{
public DocumentClient Client { get; }
public string DatabaseId { get { return "OpcVault"; } }
public string DatabaseId { get; }
public DocumentDBRepository(string endpoint, string authKeyOrResourceToken)
public DocumentDBRepository(string endpoint, string dataBaseId, string authKeyOrResourceToken)
{
this.DatabaseId = dataBaseId;
this.Client = new DocumentClient(new Uri(endpoint), authKeyOrResourceToken);
CreateDatabaseIfNotExistsAsync().Wait();
}
public DocumentDBRepository(string endpoint, SecureString authKeyOrResourceToken)
public DocumentDBRepository(string endpoint, string dataBaseId, SecureString authKeyOrResourceToken)
{
this.DatabaseId = dataBaseId;
this.Client = new DocumentClient(new Uri(endpoint), authKeyOrResourceToken);
CreateDatabaseIfNotExistsAsync().Wait();
}
@ -48,4 +50,4 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.CosmosDB
}
}
}
}
}

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -12,4 +12,4 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.CosmosDB
DocumentClient Client { get; }
string DatabaseId { get; }
}
}
}

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -33,7 +33,6 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.CosmosDB.Models
public string SubjectName { get; set; }
public string[] DomainNames { get; set; }
public string PrivateKeyFormat { get; set; }
public byte[] PrivateKey { get; set; }
public string PrivateKeyPassword { get; set; }
public string AuthorityId { get; set; }
public byte[] Certificate { get; set; }

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -14,7 +14,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Diagnostics
public class Serialization
{
// Save memory avoiding serializations that go too deep
private static readonly JsonSerializerSettings serializationSettings =
private static readonly JsonSerializerSettings _serializationSettings =
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
@ -44,7 +44,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Diagnostics
}
}
return JsonConvert.SerializeObject(logdata, serializationSettings);
return JsonConvert.SerializeObject(logdata, _serializationSettings);
}
private static object SerializeException(Exception e, int depth = 3)
@ -52,8 +52,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Diagnostics
if (e == null) return null;
if (depth == 0) return "-max serialization depth reached-";
var exception = e as AggregateException;
if (exception != null)
if (e is AggregateException exception)
{
var innerExceptions = exception.InnerExceptions
.Select(ie => SerializeException(ie, depth - 1)).ToList();

Просмотреть файл

@ -21,7 +21,6 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
{
public sealed class KeyVaultCertificateGroupProvider : Opc.Ua.Gds.Server.CertificateGroup
{
public TimeSpan CrlUpdateTime = TimeSpan.FromDays(30);
public X509CRL Crl;
public X509SignatureGenerator x509SignatureGenerator;
@ -140,7 +139,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
#region ICertificateGroupProvider
public override async Task Init()
{
await semaphoreSlim.WaitAsync();
await _semaphoreSlim.WaitAsync();
try
{
Opc.Ua.Utils.Trace(Opc.Ua.Utils.TraceMasks.Information, "InitializeCertificateGroup: {0}", m_subjectName);
@ -167,7 +166,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
}
finally
{
semaphoreSlim.Release();
_semaphoreSlim.Release();
}
}
@ -177,7 +176,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
/// </summary>
public async Task<bool> CreateImportedCACertificateAsync()
{
await semaphoreSlim.WaitAsync();
await _semaphoreSlim.WaitAsync();
try
{
@ -217,7 +216,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
}
finally
{
semaphoreSlim.Release();
_semaphoreSlim.Release();
}
}
@ -226,7 +225,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
/// </summary>
public async Task<bool> CreateCACertificateAsync()
{
await semaphoreSlim.WaitAsync();
await _semaphoreSlim.WaitAsync();
try
{
DateTime notBefore = TrimmedNotBeforeDate();
@ -254,7 +253,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
// create default revocation list, sign with KeyVault
Crl = RevokeCertificate(Certificate, null, null,
notBefore, notBefore + CrlUpdateTime,
notBefore, DateTime.MinValue,
new KeyVaultSignatureGenerator(_keyVaultServiceClient, _caCertKeyIdentifier, Certificate),
this.Configuration.CACertificateHashSize);
@ -265,7 +264,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
}
finally
{
semaphoreSlim.Release();
_semaphoreSlim.Release();
}
}
@ -291,7 +290,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
var crl = await _keyVaultServiceClient.LoadCACrl(Configuration.Id, caCertKeyInfo.Certificate);
var crls = new List<X509CRL>() { crl };
var newCrl = RevokeCertificate(caCertKeyInfo.Certificate, crls, certificates,
now, now + CrlUpdateTime,
now, DateTime.MinValue,
new KeyVaultSignatureGenerator(_keyVaultServiceClient, caCertKeyInfo.KeyIdentifier, caCertKeyInfo.Certificate),
this.Configuration.CACertificateHashSize);
await _keyVaultServiceClient.ImportCACrl(Configuration.Id, caCertKeyInfo.Certificate, newCrl).ConfigureAwait(false);
@ -341,7 +340,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
var crl = await _keyVaultServiceClient.LoadCACrl(Configuration.Id, caCertKeyInfo.Certificate);
var crls = new List<X509CRL>() { crl };
var newCrl = RevokeCertificate(caCertKeyInfo.Certificate, crls, caRevokeCollection,
now, now + CrlUpdateTime,
now, DateTime.MinValue,
new KeyVaultSignatureGenerator(_keyVaultServiceClient, caCertKeyInfo.KeyIdentifier, caCertKeyInfo.Certificate),
this.Configuration.CACertificateHashSize);
await _keyVaultServiceClient.ImportCACrl(Configuration.Id, caCertKeyInfo.Certificate, newCrl).ConfigureAwait(false);
@ -356,9 +355,9 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
}
/// <summary>
/// Creates a new key pair with certificate offline and signs it with KeyVault.
/// Creates a new key pair with KeyVault and signs it with KeyVault.
/// </summary>
public override async Task<X509Certificate2KeyPair> NewKeyPairRequestAsync(
public async Task<X509Certificate2KeyPair> NewKeyPairRequestKeyVaultAsync(
ApplicationRecordDataType application,
string subjectName,
string[] domainNames,
@ -369,7 +368,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
DateTime notBefore = TrimmedNotBeforeDate();
DateTime notAfter = notBefore.AddMonths(Configuration.DefaultCertificateLifetime);
// create new cert in HSM storage
// create new cert with KeyVault
using (var signedCertWithPrivateKey = await _keyVaultServiceClient.CreateSignedKeyPairAsync(
Configuration.Id,
Certificate,
@ -404,7 +403,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
/// <summary>
/// Creates a new key pair with certificate offline and signs it with KeyVault.
/// </summary>
public async Task<X509Certificate2KeyPair> NewKeyPairRequestOfflineAsync(
public override async Task<X509Certificate2KeyPair> NewKeyPairRequestAsync(
ApplicationRecordDataType application,
string subjectName,
string[] domainNames,
@ -458,11 +457,44 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
{
throw new ServiceResultException(StatusCodes.BadInvalidArgument, "Invalid private key format");
}
return new X509Certificate2KeyPair(new X509Certificate2(signedCertWithPrivateKey.RawData), privateKeyFormat, privateKey);
}
}
}
/// <summary>
/// Stores the private key of a cert request in a Key Vault secret.
/// </summary>
public async Task ImportCertKeySecret(string id, string requestId, byte[] privateKey, string privateKeyFormat, CancellationToken ct = default)
{
await _keyVaultServiceClient.ImportCertKey(id, requestId, privateKey, privateKeyFormat, ct);
}
/// <summary>
/// Load the private key of a cert request from Key Vault secret.
/// </summary>
public async Task<byte[]> LoadCertKeySecret(string id, string requestId, string privateKeyFormat, CancellationToken ct = default)
{
return await _keyVaultServiceClient.LoadCertKey(id, requestId, privateKeyFormat, ct);
}
/// <summary>
/// Accept the private key of a cert request from Key Vault secret.
/// </summary>
public async Task AcceptCertKeySecret(string id, string requestId, CancellationToken ct = default)
{
await _keyVaultServiceClient.AcceptCertKey(id, requestId, ct);
}
/// <summary>
/// Delete the private key of a cert request from Key Vault secret.
/// </summary>
public async Task DeleteCertKeySecret(string id, string requestId, CancellationToken ct = default)
{
await _keyVaultServiceClient.DeleteCertKey(id, requestId, ct);
}
/// <summary>
/// Creates a KeyVault signed certficate from signing request.
/// </summary>
@ -661,13 +693,15 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
private static Dictionary<NodeId, string> CertTypeMap()
{
var certTypeMap = new Dictionary<NodeId, string>();
// FUTURE: support more cert types
//CertTypeMap.Add(Opc.Ua.ObjectTypeIds.HttpsCertificateType, "HttpsCertificateType");
//CertTypeMap.Add(Opc.Ua.ObjectTypeIds.UserCredentialCertificateType, "UserCredentialCertificateType");
certTypeMap.Add(Opc.Ua.ObjectTypeIds.ApplicationCertificateType, "ApplicationCertificateType");
//CertTypeMap.Add(Opc.Ua.ObjectTypeIds.RsaMinApplicationCertificateType, "RsaMinApplicationCertificateType");
certTypeMap.Add(Opc.Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType, "RsaSha256ApplicationCertificateType");
var certTypeMap = new Dictionary<NodeId, string>
{
// FUTURE: support more cert types
//{ Opc.Ua.ObjectTypeIds.HttpsCertificateType, "HttpsCertificateType" },
//{ Opc.Ua.ObjectTypeIds.UserCredentialCertificateType, "UserCredentialCertificateType" },
{ Opc.Ua.ObjectTypeIds.ApplicationCertificateType, "ApplicationCertificateType" },
//{ Opc.Ua.ObjectTypeIds.RsaMinApplicationCertificateType, "RsaMinApplicationCertificateType" },
{ Opc.Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType, "RsaSha256ApplicationCertificateType" }
};
return certTypeMap;
}
@ -712,6 +746,6 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
private string _caCertSecretIdentifier;
private string _caCertKeyIdentifier;
private DateTime _lastUpdate;
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
private SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
}
}

Просмотреть файл

@ -360,6 +360,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
int keySize,
int hashSize,
bool trusted,
string crlDistributionPoint = null,
CancellationToken ct = default)
{
try
@ -443,7 +444,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
null,
publicKey,
new KeyVaultSignatureGenerator(this, caCertKeyIdentifier, null),
true);
true,
crlDistributionPoint);
// merge Root CA cert with
var mergeResult = await _keyVaultClient.MergeCertificateAsync(
@ -461,7 +463,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
return signedcert;
}
catch
catch
{
throw new ServiceResultException(StatusCodes.BadInternalError, "Failed to create new Root CA certificate");
}
@ -530,8 +532,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
(ushort)hashSize,
issuerCert,
publicKey,
generator,
true);
generator);
// merge signed cert with keystore
var mergeResult = await _keyVaultClient.MergeCertificateAsync(
@ -622,6 +623,75 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
return null;
}
/// <summary>
/// Imports a Private Key for group id and certificate.
/// </summary>
public async Task ImportCertKey(string id, string requestId, byte[] privateKey, string privateKeyFormat, CancellationToken ct = default(CancellationToken))
{
var contentType = PrivateKeyFormatToContentType(privateKeyFormat);
string secretIdentifier = KeySecretName(id, requestId);
DateTime now = DateTime.UtcNow;
SecretAttributes secretAttributes = new SecretAttributes()
{
Enabled = true,
NotBefore = now - TimeSpan.FromDays(-1),
Expires = now + TimeSpan.FromDays(30),
};
var result = await _keyVaultClient.SetSecretAsync(
_vaultBaseUrl,
secretIdentifier,
(contentType == ContentTypePfx) ? Convert.ToBase64String(privateKey) : Encoding.ASCII.GetString(privateKey),
null,
contentType,
secretAttributes,
ct)
.ConfigureAwait(false);
}
/// <summary>
/// Load Private Key for certificate in group.
/// </summary>
public async Task<byte[]> LoadCertKey(string id, string requestId, string privateKeyFormat, CancellationToken ct = default(CancellationToken))
{
var contentType = PrivateKeyFormatToContentType(privateKeyFormat);
string secretIdentifier = KeySecretName(id, requestId);
var secret = await _keyVaultClient.GetSecretAsync(_vaultBaseUrl, secretIdentifier, ct).ConfigureAwait(false);
if (secret.ContentType == contentType)
{
if (secret.ContentType == ContentTypePfx)
{
return Convert.FromBase64String(secret.Value);
}
else if (secret.ContentType == ContentTypePem)
{
return Encoding.ASCII.GetBytes(secret.Value);
}
}
return null;
}
/// <summary>
/// Accept Private Key for certificate in group.
/// </summary>
public async Task AcceptCertKey(string id, string requestId, CancellationToken ct = default)
{
string secretIdentifier = KeySecretName(id, requestId);
var secretAttributes = new SecretAttributes
{
Enabled = false
};
await _keyVaultClient.UpdateSecretAsync(_vaultBaseUrl, secretIdentifier, secretAttributes, null, ct).ConfigureAwait(false);
}
/// <summary>
/// Delete Private Key for certificate in group.
/// </summary>
public async Task DeleteCertKey(string id, string requestId, CancellationToken ct = default)
{
string secretIdentifier = KeySecretName(id, requestId);
await _keyVaultClient.DeleteSecretAsync(_vaultBaseUrl, secretIdentifier, ct).ConfigureAwait(false);
}
/// <summary>
/// Creates a trust list with all certs and crls in issuer and trusted list.
/// i) First load all certs and crls tagged with id==Issuer or id==Trusted.
@ -762,7 +832,9 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
var secretItems = await _keyVaultClient.GetSecretsAsync(_vaultBaseUrl, MaxResults, ct).ConfigureAwait(false);
while (secretItems != null)
{
foreach (var secretItem in secretItems.Where(s => s.ContentType == ContentTypeCrl))
foreach (var secretItem in secretItems.Where(s =>
(s.ContentType == ContentTypeCrl || s.ContentType == ContentTypePem || s.ContentType == ContentTypePfx)
))
{
try
{
@ -891,7 +963,11 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
private string KeyStoreName(string id, string requestId)
{
return id + "Keys" + requestId;
return id + "Key" + requestId;
}
private string KeySecretName(string id, string requestId)
{
return id + "Key" + requestId;
}
private string CrlSecretName(string id, X509Certificate2 certificate)
{
@ -920,6 +996,19 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
return null;
}
private string PrivateKeyFormatToContentType(string privateKeyFormat)
{
if (privateKeyFormat.Equals("PFX", StringComparison.OrdinalIgnoreCase))
{
return ContentTypePfx;
}
else if (privateKeyFormat.Equals("PEM", StringComparison.OrdinalIgnoreCase))
{
return ContentTypePem;
}
throw new Exception("Unknown Private Key format.");
}
private string _vaultBaseUrl;
private bool _keyStoreHSM;
private IKeyVaultClient _keyVaultClient;

Просмотреть файл

@ -38,7 +38,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
X509Certificate2 issuerCAKeyCert,
RSA publicKey,
X509SignatureGenerator generator,
bool caCert = false
bool caCert = false,
string crlDistributionPoint = null
)
{
if (publicKey == null)
@ -68,7 +69,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
// Basic constraints
request.CertificateExtensions.Add(
new X509BasicConstraintsExtension(caCert, false, 0, true));
new X509BasicConstraintsExtension(caCert, caCert, 0, true));
// Subject Key Identifier
var ski = new X509SubjectKeyIdentifierExtension(
@ -93,6 +94,15 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign,
true));
if (crlDistributionPoint != null)
{
string serial = BitConverter.ToString(serialNumber).Replace("-","").ToLower();
// add CRL endpoint, if available
request.CertificateExtensions.Add(
BuildX509CRLDistributionPoints(crlDistributionPoint.Replace("%serial%", serial))
);
}
}
else
{
@ -507,6 +517,33 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.KeyVault
return BuildAuthorityKeyIdentifier(issuer.SubjectName, issuer.GetSerialNumber(), ski);
}
private static X509Extension BuildX509CRLDistributionPoints(
string distributionPoint
)
{
var context0 = new Asn1Tag(TagClass.ContextSpecific, 0, true);
Asn1Tag distributionPointChoice = context0;
Asn1Tag fullNameChoice = context0;
Asn1Tag generalNameUriChoice = new Asn1Tag(TagClass.ContextSpecific, 6);
using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
{
writer.PushSequence();
writer.PushSequence();
writer.PushSequence(distributionPointChoice);
writer.PushSequence(fullNameChoice);
writer.WriteCharacterString(
generalNameUriChoice,
UniversalTagNumber.IA5String,
distributionPoint);
writer.PopSequence(fullNameChoice);
writer.PopSequence(distributionPointChoice);
writer.PopSequence();
writer.PopSequence();
return new X509Extension("2.5.29.31", writer.Encode(), false);
}
}
private static X509Extension BuildAuthorityKeyIdentifier(
X500DistinguishedName issuerName,
byte[] issuerSerialNumber,

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -22,8 +22,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Runtime
ICorsConfig, IClientConfig, ISwaggerConfig
{
// services config
private const string OpcVaultKey = "OpcVault";
private const string SwaggerKey = "Swagger";
private const string _opcVaultKey = "OpcVault";
private const string _swaggerKey = "Swagger";
/// <summary>
/// Configuration constructor
@ -44,9 +44,9 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Runtime
IConfigurationRoot configuration) :
base(processId, configuration) {
ServicesConfig = new ServicesConfig();
configuration.Bind(OpcVaultKey, ServicesConfig);
configuration.Bind(_opcVaultKey, ServicesConfig);
SwaggerConfig = new SwaggerConfig();
configuration.Bind(SwaggerKey, SwaggerConfig);
configuration.Bind(_swaggerKey, SwaggerConfig);
_auth = new AuthConfig(configuration, serviceId);
_cors = new CorsConfig(configuration);
}

Просмотреть файл

@ -1,10 +1,9 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
using Microsoft.Azure.IIoT.Utils;
namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Runtime
{
@ -13,6 +12,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Runtime
string KeyVaultBaseUrl { get; set; }
string KeyVaultResourceId { get; set; }
string CosmosDBEndpoint { get; set; }
string CosmosDBDatabase { get; set; }
string CosmosDBCollection { get; set; }
string CosmosDBToken { get; set; }
bool AutoApprove { get; set; }
}
@ -23,6 +24,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Runtime
public ServicesConfig()
{
KeyVaultResourceId = "https://vault.azure.net";
CosmosDBDatabase = "OpcVault";
CosmosDBCollection = "AppsAndCertRequests";
AutoApprove = false;
}
@ -33,6 +36,10 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Runtime
/// <inheritdoc/>
public string CosmosDBEndpoint { get; set; }
/// <inheritdoc/>
public string CosmosDBDatabase { get; set; }
/// <inheritdoc/>
public string CosmosDBCollection { get; set; }
/// <inheritdoc/>
public string CosmosDBToken { get; set; }
/// <inheritdoc/>
public bool AutoApprove { get; set; }

Просмотреть файл

@ -10,6 +10,10 @@
//
// "CosmosDBEndpoint": "",
// "CosmosDBToken": ""
// name of database, default "OpcVault"
// "CosmosDBDatabase": ""
// name of collection, default "AppsAndCertRequests"
// "CosmosDBCollection": ""
},
//
@ -82,7 +86,7 @@
//
"Swagger": {
// Swagger needs an app registration.
"Enabled": true
// "Enabled": true
//
// The application id as registered in AAS. Retrieve from portal
// as a guid, e.g. fa01ade2-2365-4dd1-a084-a6ef027090fc

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -20,13 +20,13 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
[Authorize(Policy = Policies.CanRead)]
public sealed class CertificateGroupController : Controller
{
private readonly ICertificateGroup certificateGroups;
private readonly ICertificateGroup _certificateGroups;
/// <inheritdoc/>
public CertificateGroupController(
ICertificateGroup certificateGroups)
{
this.certificateGroups = certificateGroups;
this._certificateGroups = certificateGroups;
}
/// <returns>List of certificate groups</returns>
@ -34,7 +34,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
[SwaggerOperation(OperationId = "GetCertificateGroupIds")]
public async Task<CertificateGroupListApiModel> GetAsync()
{
return new CertificateGroupListApiModel(await this.certificateGroups.GetCertificateGroupIds());
return new CertificateGroupListApiModel(await this._certificateGroups.GetCertificateGroupIds());
}
/// <summary>Get group configuration</summary>
@ -44,7 +44,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
{
return new CertificateGroupConfigurationApiModel(
groupId,
await this.certificateGroups.GetCertificateGroupConfiguration(groupId));
await this._certificateGroups.GetCertificateGroupConfiguration(groupId));
}
/// <summary>Update group configuration</summary>
@ -53,7 +53,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
[Authorize(Policy = Policies.CanManage)]
public async Task<CertificateGroupConfigurationApiModel> PutAsync(string groupId, [FromBody] CertificateGroupConfigurationApiModel config)
{
var onBehalfOfCertificateGroups = await this.certificateGroups.OnBehalfOfRequest(Request);
var onBehalfOfCertificateGroups = await this._certificateGroups.OnBehalfOfRequest(Request);
return new CertificateGroupConfigurationApiModel(
groupId,
await onBehalfOfCertificateGroups.UpdateCertificateGroupConfiguration(groupId, config.ToServiceModel()));
@ -66,7 +66,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
public async Task<CertificateGroupConfigurationApiModel> PostAsync(string groupId, string subject, string certType)
{
var onBehalfOfCertificateGroups = await this.certificateGroups.OnBehalfOfRequest(Request);
var onBehalfOfCertificateGroups = await this._certificateGroups.OnBehalfOfRequest(Request);
return new CertificateGroupConfigurationApiModel(
groupId,
await onBehalfOfCertificateGroups.CreateCertificateGroupConfiguration(groupId, subject, certType));
@ -78,7 +78,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
public async Task<CertificateGroupConfigurationCollectionApiModel> GetConfigAsync()
{
return new CertificateGroupConfigurationCollectionApiModel(
await this.certificateGroups.GetCertificateGroupConfigurationCollection());
await this._certificateGroups.GetCertificateGroupConfigurationCollection());
}
/// <summary>Get CA Certificate chain</summary>
@ -87,7 +87,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
public async Task<X509Certificate2CollectionApiModel> GetCACertificateChainAsync(string groupId, int? maxResults)
{
return new X509Certificate2CollectionApiModel(
await this.certificateGroups.GetCACertificateChainAsync(groupId));
await this._certificateGroups.GetCACertificateChainAsync(groupId));
}
/// <summary>Get CA Certificate chain</summary>
@ -96,7 +96,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
public async Task<X509Certificate2CollectionApiModel> GetCACertificateChainNextAsync(string groupId, [FromBody] string nextPageLink, int? maxResults)
{
return new X509Certificate2CollectionApiModel(
await this.certificateGroups.GetCACertificateChainAsync(groupId));
await this._certificateGroups.GetCACertificateChainAsync(groupId));
}
/// <summary>Get CA CRL chain</summary>
@ -105,7 +105,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
public async Task<X509CrlCollectionApiModel> GetCACrlChainAsync(string groupId, int? maxResults)
{
return new X509CrlCollectionApiModel(
await this.certificateGroups.GetCACrlChainAsync(groupId));
await this._certificateGroups.GetCACrlChainAsync(groupId));
}
/// <summary>Get CA CRL chain</summary>
@ -114,7 +114,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
public async Task<X509CrlCollectionApiModel> GetCACrlChainNextAsync(string groupId, [FromBody] string nextPageLink, int? maxResults)
{
return new X509CrlCollectionApiModel(
await this.certificateGroups.GetCACrlChainAsync(groupId));
await this._certificateGroups.GetCACrlChainAsync(groupId));
}
/// <summary>Get trust list</summary>
@ -122,7 +122,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
[SwaggerOperation(OperationId = "GetTrustList")]
public async Task<TrustListApiModel> GetTrustListAsync(string groupId, int? maxResults)
{
return new TrustListApiModel(await this.certificateGroups.GetTrustListAsync(groupId, maxResults, null));
return new TrustListApiModel(await this._certificateGroups.GetTrustListAsync(groupId, maxResults, null));
}
/// <summary>Get trust list</summary>
@ -130,7 +130,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
[SwaggerOperation(OperationId = "GetTrustListNext")]
public async Task<TrustListApiModel> GetTrustListNextAsync(string groupId, [FromBody] string nextPageLink, int? maxResults)
{
return new TrustListApiModel(await this.certificateGroups.GetTrustListAsync(groupId, maxResults, nextPageLink));
return new TrustListApiModel(await this._certificateGroups.GetTrustListAsync(groupId, maxResults, nextPageLink));
}
/// <summary>Create new CA Certificate</summary>
@ -140,7 +140,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
public async Task<X509Certificate2ApiModel> PostCreateAsync(string groupId)
{
var onBehalfOfCertificateGroups = await this.certificateGroups.OnBehalfOfRequest(Request);
var onBehalfOfCertificateGroups = await this._certificateGroups.OnBehalfOfRequest(Request);
return new X509Certificate2ApiModel(
await onBehalfOfCertificateGroups.CreateCACertificateAsync(groupId));
}

Просмотреть файл

@ -22,9 +22,9 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
[Authorize(Policy = Policies.CanRead)]
public sealed class StatusController : Controller
{
private readonly ILogger log;
private readonly ICertificateGroup certificateGroups;
private readonly IApplicationsDatabase applicationDatabase;
private readonly ILogger _log;
private readonly ICertificateGroup _certificateGroups;
private readonly IApplicationsDatabase _applicationDatabase;
/// <inheritdoc/>
public StatusController(
@ -33,9 +33,9 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
ILogger logger
)
{
this.applicationDatabase = applicationDatabase;
this.certificateGroups = certificateGroups;
this.log = logger;
this._applicationDatabase = applicationDatabase;
this._certificateGroups = certificateGroups;
this._log = logger;
}
/// <summary>
@ -51,7 +51,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
string applicationMessage = "Alive and well";
try
{
var apps = await applicationDatabase.QueryApplicationsAsync(0, 1, null, null, 0, null, null);
var apps = await _applicationDatabase.QueryApplicationsAsync(0, 1, null, null, 0, null, null);
applicationOk = apps != null;
}
catch (Exception ex)
@ -59,13 +59,13 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
applicationOk = false;
applicationMessage = ex.Message;
}
this.log.Info("Service status application database", () => new { Healthy = applicationOk, Message = applicationMessage });
this._log.Info("Service status application database", () => new { Healthy = applicationOk, Message = applicationMessage });
bool kvOk;
string kvMessage = "Alive and well";
try
{
var groups = await certificateGroups.GetCertificateGroupIds();
var groups = await _certificateGroups.GetCertificateGroupIds();
kvOk = groups.Length > 0;
kvMessage = String.Join(",", groups);
}
@ -74,7 +74,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.v1.Controllers
kvOk = false;
kvMessage = ex.Message;
}
this.log.Info("Service status KeyVault", () => new { Healthy = kvOk, Message = kvMessage });
this._log.Info("Service status KeyVault", () => new { Healthy = kvOk, Message = kvMessage });
return new StatusApiModel(applicationOk, applicationMessage, kvOk, kvMessage);
}

Просмотреть файл

@ -1,4 +1,4 @@
// ------------------------------------------------------------
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
@ -98,32 +98,32 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
[TestCaseOrderer("TestCaseOrdering.PriorityOrderer", "Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test")]
public class CertificateGroupTest
{
IConfigurationRoot Configuration;
IConfigurationRoot _configuration;
ServicesConfig ServiceConfig = new ServicesConfig();
IClientConfig ClientConfig = new ClientConfig();
TraceLogger Logger = new TraceLogger(new LogConfig());
ServicesConfig _serviceConfig = new ServicesConfig();
IClientConfig _clientConfig = new ClientConfig();
TraceLogger _logger = new TraceLogger(new LogConfig());
public CertificateGroupTest(ITestOutputHelper log)
{
_log = log;
_randomSource = new RandomSource(randomStart);
_randomSource = new RandomSource(_randomStart);
_dataGenerator = new DataGenerator(_randomSource);
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("testsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile("testsettings.Development.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
Configuration.Bind("OpcVault", ServiceConfig);
Configuration.Bind("Auth", ClientConfig);
_configuration = builder.Build();
_configuration.Bind("OpcVault", _serviceConfig);
_configuration.Bind("Auth", _clientConfig);
}
[SkippableFact, Trait(Constants.Type, Constants.UnitTest), TestPriority(100)]
private async Task KeyVaultPurgeCACertificateAsync()
{
SkipOnInvalidConfiguration();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
await keyVault.PurgeAsync();
}
@ -132,7 +132,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
public async Task KeyVaultCreateCACertificateAsync()
{
SkipOnInvalidConfiguration();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
string[] groups = await keyVault.GetCertificateGroupIds();
foreach (string group in groups)
{
@ -159,7 +159,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
public async Task KeyVaultInit()
{
SkipOnInvalidConfiguration();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
await keyVault.Init();
}
@ -167,7 +167,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
public async Task KeyVaultListOfCertGroups()
{
SkipOnInvalidConfiguration();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
string[] groups = await keyVault.GetCertificateGroupIds();
}
@ -175,7 +175,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
public async Task KeyVaultGroupConfigurationCollection()
{
SkipOnInvalidConfiguration();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
Opc.Ua.Gds.Server.CertificateGroupConfigurationCollection groupCollection = await keyVault.GetCertificateGroupConfigurationCollection();
Assert.NotNull(groupCollection);
Assert.NotEmpty(groupCollection);
@ -185,7 +185,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
public async Task KeyVaultGetCertificateAsync()
{
SkipOnInvalidConfiguration();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
await keyVault.Init();
string[] groups = await keyVault.GetCertificateGroupIds();
foreach (string group in groups)
@ -213,13 +213,15 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
{
SkipOnInvalidConfiguration();
X509CertificateCollection certCollection = new X509CertificateCollection();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
string[] groups = await keyVault.GetCertificateGroupIds();
foreach (string group in groups)
{
ApplicationTestData randomApp = RandomApplicationTestData();
Guid requestId = Guid.NewGuid();
Opc.Ua.Gds.Server.X509Certificate2KeyPair newKeyPair = await keyVault.NewKeyPairRequestAsync(
group,
requestId.ToString(),
randomApp.ApplicationRecord.ApplicationUri,
randomApp.Subject,
randomApp.DomainNames.ToArray(),
@ -250,7 +252,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
{
SkipOnInvalidConfiguration();
X509CertificateCollection certCollection = new X509CertificateCollection();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
string[] groups = await keyVault.GetCertificateGroupIds();
foreach (string group in groups)
{
@ -298,14 +300,16 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
public async Task KeyVaultNewKeyPairAndRevokeCertificateAsync()
{
SkipOnInvalidConfiguration();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
await keyVault.Init();
string[] groups = await keyVault.GetCertificateGroupIds();
foreach (string group in groups)
{
ApplicationTestData randomApp = RandomApplicationTestData();
Guid requestId = Guid.NewGuid();
Opc.Ua.Gds.Server.X509Certificate2KeyPair newCert = await keyVault.NewKeyPairRequestAsync(
group,
requestId.ToString(),
randomApp.ApplicationRecord.ApplicationUri,
randomApp.Subject,
randomApp.DomainNames.ToArray(),
@ -332,7 +336,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
public async Task GetTrustListAsync()
{
SkipOnInvalidConfiguration();
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
await keyVault.Init();
string[] groups = await keyVault.GetCertificateGroupIds();
foreach (string group in groups)
@ -364,7 +368,7 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
}
}
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(ServiceConfig, ClientConfig, Logger);
KeyVaultCertificateGroup keyVault = new KeyVaultCertificateGroup(_serviceConfig, _clientConfig, _logger);
await keyVault.Init();
string[] groups = await keyVault.GetCertificateGroupIds();
@ -513,17 +517,17 @@ namespace Microsoft.Azure.IIoT.OpcUa.Services.Vault.Test
private void SkipOnInvalidConfiguration()
{
Skip.If(
ServiceConfig.KeyVaultBaseUrl == null ||
ServiceConfig.KeyVaultResourceId == null ||
ClientConfig.AppId == null ||
ClientConfig.AppSecret == null,
_serviceConfig.KeyVaultBaseUrl == null ||
_serviceConfig.KeyVaultResourceId == null ||
_clientConfig.AppId == null ||
_clientConfig.AppSecret == null,
"Missing valid KeyVault configuration");
}
/// <summary>The test logger</summary>
private readonly ITestOutputHelper _log;
private const int randomStart = 1;
private const int _randomStart = 1;
private RandomSource _randomSource;
private DataGenerator _dataGenerator;