Working with SAP Kapsel SDK and older Android devices with DigiCert G5 Certificate

Background

SAP is changing the root certificate of the domains BTP to DigiCert TLS RSA4096 Root G5 CA, see SAP Note 3566727 for more details.

The new certificate is supported by Android 14 and later. For older devices that cannot be upgraded, the certificate has to be installed on the device or embedded in the Android App. This post explains the application embeding approach, for Mobile Applications built using the Kapsel SDK. 

Important: The following approach essentially adds the DigiCert G5 certificate such that the application can validate the SAP Certificate signed by it. As such, great care should be taken to ensure that correct certificate is being added. The DigiCert TLS RSA4096 Root G5 used in the following was downloaded from DigiCert Trusted Root Authority Certificates.

1. Download DigiCert G5 certificate

1. Download the root certificate from https://cacerts.digicert.com/DigiCertTLSRSA4096RootG5.crt mentioned in the above note

2. Rename the file to all lowercase letters.  I have used the name digicert_tls_rsa_4096_root_g5.crt for readability

2. Online Functionality Only

1. In Kapsel Application project root path create the following new subdirectories if they don’t already exist: res/android/raw, res/android/xml.

2. Copy the certificate file to res/android/raw, creating res/android/raw/digicert_tls_rsa_4096_root_g5.crt

3. Create file res/android/xml/kapsel_network_security_config.xml and add a reference to the certificate file. In the following example, the App trusts servers that identify with this certificate in addition to all other certificates in the System store.

<?xml version=’1.0′ encoding=’utf-8′?>
<network-security-config>
<base-config cleartextTrafficPermitted=”true”>
<trust-anchors>
<certificates src=”system” />
<certificates src=”user” />
<certificates src=”@raw/digicert_tls_rsa_4096_root_g5digicert_root_g5_crt” />
</trust-anchors>
</base-config>
</network-security-config>

3. Edit config.xml file and add following two properties under the `<platform name=”android”>` elements:

<resource-file src=”res/android/raw/digicert_root_g5_crt” target=”app/src/main/res/raw/digicert_root_g5_crt” />
<resource-file src=”res/android/xml/kapsel_network_security_config.xml” target=”app/src/main/res/xml/kapsel_network_security_config.xml” />

3. Offline Functionality

In addition to above, the following changes are required for apps utilizing the Offline functionality, providing the certificate as a ‘trusted_certificates’ in ‘streamParams’ property used for creating Offline Store. 

1. Create new subdirectory if it doesn’t already exist: www/certs.

2. Copy the certificate file to www/certs.

3. Code modification

Locate the code where `createOfflineStore` is called and add `extraStreamParameters` as follows. Ensure that the property is not already assigned, which would overwrite these changes. In that case, concatenate this with existing value.  Note that this is based on an App created with the Sample Service feature with two defining requests, please adjust for your application.

 

function openStore() {
console.log(“EventLogging: openStore”);

ensureTrustedRootsFile(function (certFileURL) {
startTime = new Date();
updateStatus2(“store.open called”);
var properties = {
“enableIndividualErrorArchiveDeletion”: true,
“name”: “ESPMOfflineStore”,
“host”: applicationContext.registrationContext.serverHost,
“port”: applicationContext.registrationContext.serverPort,
“https”: applicationContext.registrationContext.https,
“serviceRoot”: “sampleodatav4”,
“definingRequests”: {
“SuppliersDR”: “/Suppliers”,
“CategoriesDR”: “/Categories”
},
“streamParams”: “trusted_certificates=” + certFileURL + “;”
};
if (certFileURL != null) {
properties.streamParams = “trusted_certificates=” + certFileURL + “;”;
}
store = sap.OData.createOfflineStore(properties);
store.onrequesterror = onRequestError; //called for each modification error during flush
var options = {};
store.open(openStoreSuccessCallback, errorCallback, options, progressCallback);
});
}

