MeeGo 1.2 Harmattan Developer Documentation Develop for the Nokia N9

Using Harmattan security features

Overview of application security

Application security refers to procedures used in the design, development, deployment, upgrade, and maintenance of an application to ensure that it functions according to the security policy used by it and the system it runs on. These procedures include, for example, secure coding. This guide concentrates in giving instructions on how to use the Harmattan platform security framework to make your application secure. If Harmattan applications need to access protected resources or the application provides protected resources, they must be defined in the Aegis manifest file.

A protected resource is a system resource or service that is not available by default for an application. Such resources are protected with the corresponding resource token or another credential. Applications are required to announce to which protected resources they request access, and to define if they provide the same kind of protected resources to other applications. For example, an application can access the cellular function, or a service can provide location services. Only the processes that have the required security credentials can access protected resources. Moreover, software from different software sources may be blocked from accessing certain resources.

When assessing the security needs of your application, consider the following questions:

If you answer 'yes' to any of the questions, see the instructions that apply to your application.

If you have no specific needs, you still have to consider secure coding.

Providing protected resources

The following file provides an example of an Aegis manifest file for a server. The Aegis manifest file provides a number of interfaces over D-Bus. The example assumes that there is a global resource token, TestToken, in the system. The server in the example provides the following interfaces:

  • com.nokia.server.datarequest with the methods:
    • get_data
    • get_raw_data
    • check_data_status
    For the com.nokia.server.datarequest interface, the server uses local resource tokens to protect the get_data ("get-data" resource token) and get_raw_data ("get-raw-data" resource token) methods. The check_data_status method should not be protected with any resource tokens.
  • com.nokia.server.datainfo
    For the com.nokia.server.datainfo interface, the server would like to use the global token TestToken to protect the entire interface.
    The binding to the service name is protected with a local dbus-server-bind token.


<aegis>
        <provide>
                <credential name="get-data" />
                <credential name="get-raw-data" />
                <credential name="dbus-server-bind" />
                <dbus name="com.maemo.server" own="dbus-server-bind" bus="session">
                        <node name="/">
                                <interface name="server.datarequest">
                                        <method name="get_data">
                                                <annotation name="com.maemo.Aegis" value="get-data"/>
                                        </method>
                                        <method name="get_raw_data">
                                                <annotation name="com.maemo.Aegis" value="get-raw-data"/>
                                        </method>
                                <interface name="server.datainfo">
                                        <annotation name="com.maemo.Aegis" value="TestToken"/>
                                </interface>
                        </node>
                </dbus>
        </provide>
        <request>
                <credential name="dbus-server-bind" />
                <for path="/usr/bin/aegis-dbus-server" />
        </request>
</aegis>

Creating specific user accounts and groups

To create specific user accounts and groups by using the Aegis manifest file, use the optional <account> element. The element creates users and groups when the application package is installed, even if you specify a non-existent group.

To define the name and group for the account, use the <user> element within the <account> element:

  • name attribute is the user name (UID)
  • group attribute is the group name (GID)

The following is an example of an <account> definition:

<aegis>
  <account>
    <user name="donald" group="duck"/>
    <user name="mickey" group="mouse"/>
  </account>
</aegis>

Note: The users and groups created when the application package is installed are not removed if the application package itself is removed.

Requesting specific user accounts and groups

Note: You can only use an account created through an Aegis manifest file to run applications with the UID and GID defined in it. You cannot use the account, for example, to log into the device. In addition, the account does not have a home folder.

To define the requested credential, use the value of the name attribute within a <credential> element. The general syntax is:

[namespace "::"] name

To request the system to run your application under a specific user identifier or group identifier, use the UID::username and GID::groupname attribute values. The UID and GID must already exist on the device or the request fails. To create new UIDs and GIDs, see section Creating specific user accounts and groups.

The following is an example of a <credential> definition:

<aegis>
   <request>
     <credential name="UID::myuser" />
     <credential name="GID::mygroup" />
     <for path="/usr/bin/myapp" />
   </request>
</aegis>

Requesting supplementary groups

To request to have the group included in supplementary groups, use the GRP::group attribute value.

The following is an example of a supplementary group request:

<aegis>
   <request>
     <credential name="GRP::dial-out" />
     <for path="/usr/bin/myapp" />
   </request>
</aegis>

Requesting POSIX capabilities

If your application needs any POSIX capabilities, the following example shows how to request POSIX capabilities for your application.

Note that POSIX capabilities are sensitive credentials. Even if you use the request, whether you get the capabilities or not depends on the software source.

