C++ Based OPC UA Client/Server/PubSub SDK  1.8.3.628
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(2048);
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 );
OPC UA specific certificate information.
Definition: uapkicertificate.h:57
long validTime
The time in seconds this certificate should be valid.
Definition: uapkicertificate.h:65
Class for handling X509 certificates.
Definition: uapkicertificate.h:160
Identity for certificate issuer and subject.
Definition: uapkiidentity.h:44
Wrapper class for a private key.
Definition: uapkiprivatekey.h:44
UaPkiPublicKey.
Definition: uapkipublickey.h:53
UaPkiRsaKeyPair.
Definition: uapkirsakeypair.h:47
Wrapper class for the UA stack structure OpcUa_String.
Definition: uastring.h:101
UaString arg(const UaString &a, int fieldWidth=0, const UaChar &fillChar=UaChar(' ')) const
Returns a copy of this string with the lowest numbered place marker replaced by string a,...
Definition: uastring.cpp:1785

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 UaByteArray handles the access to an array of bytes.
Definition: uabytearray.h:52
UaDir.
Definition: uadir.h:63
static UaUniString fromNativeSeparators(const UaUniString &sPathName)
Returns pathName using '/' as file separator.
Definition: uadir.cpp:164
const char * toUtf8() const
Returns the string as a '\0'-terminated array of characters.
Definition: uastring.cpp:1452
UaByteArray toUtf16() const
Returns a UTF-16 representation of the string as a UaByteArray.
Definition: uastring.cpp:1483
OPC UA string handling class.
Definition: uaunistring.h:77

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
}
OpcUa_Boolean isBad() const
Checks if the status code is BAD.
Definition: statuscode.h:215
This class handles status codes, conversions of the status code and diagnostic information.
Definition: statuscode.h:281

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);
}
Wrapper class for the UA stack structure OpcUa_ByteString.
Definition: uabytestring.h:45
UaString toString() const
Converts the UaDateTime into a UaString.
Definition: uadatetime.cpp:276
UaPkiIdentity issuer() const
Returns the certificate issuer identity.
Definition: uapkicertificate.cpp:1397
UaString commonName() const
Returns the certificate's commonName field.
Definition: uapkicertificate.cpp:1353
UaDateTime validFrom() const
Returns the start date of the certificate's valid time period.
Definition: uapkicertificate.cpp:1718
static UaPkiCertificate fromDER(const UaByteArray &DERdata)
Loads a certificate from a DER encoded byte array.
Definition: uapkicertificate.cpp:1065
UaDateTime validTo() const
Returns the end date of the certificate's valid time period.
Definition: uapkicertificate.cpp:1754
This Built-in DataType defines a value that is a sequence of Byte values.