function ensureTrustedRootsFile(callback) {
// The certificate file needs to be copied from the www directory to the data directory for the following reason:
// The www directory is located inside the app sandbox and cannot be accessed directly by the offline odata module.
// The data directory is a readable/writable area for the app, and the offline odata module can access files in the data directory.
var certFileName = ‘digicert_root_g5_crt’;
var srcPath = cordova.file.applicationDirectory + ‘www/certs/’ + certFileName;
var destDir = cordova.file.dataDirectory;
var destPath = destDir + certFileName;
// Check if the target file already exists
window.resolveLocalFileSystemURL(destPath, function (existingEntry) {
// If the file exists, return its native absolute path
var nativePath = existingEntry.nativeURL;
if (nativePath.startsWith(‘file://’)) {
nativePath = nativePath.substring(7);
}
if (!nativePath.startsWith(‘/’)) {
nativePath = ‘/’ + nativePath;
}
console.log(‘Certificate already exists at: ‘ + nativePath);
callback(nativePath);
}, function () {
// If the file does not exist, perform the copy operation
window.resolveLocalFileSystemURL(srcPath, function (srcEntry) {
window.resolveLocalFileSystemURL(destDir, function (destDirEntry) {
// Copy the certificate file to the data directory
srcEntry.copyTo(destDirEntry, certFileName, function (newFileEntry) {
var nativePath = newFileEntry.nativeURL;
if (nativePath.startsWith(‘file://’)) {
nativePath = nativePath.substring(7);
}
if (!nativePath.startsWith(‘/’)) {
nativePath = ‘/’ + nativePath;
}
console.log(‘Certificate copied to: ‘ + nativePath);
callback(nativePath);
}, function (copyErr) {
alert(‘Failed to copy certificate file: ‘ + JSON.stringify(copyErr));
callback(null);
});
}, function (destErr) {
alert(‘Destination directory not found: ‘ + destDir);
callback(null);
});
}, function (srcErr) {
alert(‘Certificate file not found: ‘ + srcPath + ‘. Please ensure the file is in the correct location.’);
callback(null);
});
});
}

Verification

Launch the Kapsel app and connect to your server.If needed, use Logcat to confirm the TLS handshake succeeds (no CERTIFICATE_UNKNOWN or SSLHandshakeException).Test on the target Android versions you plan to support.

Troubleshooting

Confirm you exported the root certificate: “DigiCert TLS RSA4096 Root G5.”Verify the file is Base64 (.crt/.pem) and not a binary DER.

Conclusion

As explained above, without any changes the mobile app may not be able to connect after the switch to G5 and result in complete outage. Feel free to post any follow-up questions here.

 

​ BackgroundSAP is changing the root certificate of the domains BTP to DigiCert TLS RSA4096 Root G5 CA, see SAP Note 3566727 for more details.The new certificate is supported by Android 14 and later. For older devices that cannot be upgraded, the certificate has to be installed on the device or embedded in the Android App. This post explains the application embeding approach, for Mobile Applications built using the Kapsel SDK. Important: The following approach essentially adds the DigiCert G5 certificate such that the application can validate the SAP Certificate signed by it. As such, great care should be taken to ensure that correct certificate is being added. The DigiCert TLS RSA4096 Root G5 used in the following was downloaded from DigiCert Trusted Root Authority Certificates.1. Download DigiCert G5 certificate1. Download the root certificate from https://cacerts.digicert.com/DigiCertTLSRSA4096RootG5.crt mentioned in the above note2. Rename the file to all lowercase letters.  I have used the name digicert_tls_rsa_4096_root_g5.crt for readability2. Online Functionality Only1. In Kapsel Application project root path create the following new subdirectories if they don’t already exist: res/android/raw, res/android/xml.2. Copy the certificate file to res/android/raw, creating res/android/raw/digicert_tls_rsa_4096_root_g5.crt3. Create file res/android/xml/kapsel_network_security_config.xml and add a reference to the certificate file. In the following example, the App trusts servers that identify with this certificate in addition to all other certificates in the System store.<?xml version=’1.0′ encoding=’utf-8′?>
<network-security-config>
<base-config cleartextTrafficPermitted=”true”>
<trust-anchors>
<certificates src=”system” />
<certificates src=”user” />
<certificates src=”@raw/digicert_tls_rsa_4096_root_g5digicert_root_g5_crt” />
</trust-anchors>
</base-config>
</network-security-config>3. Edit config.xml file and add following two properties under the `<platform name=”android”>` elements: <resource-file src=”res/android/raw/digicert_root_g5_crt” target=”app/src/main/res/raw/digicert_root_g5_crt” />
<resource-file src=”res/android/xml/kapsel_network_security_config.xml” target=”app/src/main/res/xml/kapsel_network_security_config.xml” />3. Offline FunctionalityIn addition to above, the following changes are required for apps utilizing the Offline functionality, providing the certificate as a ‘trusted_certificates’ in ‘streamParams’ property used for creating Offline Store. 1. Create new subdirectory if it doesn’t already exist: www/certs.2. Copy the certificate file to www/certs.3. Code modificationLocate the code where `createOfflineStore` is called and add `extraStreamParameters` as follows. Ensure that the property is not already assigned, which would overwrite these changes. In that case, concatenate this with existing value.  Note that this is based on an App created with the Sample Service feature with two defining requests, please adjust for your application.  function openStore() {
console.log(“EventLogging: openStore”);

ensureTrustedRootsFile(function (certFileURL) {
startTime = new Date();
updateStatus2(“store.open called”);
var properties = {
“enableIndividualErrorArchiveDeletion”: true,
“name”: “ESPMOfflineStore”,
“host”: applicationContext.registrationContext.serverHost,
“port”: applicationContext.registrationContext.serverPort,
“https”: applicationContext.registrationContext.https,
“serviceRoot”: “sampleodatav4”,
“definingRequests”: {
“SuppliersDR”: “/Suppliers”,
“CategoriesDR”: “/Categories”
},
“streamParams”: “trusted_certificates=” + certFileURL + “;”
};
if (certFileURL != null) {
properties.streamParams = “trusted_certificates=” + certFileURL + “;”;
}
store = sap.OData.createOfflineStore(properties);
store.onrequesterror = onRequestError; //called for each modification error during flush
var options = {};
store.open(openStoreSuccessCallback, errorCallback, options, progressCallback);
});
}

function ensureTrustedRootsFile(callback) {
// The certificate file needs to be copied from the www directory to the data directory for the following reason:
// The www directory is located inside the app sandbox and cannot be accessed directly by the offline odata module.
// The data directory is a readable/writable area for the app, and the offline odata module can access files in the data directory.
var certFileName = ‘digicert_root_g5_crt’;
var srcPath = cordova.file.applicationDirectory + ‘www/certs/’ + certFileName;
var destDir = cordova.file.dataDirectory;
var destPath = destDir + certFileName;
// Check if the target file already exists
window.resolveLocalFileSystemURL(destPath, function (existingEntry) {
// If the file exists, return its native absolute path
var nativePath = existingEntry.nativeURL;
if (nativePath.startsWith(‘file://’)) {
nativePath = nativePath.substring(7);
}
if (!nativePath.startsWith(‘/’)) {
nativePath = ‘/’ + nativePath;
}
console.log(‘Certificate already exists at: ‘ + nativePath);
callback(nativePath);
}, function () {
// If the file does not exist, perform the copy operation
window.resolveLocalFileSystemURL(srcPath, function (srcEntry) {
window.resolveLocalFileSystemURL(destDir, function (destDirEntry) {
// Copy the certificate file to the data directory
srcEntry.copyTo(destDirEntry, certFileName, function (newFileEntry) {
var nativePath = newFileEntry.nativeURL;
if (nativePath.startsWith(‘file://’)) {
nativePath = nativePath.substring(7);
}
if (!nativePath.startsWith(‘/’)) {
nativePath = ‘/’ + nativePath;
}
console.log(‘Certificate copied to: ‘ + nativePath);
callback(nativePath);
}, function (copyErr) {
alert(‘Failed to copy certificate file: ‘ + JSON.stringify(copyErr));
callback(null);
});
}, function (destErr) {
alert(‘Destination directory not found: ‘ + destDir);
callback(null);
});
}, function (srcErr) {
alert(‘Certificate file not found: ‘ + srcPath + ‘. Please ensure the file is in the correct location.’);
callback(null);
});
});
}VerificationLaunch the Kapsel app and connect to your server.If needed, use Logcat to confirm the TLS handshake succeeds (no CERTIFICATE_UNKNOWN or SSLHandshakeException).Test on the target Android versions you plan to support.TroubleshootingConfirm you exported the root certificate: “DigiCert TLS RSA4096 Root G5.”Verify the file is Base64 (.crt/.pem) and not a binary DER.ConclusionAs explained above, without any changes the mobile app may not be able to connect after the switch to G5 and result in complete outage. Feel free to post any follow-up questions here.   Read More Technology Blog Posts by SAP articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author