<aegis>
   <request>
      <credential name="CAP::chown" />
      <credential name="CAP::kill" />
      <for path="/usr/bin/myapp" />
   </request>
</aegis>

Requesting resource tokens

The Aegis manifest file syntax depends on the token that the application package needs to request. There are two types of tokens:

  • Global tokens are declared in the aegis-global-tokens package. You can request these tokens directly in the <request> sections.
    The following is an example of a global token request:
<aegis>
   <request>
      <credential name="Cellular" />
      <for path="/usr/bin/userdatamanager" />
   </request>
</aegis>

The above example states that whenever /usr/bin/userdatamanager is executed, its credential set includes the Cellular credential.

  • Internal tokens can be provided by any package. When you request an internal token, you need to add the package name as the prefix in the name attribute.
    The following is an example of an Aegis manifest file requesting an internal token:

A package called cool-tools includes the following <provide> section:

<aegis>
   <provide>
     <credential name="UserData" />
   </provide>
</aegis>

To request this resource token, a package must include the following <request> section in its Aegis manifest file:

<aegis>
   <request>
      <credential name="cool-tools::UserData" />
      <for path="/usr/bin/userdatamanager" />
   </request>
</aegis>

Requesting an application identifier

On the Harmattan system, the term 'application identifier' refers to a unique identifier for an application on the platform. Such a unique identifier can be used in order to keep application data secure. For more information, see Protecting the server's data store by using encryption and signing.

By default, all binaries that have even an empty request in their Aegis manifest file get an application identifier in the format AID::sw_source_name.packagename.. To request different application identifiers for two or more binaries in the same package, add an additional id attribute in the <for> element of your request. The value of the id attribute is combined with the source domain and package name, and the resulting string is the actual application identifier (AID::sw_source_name.packagename.id).

The following is an example of an application identifier request:

<aegis>
   <request>
      <for path="/usr/bin/foo" id="foo" />
   </request>
</aegis>

Requesting additional credentials for a different binary from plug-in package

If you are creating a plug-in that needs some credentials in order to access protected interfaces, you may need to request these credentials for the main application binary that usually resides in another package. In order to do that, the main application binary should have at least an empty manifest request in its package:

<aegis>
     <request>
         <for path="/full/path/to/main/application" />
     </request>
</aegis>

In order to request credentials for the main application binary package that includes the above Aegis manifest file, create the following Aegis manifest file:

<aegis>
     <request>
         <credential name="cred_name_1" />
         <credential name="cred_name_2" />
         <for path="main_application_package::/full/path/to/main/application" />
     </request>
</aegis>

In some cases, you may want the main application to drop unnecessary credentials before loading a plug-in (a shared library). This is a good practice if you want to limit the amount of credentials that plug-in has access to.

Dropping the credentials to the level of the plug-in

In order to drop the credentials to the level needed by a certain plug-in that the main application loads, an application should use the creds_confine2() function from the libcreds2 library. Provide a full path to the plug-in (shared library) with the function. However, in order for this function to work, the plug-in package should not only request credentials for the main application in its manifest, but also for itself:

<aegis>
     <request>
         <credential name="cred_name_1" />
         <credential name="cred_name_2" />
         <for path="main_application_package::/full/path/to/main/application" />
         <for path="/full/path/to/the/plug-in" />
     </request>
</aegis>

The main application should call creds_confine2 providing the /full/path/to/the/plug-in before loading the plug-in (shared library).

Requesting credentials for a pre-started application

If your application is pre-started with the application launcher, you need one more line in your Aegis manifest file, which grants the same set of credentials to the application launcher binary:

<aegis>
     <request>
         <credential name="cred_name_1" />
         <credential name="cred_name_2" />
         <for path="/usr/bin/myapp" />
         <for path="applauncherd-launcher::/usr/bin/applauncherd.bin" id="" />
     </request>
</aegis>

This definition also makes sure that your application is started with the correct application identifier.

Everything that is requested for the application binary must be requested to the application launcher binary as well.

If you specify your own application identifier, you must also refer to the same identifier in the for statement for the application launcher:

<aegis>
  <request>
    <credential name="cred_name_1" />
    <credential name="cred_name_2" />
    <for path="/usr/bin/myapp" id="myappid" />
    <for path="applauncherd-launcher::/usr/bin/applauncherd.bin" id="myappid" />
  </request>
</aegis>

Addressing specific security needs

