UA Server SDK C++ Bundle  1.4.3.291
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
Security

Initial application configuration

The client application needs an initial configuration that defines and creates the

  • used certificate store,
  • the application instance certificate,
  • and the application instance URI

The default certificate store is the file based OpenSSL PKI store. The store has the following directories and subdirectories

  • own
    • certs
      The public key of the application. Certificates have to be stored in DER format (with file extension .der).
    • private
      The private key of the application. The private key is encoded in PEM format (with .pem as file extension).
  • trusted
    • certs
      The folder where certificates of trusted applications and trusted Certificate Authorities (CAs) should be stored. Each CA requires one and only one Certificate Revocation List (CRL) in the parallel crl directory. Certificates have to be stored in DER format (with file extension .der).
    • crl
      The folder where revocation lists for trusted CAs should be stored. A CRL may be empty if no certificates have been revoked yet. Revocation lists have to be stored in DER format (with file extension .crl) or in PEM format (with .pem as file extension).
  • issuers
    • certs
      The folder where issuer certificates are stored. Issuer certificates are CA certificates necessary for the verification of the full trust chain of CA certificates in the trust list. Each CA requires one and only one CRL. The CRL may be empty if no certificates have been revoked yet.
    • crl
      The folder where revocation lists for issuer CAs should be stored.

The method ClientSecurityInfo::initializePkiProviderOpenSSL() is used to initialize the security context with the file directory paths for trusted and issuer certificates. The security context is used for discovery and connect calls to the server.

On Windows systems the Windows certificate store can be used instead of the file store. The following steps can be used to open the windows certificate store

  • Start → Run → mmc
  • File → Add Snap-In
  • Add
  • Select Certificates from the list
  • Select location, the default location is LocalComputer

The method ClientSecurityInfo::initializePkiProviderWindows() is used to initialize the security context. It is used for discovery and connect calls to the server. The store location (Location_LocalMachine or Location_CurrentUser) and the store name is passed to initializePkiProviderWindows().

Before the client is used the first time, a client instance certificate with public and private key needs to be created or provided by the administrator of the application. A default certificate can be created during setup or at the first start of the application. The following code creates a certificate. The strings for the identity should be provided by the administrator of the application.

// Create the certificate
UaPkiRsaKeyPair keyPair ( 1024 );
UaPkiPrivateKey IssuerPrivateKey;
UaPkiPublicKey SubjectPublicKey;
UaPkiIdentity identity;
UaString sNodeName;
char szHostName[256];
if ( 0 == gethostname(szHostName, 256) )
{
sNodeName = szHostName;
}
// The following paramters should be provided by the administrator of the client application
identity.commonName = UaString("ProductName@%1").arg(sNodeName);;
identity.organization = "Organization";
identity.organizationUnit = "Unit";
identity.locality = "LocationName";
identity.state = "State";
identity.country = "DE";
identity.domainComponent = "MyComputer";
// The URI must be unique. This can be done by combining the vendor and product name with the host name
info.URI = UaString("urn:%1:UnifiedAutomation::Client_Cpp_SDK").arg(sNodeName);
info.DNS = sNodeName;
info.validTime = 3600*24*365*5; // seconds
IssuerPrivateKey = keyPair.privateKey();
SubjectPublicKey = keyPair.publicKey();
// create a self signed certificate
UaPkiCertificate cert ( info, identity, SubjectPublicKey, identity, IssuerPrivateKey );

The following code is an example for storing the created certificate in the file based OpenSSL PKI store.

// Path and file name of the client public key (certs directory)
UaString sClientCertificateFile;
// Path and file name of the client private key (private directory)
UaString sClientPrivateKeyFile;
// First make sure the directories exist
UaDir dirHelper("");
UaUniString usClientCertificatePath(dirHelper.filePath(UaDir::fromNativeSeparators(sClientCertificateFile.toUtf16())));
dirHelper.mkpath(usClientCertificatePath);
UaUniString usPrivateKeyPath(dirHelper.filePath(UaDir::fromNativeSeparators(sClientPrivateKeyFile.toUtf16())));
dirHelper.mkpath(usPrivateKeyPath);
// Store the public key in the file in DER format
cert.toDERFile ( sClientCertificateFile.toUtf8() );
// Store the private key in the file
keyPair.toPEMFile ( sClientPrivateKeyFile.toUtf8(), 0 );
UaByteArray DERData = keyPair.toDER();

The following code is an example for storing the created certificate in the Windows certificate store.

// The store location in the Windows store like Location_LocalMachine or Location_CurrentUser
WindowsStoreLocation windowsStoreLocation = Location_LocalMachine;
// Name of the store in the location
UaString sWindowsStoreName = "MyApplicationStore";
UaString sThumbprint;
// Store in Windows certificate store
cert.toWindowsStoreWithPrivateKey(windowsStoreLocation, sWindowsStoreName, keyPair);
// Create the thumbprint string to be stored as name
sThumbprint = cert.thumbPrint().toHex();
// Store this string in the configuration
// It is used to load the certificate

Loading the certificates at start up

The initial application configuration with certificate creation needs to be done once. The client certificate need to be loaded at every start up of the client.

The following code is used to load the client certificate (public and private key) from the file based OpenSSL PKI store.

