Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfix/certificates #423

Merged
merged 13 commits into from
May 8, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static bool IsNull(this VariantValue value) {
/// <returns></returns>
public static T GetValueOrDefault<T>(this Dictionary<string, VariantValue> dict,
string key, T defaultValue) {
if (dict != null && dict.TryGetValue(key, out var token)) {
if (dict != null && dict.TryGetValue(key, out var token) && token != null) {
try {
return token.ConvertTo<T>();
}
Expand All @@ -52,7 +52,7 @@ public static T GetValueOrDefault<T>(this Dictionary<string, VariantValue> dict,
/// <returns></returns>
public static T? GetValueOrDefault<T>(this Dictionary<string, VariantValue> dict,
string key, T? defaultValue) where T : struct {
if (dict != null && dict.TryGetValue(key, out var token)) {
if (dict != null && dict.TryGetValue(key, out var token) && token != null) {
try {
// Handle enumerations serialized as string
if (typeof(T).IsEnum &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ namespace Microsoft.Azure.IIoT.OpcUa.Gateway.Server {
using Microsoft.Azure.IIoT.OpcUa.Twin;
using Microsoft.Azure.IIoT.OpcUa.History.Models;
using Microsoft.Azure.IIoT.OpcUa.History;
using Microsoft.Azure.IIoT.Auth.Server;
using Microsoft.Azure.IIoT.Auth;
using Microsoft.Azure.IIoT.Serializers;
using Serilog;
using Opc.Ua;
using Opc.Ua.Configuration;
Expand All @@ -27,10 +27,11 @@ namespace Microsoft.Azure.IIoT.OpcUa.Gateway.Server {
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.Text;
using Microsoft.Azure.IIoT.Serializers;


/// <summary>
/// Gateway server controller implementation
Expand Down Expand Up @@ -1605,9 +1606,11 @@ private async Task InitAsync() {

SecurityConfiguration = new SecurityConfiguration {
ApplicationCertificate = new CertificateIdentifier {
StoreType = "Directory",
StorePath =
"OPC Foundation/CertificateStores/MachineDefault",
StoreType = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
"X509Store" : "Directory",
StorePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
"CurrentUser\\UA_MachineDefault" :
"OPC Foundation/CertificateStores/MachineDefault",
cristipogacean marked this conversation as resolved.
Show resolved Hide resolved
cristipogacean marked this conversation as resolved.
Show resolved Hide resolved
SubjectName = "Opc UA Gateway Server"
},
TrustedPeerCertificates = new CertificateTrustList {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class ClientServicesConfig2 : ConfigBase, IClientServicesConfig2, ISecuri
public uint MaxKeepAliveCount =>
(uint)GetIntOrDefault(MaxKeepAliveCountKey, () => 5);

/// <inheritdoc/>
public string PkiRootPath => _security.PkiRootPath;

/// <inheritdoc/>
public CertificateInfo ApplicationCertificate => _security.ApplicationCertificate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class SecurityConfig : ConfigBase, ISecurityConfig {
/// Configuration
/// </summary>
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public const string PkiRootPathKey = "PkiRootPath";
hansgschossmann marked this conversation as resolved.
Show resolved Hide resolved
public const string ApplicationCertificateStorePathKey = "ApplicationCertificateStorePath";
public const string ApplicationCertificateStoreTypeKey = "ApplicationCertificateStoreType";
public const string ApplicationCertificateSubjectNameKey = "ApplicationCertificateSubjectName";
Expand All @@ -31,11 +32,15 @@ public class SecurityConfig : ConfigBase, ISecurityConfig {
public const string MinimumCertificateKeySizeKey = "MinimumCertificateKeySize";
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member

/// <inheritdoc/>
public string PkiRootPath =>
GetStringOrDefault(PkiRootPathKey, () => "pki");

/// <inheritdoc/>
public CertificateInfo ApplicationCertificate => new CertificateInfo {
StorePath = GetStringOrDefault(ApplicationCertificateStorePathKey,
() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
"CurrentUser\\My" : "/pki/own"),
"CurrentUser\\UA_MachineDefault" : $"{PkiRootPath}/own"),
StoreType = GetStringOrDefault(ApplicationCertificateStoreTypeKey,
() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
"X509Store" : "Directory"),
Expand All @@ -45,19 +50,19 @@ public class SecurityConfig : ConfigBase, ISecurityConfig {

/// <inheritdoc/>
public CertificateStore TrustedIssuerCertificates => new CertificateStore {
StorePath = GetStringOrDefault(TrustedIssuerCertificatesPathKey, () => "/pki/trusted"),
cristipogacean marked this conversation as resolved.
Show resolved Hide resolved
StorePath = GetStringOrDefault(TrustedIssuerCertificatesPathKey, () => $"{PkiRootPath}/trusted"),
StoreType = GetStringOrDefault(TrustedIssuerCertificatesTypeKey, () => "Directory"),
};

/// <inheritdoc/>
public CertificateStore TrustedPeerCertificates => new CertificateStore {
StorePath = GetStringOrDefault(TrustedPeerCertificatesPathKey, () => "/pki/trusted"),
StorePath = GetStringOrDefault(TrustedPeerCertificatesPathKey, () => $"{PkiRootPath}/trusted"),
StoreType = GetStringOrDefault(TrustedPeerCertificatesTypeKey, () => "Directory"),
};

/// <inheritdoc/>
public CertificateStore RejectedCertificateStore => new CertificateStore {
StorePath = GetStringOrDefault(RejectedCertificateStorePathKey, () => "/pki/trusted"),
cristipogacean marked this conversation as resolved.
Show resolved Hide resolved
StorePath = GetStringOrDefault(RejectedCertificateStorePathKey, () => $"{PkiRootPath}/trusted"),
StoreType = GetStringOrDefault(RejectedCertificateStoreTypeKey, () => "Directory"),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
namespace Microsoft.Azure.IIoT.OpcUa.Protocol {
using System;
using System.Security.Cryptography.X509Certificates;
using System.Linq;
using Opc.Ua;

/// <summary>
Expand Down Expand Up @@ -40,37 +41,62 @@ public static ApplicationConfiguration ToApplicationConfiguration(

applicationConfiguration.CertificateValidator.CertificateValidation += handler;

X509Certificate2 certificate = null;
var configuredSubject = applicationConfiguration.SecurityConfiguration
.ApplicationCertificate.SubjectName;
applicationConfiguration.SecurityConfiguration.ApplicationCertificate.SubjectName =
applicationConfiguration.ApplicationName;
applicationConfiguration.CertificateValidator
.Update(applicationConfiguration.SecurityConfiguration).ConfigureAwait(false);
cristipogacean marked this conversation as resolved.
Show resolved Hide resolved

// use existing certificate, if it is there
certificate = applicationConfiguration.SecurityConfiguration.ApplicationCertificate.Find(true).Result;
var certificate = applicationConfiguration.SecurityConfiguration
.ApplicationCertificate.Find(true).Result;

// create a self signed certificate if there is none
if (certificate == null && createSelfSignedCertIfNone) {
certificate = CertificateFactory.CreateCertificate(
applicationConfiguration.SecurityConfiguration.ApplicationCertificate.StoreType,
applicationConfiguration.SecurityConfiguration.ApplicationCertificate.StorePath,
applicationConfiguration.SecurityConfiguration
.ApplicationCertificate.StoreType,
applicationConfiguration.SecurityConfiguration
.ApplicationCertificate.StorePath,
null,
applicationConfiguration.ApplicationUri,
applicationConfiguration.ApplicationName,
applicationConfiguration.ApplicationName,
configuredSubject,
null,
CertificateFactory.defaultKeySize,
DateTime.UtcNow - TimeSpan.FromDays(1),
CertificateFactory.defaultLifeTime,
CertificateFactory.defaultHashSize
);

// update security information
applicationConfiguration.SecurityConfiguration.ApplicationCertificate.Certificate =
certificate ??
if (certificate == null) {
throw new Exception(
"OPC UA application certificate can not be created! Cannot continue without it!");
//await applicationConfiguration.CertificateValidator.UpdateCertificate(
//applicationConfiguration.SecurityConfiguration).ConfigureAwait(false);
}

applicationConfiguration.SecurityConfiguration
.ApplicationCertificate.Certificate = certificate;
cristipogacean marked this conversation as resolved.
Show resolved Hide resolved

try {
// copy the certificate *public key only* into the trusted certificates list
using (ICertificateStore trustedStore = applicationConfiguration
.SecurityConfiguration.TrustedPeerCertificates.OpenStore()) {
using (var publicKey = new X509Certificate2(certificate.RawData)) {
trustedStore.Add(publicKey.YieldReturn());
}
}
}
catch { }
cristipogacean marked this conversation as resolved.
Show resolved Hide resolved

// update security information
applicationConfiguration.CertificateValidator.UpdateCertificate(
applicationConfiguration.SecurityConfiguration).ConfigureAwait(false);
}

applicationConfiguration.ApplicationUri = Utils.GetApplicationUriFromCertificate(certificate);
applicationConfiguration.CertificateValidator
.Update(applicationConfiguration).ConfigureAwait(false);

return applicationConfiguration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ namespace Microsoft.Azure.IIoT.OpcUa.Protocol {
/// </summary>
public interface ISecurityConfig {

/// <summary>
/// PkiRootPath
/// </summary>
string PkiRootPath { get; }

/// <summary>
/// Certificate
/// </summary>
Expand Down
Loading