This section presents a simple example of server and client applications to demonstrate certain security features that applications can use. New features are gradually added to the example with references to corresponding branches in the repository. The example uses Qt APIs. Therefore, you can more easily follow the examples if you are familiar with creating Qt applications.

To evaluate which features your application needs, see the questions in Overview of application security.

Providing new resource tokens

To use the examples in the following sections, you must understand how to create new resource tokens in the Harmattan system. This is done with the <provide> element.

Use the <provide> element to define credential tokens specific to your application. The <provide> element can contain one or more <credential> elements. Each <credential> definition defines a new application-specific token. The name you give in the <credential> element is implicitly prefixed with the name of the application package, and cannot have a namespace definition. You cannot provide a token that belongs to another package.

The following example shows how to use the <provide> element:

<aegis> 
   <provide> 
      <credential name="encrypt"> 
          <docstring> 
             A Token that is used for encrypting data. 
          </docstring> 
      </credential> 
   </provide> 
</aegis> 

Basic example of server and client communication

The master branch of the server and client application example contains the Qt implementation of the client and server without any additional security features. The server provides two interfaces:

  • com.meego.mssf, a D-Bus interface on the system bus
  • /tmp/mssf.socket, a Unix domain socket interface

The only method, provided by the server over the com.meego.mssf D-Bus interface is setState, which enables setting the internal state of the server. The same functionality is available over the /tmp/mssf.socket local socket interface. The client program calls the server D-Bus interface with all possible states and tries to set a state by using local socket communication. For more information, see Client::run method.

The server also provides a standard D-Bus configuration policy, com.meego.mssf.conf, which allows all applications to access this interface. The daemon stores its configuration settings in the /tmp/mssf-demo.storage file and has two classes, ConfigWriter and Configuration, to access this storage.

Communication between server and client

Protecting the server's data store by using standard Linux permissions

In the basic example above, the server uses the configuration data from the /tmp/mssf-demo.storage file. To protect the integrity and confidentiality of the data stored in this file, you can use the standard Linux file system permissions, which work in the same way on the device.

As a number of online tutorials on Linux file system access control are available (for example, File System Permissions), all details of suitable setups for different use cases are not covered in this topic. Instead, one possible setting is presented in the example below.

To allow only the server application to read and write in the data storage, and others only to read the storage, specify the permissions for the file in the following way:

     % ls -l /tmp/mssf-demo.storage
      -rw-rw-r-- 1 appd appd 123 Jan 22 16:29 mssf-demo.storage

In this setting, the user and group that own the file are called appd, and only this user and group have a right to read and write in the file. Others are only allowed to read. You must create this user and group and request the corresponding group in the Aegis manifest file of the server application.

Protecting the server's data store by using encryption and signing

Protecting the configuration data file of the application server against offline attacks is necessary because data can be modified even if the device is switched off. Linux access control permissions cannot be used for this because they are not enforced when the device is switched off.

The next branch presents a method to provide confidentiality of the daemon's configuration data by using encryption and decryption methods. This example does not guarantee the integrity of configuration data, because it does not use signing methods. To add integrity protection, use the signing and verification methods provided below.

To see how encryption and signing of data transmitted from the server to client is added to the example, see the difference between this branch and the previous one.

The main difference is the presence of a new class, AegisCrypto, which provides methods to sign and verify, as well as encrypt and decrypt the data. The daemon program uses this class to encrypt and decrypt the configuration data in the ConfigWriter class. For information on AegisCrypto API, see Platform API reference.

If you take a look at the code inside the AegisCrypto class, you see that it uses methods declared in aegis_crypto.h (libaegis-crypto library). This library provides a method to perform cryptographic operations by using the keys stored securely in the Trusted Execution Environment (TEE) of the device. For each cryptographic operation, a key is derived inside TEE from the device-specific symmetric key and the credential defined in the method. The credential can be either a resource token or an application identifier. (If a resource token is not specified, an application identifier is used by default.) The choice of credential defines, for example, which applications can decrypt the encrypted data. The security driver in the device ensures that the calling application possesses a credential specified in the method. The method returns the requested result only if this credential is found. In this example, only an application identifier is used, and therefore all the encryption and decryption calls succeed.

A more detailed look at the simple encryptData method reveals that it first checks the input parameters and declares needed variables. After this, it calls the aegis_crypto_encrypt method, where it supplies the clear text data, its length, and the credential to be used for encryption (token), and receives the cipher text with its length. If something fails, the returned data is cleaned. If everything is successful, the data is copied and returned.

