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.
char szHostName[256];
if ( 0 == gethostname(szHostName, 256) )
{
sNodeName = szHostName;
}
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";
info.URI =
UaString(
"urn:%1:UnifiedAutomation::Client_Cpp_SDK").
arg(sNodeName);
info.DNS = sNodeName;
IssuerPrivateKey = keyPair.privateKey();
SubjectPublicKey = keyPair.publicKey();
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.
UaString sCertificateRevocationListLocation;
dirHelper.mkpath(usClientCertificatePath);
dirHelper.mkpath(usPrivateKeyPath);
dirHelper.mkpath(usRevocationListPath);
cert.toDERFile ( sClientCertificateFile.
toUtf8() );
keyPair.toPEMFile ( sClientPrivateKeyFile.
toUtf8(), 0 );
revocationList.sign(keyPair.privateKey());
revocationList.toPEMFile ( sCertificateRevocationListLocation.
toUtf8() );
The following code is an example for storing the created certificate in the Windows certificate store.
WindowsStoreLocation windowsStoreLocation = Location_LocalMachine;
UaString sWindowsStoreName =
"MyApplicationStore";
cert.toWindowsStoreWithPrivateKey(windowsStoreLocation, sWindowsStoreName, keyPair);
sThumbprint = cert.thumbPrint().toHex();
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.
SessionSecurityInfo sessionSecurityInfo;
UaString sCertificateRevocationListLocation;
uStatus = sessionSecurityInfo.initializePkiProviderOpenSSL(
sCertificateRevocationListLocation,
sCertificateTrustListLocation);
{
}
uStatus = sessionSecurityInfo.loadClientCertificateOpenSSL(
sClientCertificateFile,
sClientPrivateKeyFile);
{
}
The following code is used to load the client certificate (public and private key) from the Windows certificate store.
SessionSecurityInfo sessionSecurityInfo;
WindowsStoreLocation windowsStoreLocation = Location_LocalMachine;
UaString sWindowsStoreName =
"MyApplicationStore";
UaString sThumbprint =
"3FFAD542EED94A17B5726296BAC39EF8EE28004D";
uStatus = sessionSecurityInfo.initializePkiProviderWindows(
windowsStoreLocation,
sWindowsStoreName);
{
}
uStatus = sessionSecurityInfo.loadClientCertificateWindows(sThumbprint);
{
}
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.
SessionSecurityInfo sessionSecurityInfo;
sessionSecurityInfo.serverCertificate = serverCertificate;
if ( sessionSecurityInfo.verifyServerCertificate().isBad() )
{
UaByteArray derCertificate(*(
const OpcUa_ByteString*)serverCertificate);
cert = cert.
fromDER(derCertificate);
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() );
sessionSecurityInfo.saveServerCertificate(certificateName);
}