OpcUaServer.cs
using Opc.Ua;
using Opc.Ua.Configuration;
using Opc.Ua.Server;
using System.Security.Cryptography.X509Certificates;
namespace OpcServerCore.Util
{
public class OpcUaServer : StandardServer
{
public ApplicationInstance Application { get; private set; }
public bool AutoAccept { get; set; }
/// <summary>
/// 认证方式
/// </summary>
public UserTokenType UserTokenType { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string UserName { get; set; } = "admin";
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; } = "123456";
ICertificateValidator m_userCertificateValidator;
public OpcUaNodeManager OpcuaNode1 { get; set; }
protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
{
OpcuaNode1 = new OpcUaNodeManager(server, configuration);
List<INodeManager> nodeManagers = new List<INodeManager>();
nodeManagers.Add(OpcuaNode1);
return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray());
}
public async Task LoadAsync(string applicationName, string configSectionName)
{
try
{
CertificatePasswordProvider passwordProvider = new CertificatePasswordProvider(Password);
Application = new ApplicationInstance
{
ApplicationName = applicationName,
ApplicationType = ApplicationType.Server,
ConfigSectionName = configSectionName,
CertificatePasswordProvider = passwordProvider
};
await Application.LoadApplicationConfiguration(false);
}
catch (Exception ex)
{
throw new Exception($"Load async error: {ex.Message}", ex);
}
}
public async Task CheckCertificateAsync(bool renewCertificate)
{
try
{
ApplicationConfiguration config = Application.ApplicationConfiguration;
if (renewCertificate)
{
await Application.DeleteApplicationInstanceCertificate();
}
bool haveAppCertificate = await Application.CheckApplicationInstanceCertificate(false, minimumKeySize: 0);
if (!haveAppCertificate)
{
throw new Exception("Application instance certificate invalid!");
}
if (!config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
{
config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
}
// 指定认证方式
config.ServerConfiguration.UserTokenPolicies.Clear();
UserTokenPolicy utp = new UserTokenPolicy();
utp.TokenType = UserTokenType;
utp.PolicyId = utp.TokenType.ToString();
config.ServerConfiguration.UserTokenPolicies.Add(utp);
for (int ii = 0; ii < config.ServerConfiguration.UserTokenPolicies.Count; ii++)
{
UserTokenPolicy policy = config.ServerConfiguration.UserTokenPolicies[ii];
if (policy.TokenType == UserTokenType.Certificate)
{
if (config.SecurityConfiguration.TrustedUserCertificates != null && config.SecurityConfiguration.UserIssuerCertificates != null)
{
CertificateValidator certificateValidator = new CertificateValidator();
certificateValidator.Update(config.SecurityConfiguration).Wait();
certificateValidator.Update(config.SecurityConfiguration.UserIssuerCertificates, config.SecurityConfiguration.TrustedUserCertificates, config.SecurityConfiguration.RejectedCertificateStore);
m_userCertificateValidator = certificateValidator.GetChannelValidator();
}
}
}
}
catch (Exception ex)
{
throw new Exception($"CheckCertificateAsync: {ex.Message}", ex);
}
}
/// <summary>
/// Create server instance and add node managers.
/// </summary>
public void Create(IList<INodeManagerFactory> nodeManagerFactories)
{
try
{
if (nodeManagerFactories != null)
{
foreach (INodeManagerFactory factory in nodeManagerFactories)
{
this.AddNodeManager(factory);
}
}
}
catch (Exception ex)
{
throw new Exception($"Create Node manager Factories: {ex.Message}", ex);
}
}
/// <summary>
/// Start the server.
/// </summary>
public async Task StartAsync()
{
try
{
await Application.Start(this);
this.CurrentInstance.SessionManager.ImpersonateUser += new ImpersonateEventHandler(ImpersonateUser);
this.CurrentInstance.SessionManager.SessionActivated += EventStatus;
this.CurrentInstance.SessionManager.SessionClosing += EventStatus;
this.CurrentInstance.SessionManager.SessionCreated += EventStatus;
}
catch (Exception ex)
{
throw new Exception($"Start async: {ex.Message}", ex);
}
}
/// <summary>
/// Stops the server.
/// </summary>
public async Task StopAsync()
{
try
{
this.Stop();
}
catch (Exception ex)
{
throw new Exception($"Stop async: {ex.Message}", ex);
}
}
/// <summary>
/// The certificate validator is used
/// if auto accept is not selected in the configuration.
/// </summary>
private void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
{
if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
{
if (AutoAccept)
{
Logger.Info($"Accepted Certificate: [{e.Certificate.Subject}] [{e.Certificate.Thumbprint}]");
e.Accept = true;
return;
}
}
Logger.Info($"Rejected Certificate: {e.Error} [{e.Certificate.Subject}] [{e.Certificate.Thumbprint}]");
}
private void EventStatus(Session session, SessionEventReason reason)
{
}
private void ImpersonateUser(Session session, ImpersonateEventArgs args)
{
UserNameIdentityToken? userNameToken = args.NewIdentity as UserNameIdentityToken;
if (userNameToken != null)
{
args.Identity = VerifyPassword(userNameToken);
return;
}
X509IdentityToken? x509Token = args.NewIdentity as X509IdentityToken;
if (x509Token != null)
{
VerifyUserTokenCertificate(x509Token.Certificate);
args.Identity = new UserIdentity(x509Token);
Utils.Trace("X509 Token Accepted: {0}", args.Identity.DisplayName);
return;
}
}
/// <summary>
/// Validates the password for a username token.
/// </summary>
private IUserIdentity VerifyPassword(UserNameIdentityToken userNameToken)
{
var userName = userNameToken.UserName;
var password = userNameToken.DecryptedPassword;
if (string.IsNullOrEmpty(userName))
{
throw ServiceResultException.Create(StatusCodes.BadIdentityTokenInvalid, "Security token is not a valid username token. An empty username is not accepted.");
}
if (string.IsNullOrEmpty(password))
{
throw ServiceResultException.Create(StatusCodes.BadIdentityTokenRejected, "Security token is not a valid username token. An empty password is not accepted.");
}
if (!(userName == "admin" && password == "123456"))
{
TranslationInfo info = new TranslationInfo("InvalidPassword", "en-US", "Invalid username or password.", userName);
throw new ServiceResultException(new ServiceResult(StatusCodes.BadUserAccessDenied, "InvalidPassword", "http://opcfoundation.org/Quickstart/ReferenceServer/v1.03", new LocalizedText(info)));
}
return new UserIdentity(userNameToken);
}
/// <summary>
/// Verifies that a certificate user token is trusted.
/// </summary>
private void VerifyUserTokenCertificate(X509Certificate2 certificate)
{
try
{
if (m_userCertificateValidator != null)
{
m_userCertificateValidator.Validate(certificate);
}
else
{
new CertificateValidator().Validate(certificate);
}
}
catch (Exception e)
{
TranslationInfo info;
StatusCode result = StatusCodes.BadIdentityTokenRejected;
ServiceResultException se = e as ServiceResultException;
if (se != null && se.StatusCode == StatusCodes.BadCertificateUseNotAllowed)
{
info = new TranslationInfo("InvalidCertificate", "en-US", "'{0}' is an invalid user certificate.", certificate.Subject);
result = StatusCodes.BadIdentityTokenInvalid;
}
else
{
info = new TranslationInfo("UntrustedCertificate", "en-US", "'{0}' is not a trusted user certificate.", certificate.Subject);
}
throw new ServiceResultException(new ServiceResult(result, info.Key, "http://opcfoundation.org/Quickstart/ReferenceServer/v1.04", new LocalizedText(info)));
}
}
}
}