The following example illustrates the encryptData method:

 
     if (aegis_crypto_encrypt(clearText.data(), clearText.length(), token, &cipherText, &cipherLength) != aegis_crypto_ok)
     {
        aegis_crypto_free(cipherText);
        qDebug("Failed to encrypt data: %s", aegis_crypto_last_error_str());
        return QByteArray();
     }  
     QByteArray encrypted((char *)cipherText, cipherLength);
     aegis_crypto_free(cipherText);
     return encrypted;

Protecting the server's data store by using the protected storage API

You can use another set of methods to store the configuration data securely and manage the file operations. These methods are defined in aegis-storage.h provided by the libaegis-crypto library. You can use the methods to:

To learn how to use these methods, see the next branch of the server and client example.

The main addition compared to the previous branch is a new class, AegisStorage, which operates with the protected storage content directly. It contains methods for reading and writing data into the files stored in the protected storage, and for cleaning the protected storage and listing the files.

The most important functionality is in the aegisStorageWrite function:

        QSharedPointer<storage> aegisStorage(new storage(storageName, token, storage::vis_private, storage::prot_encrypted));
        if (aegisStorage->put_file(storagePath, (void *)data.data(), data.length()) != 0)
        {
          qDebug("Failed To create or retrieve an Aegis-Storage location: %s", aegis_crypto_last_error_str());
          return false;
        }
        aegisStorage->commit();

This function creates a new encrypted storage with a specified storage name. Because the function includes the flags vis_private and prot_encrypted, only the current application can read and write into the storage. The function also adds a new file into the storage with specified data and commits the storage. For more information about the different flags, see lines 304-337 in the aegis-storage.h file.

Protecting the server's data by storing it in /home/user/private

AegisFS is a cryptographic userspace file system that enables mounting protected stores or zip files as directories anywhere in the file system. It starts automatically at boot time and mounts the directories defined in its configuration files. Applications can use these directories as any other directories in the system, provided that they have the necessary credentials. Almost all POSIX file system calls are supported including managing sub-directories, symbolic links and hard links, file locking, random access, regular file attributes and discretionary access control.

AegisFS supports two types of directories. Signed directories are implemented by help of signed protected stores to guarantee data integrity. Typically anyone is free to read the files in them but modification is allowed only for processes that have a speficic resource token. Encrypted directories store their data in encrypted protected stores and both reading and writing require an access token.

A number of platform packages define mountpoints for their own use by adding configuration files in the /etc/aegisfs.d directory. Each configuration file needs to be registered by command aegisfs --install <configuration-file>, which requires credential aegisfs::AegisFSMountAdd. Non-platform packages cannot register new mountpoints.

The encrypted /home/user/private directory is available for all applications for storing sensitive information. Anything written to this directory will be automatically encrypted and available only to the package that created it.

How does this work? Each binary mentioned in a security manifest is automatically assigned a special resource token called the application ID. It is of form AID::<source-id>.<package-name>.[<binary-id>], for instance AID::com.nokia.maemo.aegis-crypto-tools.apscli. Processes that have not been assigned an application ID use the default AID::unknown.unknown..

When the aegisfs process that serves /home/user/private receives requests it checks what is the application ID of the caller and selects a private protected store for the directory contents accordingly. If the application ID has not been seen earlier, a new protected store is created and an empty directory is shown. To illustrate how this works.

A remote developer shell through SSH connection does not have identity

 $ accli -qI
 Credentials:
         UID::user
         GID::users
         GRP::adm
         GRP::dialout
         GRP::video
         GRP::pulse-access
         GRP::users

...so everything it does in /home/user/private goes into the unknown store.

 $ date > /home/user/private/edate
 $ cat /home/user/private/edate 
 Sat Jan  1 03:15:49 EET 2000
 $ ls -l /home/user/private/
 total 1
 -rw-r--r--    1 user     users           29 Jan  1 03:15 edate

A local terminal on the other hand inherits its identity from the Home Screen application:

 $ accli -qI
 Credentials:
         UID::user
         GID::users
         SRC::com.nokia.maemo
         AID::com.nokia.maemo.meegotouchhome-nokia.
         meegotouchhome-nokia::meegotouchhome-nokia

So it has its own idea of what /home/user/private contains:

 $ ls -l /home/user/private/
 total 0
 $ date > /home/user/private/edate
 $ cat /home/user/private/edate
 Sat Jan  1 03:19:55 EET 2000

Even if both clients created a file called /home/user/private/edate they are two physically different files encrypted by different keys and stored in two different protected stores:

 $ apscli -lu | grep ^private
 private@com.nokia.maemo.meegotouchhome-nokia.:Pe
 private@unknown.unknown.:Pe