// Session security info is used for UaSession::connect
SessionSecurityInfo sessionSecurityInfo;
UaStatus uStatus;
// Path of the trusted certificates directory (trusted/certs directory)
UaString sCertificateTrustListLocation;
// Path of the revocation list directory for trusted CAs (trusted/crl directory)
UaString sCertificateRevocationListLocation;
// Path of the issuers certificates directory (issuers/certs directory)
UaString sIssuersCertificatesLocation;
// Path of the revocation list directory for issuer CAs (issuers/crl directory)
UaString sIssuersRevocationListLocation;
// Path and file name of the client public key (own/certs directory)
UaString sClientCertificateFile;
// Path and file name of the client private key (own/private directory)
UaString sClientPrivateKeyFile;
// Initialize the PKI provider for OpenSSL
uStatus = sessionSecurityInfo.initializePkiProviderOpenSSL(
sCertificateRevocationListLocation,
sCertificateTrustListLocation,
sIssuersRevocationListLocation,
sIssuersCertificatesLocation);
if(uStatus.isBad())
{
// Error handling
}
// Load certificate and private key for client from OpenSSL store
uStatus = sessionSecurityInfo.loadClientCertificateOpenSSL(
sClientCertificateFile,
sClientPrivateKeyFile);
if(uStatus.isBad())
{
// Error handling
}

The following code is used to load the client certificate (public and private key) from the Windows certificate store.

// Session security info is used for UaSession::connect
SessionSecurityInfo sessionSecurityInfo;
UaStatus uStatus;
// The store location in the Windows store like Location_LocalMachine or Location_CurrentUser
WindowsStoreLocation windowsStoreLocation = Location_LocalMachine;
// Name of the store in the location
UaString sWindowsStoreName = "MyApplicationStore";
// Thumbprint of the certificate in the store.
// This is certificate thumbprint string stored during creation of the certificate
UaString sThumbprint = "3FFAD542EED94A17B5726296BAC39EF8EE28004D";
//Initialize the PKI provider for Windows certificate store
uStatus = sessionSecurityInfo.initializePkiProviderWindows(
windowsStoreLocation,
sWindowsStoreName);
if(uStatus.isBad())
{
// Error handling
}
// Load certificate and private key for client from Windows certificate store
uStatus = sessionSecurityInfo.loadClientCertificateWindows(sThumbprint);
if(uStatus.isBad())
{
// Error handling
}

Getting security configuration from server

The application instance certificates are used for two different tasks. They are used for application authentication and for message encryption and signing. In addition, there are some consistency checks done during application layer connection establishment with CreateSession and ActivateSession.

For application authentication and message encryption and signing, the certificates must be exchanged before the first message is sent. The client must receive the server certificate before creating a secure communication channel. The typical way of getting the server certificate is via GetEndpoints. The client sends its client certificate with the CreateSecureChannel request, to be available on the server side for the CreateSecureChannel response.

Before a client is able to connect to a server, the required security configuration needs to be requested from the server using Discovery functionality. UaDiscovery::findServers() is used to get a list of available servers from a discovery server. UaDiscovery::getEndpoints() is used to get a list of endpoints from the server. An endpoint contains the URL, the server certificate (public key) and the required security settings.

GetEndpoints does not do any check on the certificate, but application authentication must be checked with the client trust list before the secure channel is created.

By setting the server certificate returned from GetEndpoints() to SessionSecurityInfo::serverCertificate, it is available for message encryption and signing. The check for application authentication on the client side can be done by the client application with SessionSecurityInfo::verifyServerCertificate() before UaSession::connect() is called, or it is executed by the Client SDK if SessionSecurityInfo::doServerCertificateVerify is set to true.

The following code shows how to verify a server certificate and store it if the user accepts the certificate and allows access to the server.

// Session security info is used for UaSession::connect
SessionSecurityInfo sessionSecurityInfo;
// Needs to be initialized with the certificate store information before used here
UaByteString serverCertificate;
// Get server certificate from GetEndpoints
sessionSecurityInfo.serverCertificate = serverCertificate;
// Check if the certificate is already trusted
if ( sessionSecurityInfo.verifyServerCertificate().isBad() )
{
// Show certificate data to user and ask if the certificate should be added to the trust list
// Assign certificate byte string to UaPkiCertificate class
UaByteArray derCertificate(*(const OpcUa_ByteString*)serverCertificate);
cert = cert.fromDER(derCertificate);
printf("- CommonName %s\n", cert.commonName().toUtf8() );
printf("- Issuer.commonName %s\n", cert.issuer().commonName.toUtf8() );
printf("- Issuer.organization %s\n", cert.issuer().organization.toUtf8() );
printf("- Issuer.organizationUnit %s\n", cert.issuer().organizationUnit.toUtf8() );
printf("- Issuer.state %s\n", cert.issuer().state.toUtf8() );
printf("- Issuer.country %s\n", cert.issuer().country.toUtf8() );
printf("- ValidFrom %s\n", cert.validFrom().toString().toUtf8() );
printf("- ValidTo %s\n", cert.validTo().toString().toUtf8() );
// Store certificate in trust list if user agrees
// The returned certificate name can be stored together with the server connect information
// to load the server certificate from the store for a later connect based on stored information
// The server certificate can be loaded by using SessionSecurityInfo::loadServerCertificateOpenSSL or SessionSecurityInfo::loadServerCertificateWindows
UaString certificateName;
sessionSecurityInfo.saveServerCertificate(certificateName);
}