UA Server SDK C++ Bundle  1.4.2.275
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups 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 three directories

certs
contains the trusted certificates and the public key of the client application
crl
contains the revocation list
private
contains the private key of the client application

The method ClientSecurityInfo::initializePkiProviderOpenSSL() is used to initialize the security context. It is used for discovery and connect calls to the server. The path of the trust list (certs directory) and of the revocation list (*.pem file in the crl directory) is passed to initializePkiProviderOpenSSL().

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 and to create an empty revocation list.

// 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;
// Path and file name of the revocation list (crl directory)
UaString sCertificateRevocationListLocation;
// 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);
UaUniString usRevocationListPath(dirHelper.filePath(UaDir::fromNativeSeparators(sCertificateRevocationListLocation.toUtf16())));
dirHelper.mkpath(usRevocationListPath);
// 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();
// Create revocation list
UaPkiRevocationList revocationList(info.validTime, identity, 1);
revocationList.sign(keyPair.privateKey());
revocationList.toPEMFile ( sCertificateRevocationListLocation.toUtf8() );

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 revocation list directory (certs directory)
UaString sCertificateTrustListLocation;
// Path and file name of the revocation list (crl directory)
UaString sCertificateRevocationListLocation;
// 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;
// Initialize the PKI provider for OpenSSL
uStatus = sessionSecurityInfo.initializePkiProviderOpenSSL(
sCertificateRevocationListLocation,
sCertificateTrustListLocation);
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

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.

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);
}