To get identity a binary needs to be mentioned in a for-tag of a manifest. This is a minimal security manifest required to create a new application id:

 <aegis>
   <request policy="add">
      <for path="/opt/coolapp/mainbin" />
   </request>
 </aegis>

By default all binaries from the same package get the same application id, so they have the same view to /home/user/private. If the data must be accessible only by a single binary it can be given an optional binary ID in the manifest as <for path="/opt/coolapp/mainbin" id="mainbin"/>.

Securing the communication between the server and client by using data signing and encryption

To learn how to secure communication between the server and client, see the next branch.

In this example, the server sends an encrypted reply over the Unix domain socket to the client:

QByteArray encrypted = AegisCrypto::encryptData(data, Mssf::tokenName);

The server uses the methods from the AegisCrypto class again, but this time it specifies a credential. This credential is the Mssf::tokenName variable, which contains the mssf-demo::encrypt internal resource token used to derive the key for the encryption. The server application also has an Aegis manifest file where it provides this new credential, and requests it and a number of other credentials.

The client application also requests the new resource token in its Aegis manifest file, and uses the AegisCrypto class to decrypt the server reply:

QDataStream byteStream(AegisCrypto::decryptData(data, Mssf::tokenName));

Protecting the server's D-Bus interface by using resource tokens

The master branch includes a D-Bus configuration file, which specifies that all applications can send messages and bind to this interface. To protect the D-Bus interface provided by the server, you must create a more restricted policy by using an Aegis manifest file, as shown in the next branch.

The main difference to the master branch is that the com.meego.mssf.conf file is removed and the server Aegis manifest file is extended. The Aegis manifest file provides two new resource tokens: MssfDBusServiceOwn and StateSetter. MssfDBusServiceOwn is used to define which application can own the specified interface on a system bus:

<dbus name="com.meego.mssf" own="MssfDBusServiceOwn" bus="system">

StateSetter is used to specify the credential required to call a setState method:

    <node name="/">
        <interface name="com.meego.mssf">
           <annotation name="com.maemo.Aegis" value=""/>
              <method name="setState">
                <annotation name="com.maemo.Aegis" value="StateSetter"/>
              </method>
       </interface>
    </node>

The server also requests both new credentials in addition to the ones in the original Aegis manifest file.

If you compile and start the client and server in this branch, you see that the client receives the message "Access denied" from the D-Bus daemon when trying to call the setState method. This happens because the client does not possess the required mssf-demo::StateSetter credential.

To learn how to request the missing credential for the client, see the next branch. One line is added into the client Aegis manifest file, enabling the client to call the setState method from the com.meego.mssf interface.

Protecting the server's interfaces by using server-enforced access control

Some cases demand a more secure or fine-grained solution than specifying a credential to call a particular method. This means that access control decisions cannot be delegated to the D-Bus daemon, but the server must make the access decisions itself. In this example, the server makes access control decisions itself using a special user space library, libcreds2.

See the changes that are needed to the daemon part.

You can use the libcreds2 library through two APIs:

This example uses the Qt wrapper.

In the example code, you can see that the daemon defines two new resource tokens: Stop_State and Clean_State. These are required to perform calls to setState method to change the daemon state to stop or clean, respectively. (As presented in the previous section, you also need the resource token required to invoke the setState method itself.) Other state changes do not require a token.

The first daemon sets the credential value based on the requested state:

     switch ((Mssf::State)state)
     {
        case Mssf::Clean:
                      credential = QLatin1String("mssf-demo::Clean_State");
                      break;
        case Mssf::Stopped:
                      credential = QLatin1String("mssf-demo::Stop_State");
                      break;
        default:
                      break;
     }

Then, if a credential is needed to change the state, the daemon checks it in the following call:

      if (!DBusContextAccessManager::hasClientCredential(*this, credential, &errorString))
      {
           qDebug() << "Failed to validate credential: " << errorString;
           sendErrorReply(QDBusError::AccessDenied, "Caller does not possess the correct credentials");
           return false;
      }

If the client does not possess the necessary credential, the daemon sends an error as a reply and does not change the state. If you compile and run this branch, you see that the client cannot set the Clean and Stop states because it does not have the necessary credentials. The next branch adds credentials to the client, enabling the client to set all possible states for the daemon.

Protecting the application's UNIX socket interface

After protecting the D-Bus socket interface of the server, you must also protect the Unix local socket interface. Because there is no D-Bus daemon between the server and client, the server must use libcreds2 library, as in the previous example.

To learn how to protect the Unix local socket interface, see the changes required for the server. In the Aegis manifest file, the server provides an additional resource token, SocketSetter, which is checked for all clients connecting over a Unix socket to the server.

The server checks the client credentials in the following call:

    if (!CredentialsManager::hasSocketCredential(sockClient->socketDescriptor(), Mssf::SocketSetter, &errorString))
    {
        qDebug() << "Failed to validate credential: " << errorString;
    }

If the client does not have the required mssf-demo::SocketSetter credential, the error is printed and the state is not set. As in the previous example, if you compile and run this branch, you see that the client cannot set the state of the server through a Unix local socket interface. To succeed, the client must request the mssf-demo::SocketSetter token in its Aegis manifest file.

Managing certificates with Certificate Manager

When communicating and authenticating with servers using the secure SSL/TLS protocol, take care of the integrity of the related X.509 certificates and the privacy of their private keys. For this purpose, the Harmattan platform provides a protected certificate store which is based on the protected storage implementation mentioned above.

The integrity of the certificates must be protected because the authenticity of the remote servers is verified by using them. The device comes with a pre-installed set of root Certificate Authority (CA) certificates, which cannot be verified by any other means but just have to be trusted. An attacker that is able to inject malicious root CA certificates in the system can spoof SSL/TLS servers and perform a man-in-the-middle attack, for example, against secure connection to banking sites.

When user certificates are used to log in to remote services, the security is based on the privacy of the private keys which are used to sign a challenge sent by a server. If private keys can be extracted from the system, they can be used to log in as the device owner and fake the owner's identity.

On the other hand, it must be possible to install new CA certificates in the system to authenticate servers that are signed, for example, with a corporate key. To limit the damage a fake CA certificate can cause, they are divided into certificate domains according to the intended use:

  • Each device comes with a pre-installed common-ca root certificate store, which cannot be modified by any means other than by an authorized software update from trusted source. These root certificates can be used for all purposes.
  • The domains ssl-ca, ssl-user, wifi-ca and wifi-user contain the signing CA and user certificates for authenticating and logging in to SSL/TLS servers and WiFi access points. These domains can be modified by the Certificate management applet available in the settings, which allows adding and removing new CA and user certificates. The purpose of a certificate can be selected by the user at installation time.
  • There are also the domains, smime-ca, smime-user, codesign-ca and codesign-user, for certificates used from S/MIME e-mail and code signing purposes. Currently there are no applications that would use these domains, so they are typically empty.

Applications can use the secure certificate stores in one of three ways:

  • Using the proprietary aegis_certman.h API.
    This is a low-level C-API that has been designed as an extension to the OpenSSL crypto and communication library. The function call, aegis_certman_collect, can be used to retrieve a populated certificate store of type X509_STORE that contains the certificates from one or more domains and used in the SSL handshake for authenticating the peer. An application that uses this function can rely on the integrity of the certificate set.
  • Using the PKCS#11 interface.
    Applications that use the Network Security Services (NSS) crypto and communication library can configure libaegis_certman.so.0 as a loadable security module and access the certificate stores in a secure way through it by using the NSS library's functions in the normal way. Only reading of certificates is currently supported through the PKCS#11 module.
  • Reading certificates from the OpenSSL's default directory /etc/ssl/certs.
    This method is also secure because the directory is served by the secure file system which maps the contents of the directory to the combined contents of the common-ca and ssl-ca certificate stores.

The last method is usually the easiest way to ensure the integrity of the root CA set. Since QNetwork uses this directory by default, all Qt applications that do not change the default settings use it with no further effort.

If your application requires implementing secure authentication using the stored user certificates and private keys, it should use the functions of aegis_certman.h to iterate through the certificates stored for a purpose and to access their private keys, which are stored as encrypted PKCS#8 envelope files.

Modifying the modifiable CA domains requires having one of resource tokens CertCASSLAdd, CertCAWifiAdd, CertCASMIMEAdd or CertCACodeSignAdd, depending on which certificate store is being modified. Using the private keys requires one of tokens CertUserSSLUse, CertUserWifiUse, CertUserSMIMEUse or CertUserCodeSignUse in the same way. All these resource tokens are declared in package aegis-certman-common-ca and must be prefixed by the package name. An application that requires modifying CA domains or getting access to the private keys must claim the respective resource tokens in its package manifest.

Further information

For more information on security, see the following: