Enhanced jailbreak-detection using app server
Necessity of Enhanced Jailbreak Detection Method
Section titled “Necessity of Enhanced Jailbreak Detection Method”One of the main functions within AppSealing SDK is detecting the environment of the jailbroken device and forcibly closes the app. However, there is a possibility that these detection functions will be bypassed by more sophisticated attack methods. This is because, due to the characteristics of the iOS operating system, the code of the loaded dynamic library (dylib) is executed first when the app is launched. An attacker may distribute the code to patch a specific area of the executable file in such a dynamic library.
If this code patch occurs before AppSealing’s detection logic is executed, the code that terminates the app has been removed, so even if a jailbreak is detected, the app will remain running.
Of course, not everyone can perform this type of attack easily, but since this type of attack has been confirmed by a group of hackers with specialized hacking knowledge, AppSealing provides an additional jailbreak detection method to overcome such an attack situation.
Since the characteristic of this attack method is to change the code of the running app in advance, no matter how strong detection logic is added to the AppSealing library itself, the situation in which the code is patched by the dynamic library is unavoidable. Therefore, the newly provided jailbreak detection function does not detect in the app, but in a way that rejects all services and actions, such as log-in in or accepting API calls, in the case of a terminal suspected of being jailbroken in the server linked to the app.
The basic method is to obtain server credentials from the app through the AppSealing interface, add them to the existing authentication parameters, and send them to the server.
This method cannot be applied for a client-only app that does not work with the server.
The following sections describe, with example code, how to obtain and validate server credentials.
iOS App Code
Section titled “iOS App Code”Additional process of your app needs verify the server credentials is to call a function in the AppSealing SDK to get the server credential string and send it to the server along with the existing authentication parameters.
Most apps that work with the server will go through a user authentication or login process, and in this process, the account information entered by the user will be transmitted to the server. You can add the server credential string to the parameters you send to the server.
The server credential string is obtained by calling the Unity_GetEncryptedCredential function as shown below. The buffer to receive the string must be declared with more than 290 bytes.
Sample credential acquire code for Unity3D project
StringBuilder encryptedCredential = new StringBuilder(290);
result = Unity_GetEncryptedCredential( encryptedCredential ); if ( result == 0 ) { string strEncryptedCredential = encryptedCredential.ToString(); UnityEngine.Debug.Log( "AppSealing Credential : " + strEncryptedCredential ); // LoginToServer( userID, password, strEncryptedCredential ); }
After you get the credential string, send it as an additional parameter when your app communicates with the server for the login or authentication process, and let the server validate it.
If your server fails to validate credential, you should also force the login to fail and the app to not proceed further. However, since code such as checking the login result and closing the app is likely to be tampered by an attacker, the best practice is configuring your server to deny service or response for any requests from that client after the server fails credential validation.
This will be discussed again in the next section.
Verification at app server
Section titled “Verification at app server”The credential data (hex string) returned from the interface call to the AppSealing module is only valid when the security logic inside AppSealing is normally performed and no dangerous situation is detected in the device.
If code patch attack is made through the dynamic library or the security logic is bypassed by other methods, valid credential data will not be generated, so the server should verify this value and blocks the attack situation of the device.
The app server must check whether the credential value sent by the client (app) is correct, and if it is not correct, it must deny authentication (login) and then deny any services (API call) requested by that client.
[Preparation]
Section titled “[Preparation]”To verify credential data on the server, you need an AES Key and IV to decrypt the data sent from the client, and the original credential data to compare and verify.
All of these values can be acquired through the “Check Credential” button of the project in the ADC. Just copy the Hex string shown here and paste it into the example code and use it. First, connect to ADC as shown in the screen below and click the “Check Credential” button in the project box.
If you click the button, the following window is displayed, where you can check the Credential value and IV and key to be used for decryption. You can use the copy button to the left of the string to copy this value as it is, paste it into the server-side verification code and use it.
If you click the button, the following window is displayed, where you can check the Credential value and IV and AES key to be used for decryption. You can use the copy button to the left of the string to copy this value as it is, paste it into the server-side verification code and use it.
[In case your server code is using Node.js/Javascript]
Section titled “[In case your server code is using Node.js/Javascript]”Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.js).
** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.
var crypto = require('crypto');
function verifyAppSealingCredential( credential ){ // Need to Change : Get From ADC (via 'Check Credential') ----------------------------------------- const ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE";
const AES_IV = "055772B7434A4174749AFE09B1413472"; const AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F"; //-------------------------------------------------------------------------------------------------
// convert credential from hex string to byte array let decrypted_UTC = 0, decrpyted_buffer, aes_key2;
// decrypt UTC try { const decipher = crypto.createDecipheriv( 'aes-128-ctr', Buffer.from( AES_KEY, 'hex' ), Buffer.from( AES_IV, 'hex' )); decrpyted_buffer = Buffer.concat( [decipher.update( credential.substr( 0, 32 ), 'hex' ), decipher.final()] ); decrypted_UTC = decrpyted_buffer.slice( 0, 8 ).readUInt32LE(); } catch( error ) { throw error; }
// verfity UTC with current time (+/-) 10sec const current_UTC = parseInt( Date.now() / 1000 ); // get current UTC in seconds if ( Math.abs( current_UTC - decrypted_UTC ) > 10 ) { console.log( "Invalid UTC value has sent, deny login & all services for this client... " + Math.abs( current_UTC - decrypted_UTC ) ); return false; } console.log( "** UTC verified : " + decrypted_UTC + " (current = " + current_UTC + ", diff = " + Math.abs( current_UTC - decrypted_UTC ) + ")" );
// get AES KEY2 aes_key2 = Buffer.concat( [new Uint8Array( decrpyted_buffer.slice( 0, 8 )), new Uint8Array( Buffer.from( ORG_CREDENTIAL.substring( 52, 52 + 16 ), 'hex' ))] ); for( let i = 0; i < 16; i++ ) aes_key2[i] ^= Buffer.from( AES_IV.substring( i * 2, i * 2 + 2 ), 'hex' ).readUInt8();
// decrypt credential let decrypted_credential = []; try { const decipher = crypto.createDecipheriv( 'aes-128-ctr', aes_key2, Buffer.from( AES_IV, 'hex' )); decrypted_credential = Buffer.concat( [decipher.update( credential.substr( 32, 256 ), 'hex' ), decipher.final()] ); } catch( error ) { throw error; }
// verfity credential with CREDENTIAL(ADC) // return if fail if ( ORG_CREDENTIAL.toLowerCase() != decrypted_credential.toString( 'hex' ).toLowerCase() ) { console.log( "Invalid credential value has sent, deny login & all services for this client..." ); return false; }
console.log( "** Credential verified : PASS" ); return true;}
[In case your server code is using Java]
Section titled “[In case your server code is using Java]”Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.java).
** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.
import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.util.Arrays;
import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import javax.xml.bind.DatatypeConverter;
public class AppSealingCredential{ private static boolean verifyAppSealingCredential( final String credential ) { // Need to Change : Get From ADC (via 'Check Credential') ----------------------------------------- final String ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE";
final String AES_IV = "055772B7434A4174749AFE09B1413472"; final String AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F"; //-------------------------------------------------------------------------------------------------
long decrypted_UTC = 0; byte[] decryptedUTC = new byte[8];
// decrypt UTC try { SecretKeySpec key = new SecretKeySpec( DatatypeConverter.parseHexBinary( AES_KEY ), "AES" ); IvParameterSpec ivSpec = new IvParameterSpec( DatatypeConverter.parseHexBinary( AES_IV ));
Cipher cipher = Cipher.getInstance( "AES/CTR/NoPadding" ); cipher.init( Cipher.DECRYPT_MODE, key, ivSpec );
byte[] encryptedUTC = DatatypeConverter.parseHexBinary( credential.substring( 0, 32 )); System.arraycopy( cipher.doFinal( encryptedUTC ), 0, decryptedUTC, 0, 8 ); System.out.println( DatatypeConverter.printHexBinary( decryptedUTC ));
decrypted_UTC = ByteBuffer.wrap( decryptedUTC ).order( ByteOrder.LITTLE_ENDIAN ).getInt() & 0xFFFFFFFFFL; } catch( Exception e ) { System.out.println( "[Error] " + e.getLocalizedMessage() ); }
// verfity UTC with current time (+/-) 10sec long current_UTC = System.currentTimeMillis() / 1000; // get current UTC in seconds
if ( Math.abs( current_UTC - decrypted_UTC ) > 10 ) { System.out.println( "Invalid UTC value has sent, deny login & all services for this client..." ); return false; } System.out.println( "** UTC verified : " + decrypted_UTC + " (current = " + current_UTC + ", diff = " + Math.abs( current_UTC - decrypted_UTC ) + ")" );
// get AES KEY2 byte[] aes_key2 = Arrays.copyOf( decryptedUTC, 16 ); byte[] xor = DatatypeConverter.parseHexBinary( ORG_CREDENTIAL.substring( 52, 52 + 16 )); System.arraycopy( xor, 0, aes_key2, decryptedUTC.length, xor.length );
for( int i = 0; i < 16; i++ ) aes_key2[i] ^= ( byte )DatatypeConverter.parseHexBinary( AES_IV.substring( i * 2, i * 2 + 2 ))[0]; System.out.println( DatatypeConverter.printHexBinary( aes_key2 ));
// decrypt credential byte[] decrypted_credential = null; try { SecretKeySpec key = new SecretKeySpec( aes_key2, "AES" ); IvParameterSpec ivSpec = new IvParameterSpec( DatatypeConverter.parseHexBinary( AES_IV ));
Cipher cipher = Cipher.getInstance( "AES/CTR/NoPadding" ); cipher.init( Cipher.DECRYPT_MODE, key, ivSpec );
System.out.println( "########### " + credential.substring( 32, 32 + 256 ) ); byte[] encrypted_credential = DatatypeConverter.parseHexBinary( credential.substring( 32, 32 + 256 )); decrypted_credential = cipher.doFinal( encrypted_credential ); } catch( Exception e ) { System.out.println( "[Error] " + e.getLocalizedMessage() ); }
// verfity credential with CREDENTIAL(ADC) // return if fail if ( !ORG_CREDENTIAL.equalsIgnoreCase( DatatypeConverter.printHexBinary( decrypted_credential ))) { System.out.println( "Invalid credential value has sent, deny login & all services for this client..." ); return false; }
System.out.println( "** Credential verified : PASS" ); return true; }}
[In case your server code is using http://ASP.NET / C#]
Section titled “[In case your server code is using http://ASP.NET / C#]”Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.cs, because C# does not support AES CTR mode, you must include the AesCtrTransform function for CTR processing as shown in the code below.)
** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.
using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Security.Cryptography;using System.Text;using System.Threading.Tasks;
namespace AppSealingSDK{ class AppSealingCredential { public static void AesCtrTransform( byte[] key, byte[] salt, Stream inputStream, Stream outputStream ) { SymmetricAlgorithm aes = new AesManaged { Mode = CipherMode.ECB, Padding = PaddingMode.None };
int blockSize = aes.BlockSize / 8; if ( salt.Length != blockSize ) throw new ArgumentException( "Salt size must be same as block size " + $"(actual: {salt.Length}, expected: {blockSize})" );
byte[] counter = ( byte[] )salt.Clone();
Queue<byte> xorMask = new Queue<byte>(); var zeroIv = new byte[blockSize]; ICryptoTransform counterEncryptor = aes.CreateEncryptor( key, zeroIv );
int b; while(( b = inputStream.ReadByte()) != -1 ) { if ( xorMask.Count == 0 ) { var counterModeBlock = new byte[blockSize]; counterEncryptor.TransformBlock( counter, 0, counter.Length, counterModeBlock, 0 );
for( var i2 = counter.Length - 1; i2 >= 0; i2-- ) { if ( ++counter[i2] != 0 ) break; } foreach( var b2 in counterModeBlock ) xorMask.Enqueue( b2 ); } var mask = xorMask.Dequeue(); outputStream.WriteByte(( byte )((( byte )b ) ^ mask )); } } public static byte[] StringToByteArray( String hex ) { return Enumerable.Range( 0, hex.Length / 2 ).Select( x => Convert.ToByte( hex.Substring( x * 2, 2 ), 16 )).ToArray(); } public static bool VerifyAppSealingCredential( String credential ) { // Need to Change : Get From ADC (via 'Check Credential') ----------------------------------------- const String ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE";
const String AES_IV = "055772B7434A4174749AFE09B1413472"; const String AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F"; //-------------------------------------------------------------------------------------------------
long decrypted_UTC = 0; byte[] decrypted_buffer = new byte[8];
// decrypt UTC try { byte[] encrypted_UTC = StringToByteArray( credential.Substring( 0, 32 )); byte[] result = new byte[16]; AesCtrTransform( StringToByteArray( AES_KEY ), StringToByteArray( AES_IV ), new MemoryStream( encrypted_UTC ), new MemoryStream( result )); Array.Copy( result, 0, decrypted_buffer, 0, 8 ); decrypted_UTC = BitConverter.ToInt64( decrypted_buffer, 0 ); System.Console.WriteLine( "** UTC = " + decrypted_UTC ); } catch(Exception e ) { System.Console.WriteLine( "[Error] " + e.Message ); }
// verfity UTC with current time (+/-) 10sec long current_UTC = DateTimeOffset.Now.ToUnixTimeMilliseconds() / 1000; // get current UTC in seconds if ( Math.Abs( current_UTC - decrypted_UTC ) > 10 ) { System.Console.WriteLine( "Invalid UTC value has sent, deny login & all services for this client..." ); return false; } System.Console.WriteLine( "** UTC verified : " + decrypted_UTC + " (current = " + current_UTC + ", diff = " + Math.Abs(current_UTC - decrypted_UTC ) + ")" );
// get AES KEY2 byte[] aes_key2 = new byte[16]; Array.Copy( decrypted_buffer, 0, aes_key2, 0, 8 ); byte[] xor = StringToByteArray( ORG_CREDENTIAL.Substring( 52, 16 )); Array.Copy( xor, 0, aes_key2, decrypted_buffer.Length, xor.Length );
for(int i = 0; i< 16; i++ ) aes_key2[i] ^= ( byte )StringToByteArray( AES_IV.Substring( i* 2, 2 ))[0];
// decrypt credential byte[] decrypted_credential = null; try { byte[] encrypted_credential = StringToByteArray( credential.Substring( 32, 256 )); decrypted_credential = new byte[encrypted_credential.Length]; AesCtrTransform( aes_key2, StringToByteArray( AES_IV ), new MemoryStream( encrypted_credential ), new MemoryStream( decrypted_credential )); } catch(Exception e ) { System.Console.WriteLine( "[Error] " + e.Message ); }
// verfity credential with CREDENTIAL(ADC) // return if fail if ( !ORG_CREDENTIAL.Equals( BitConverter.ToString( decrypted_credential ).Replace( "-", "" ), StringComparison.InvariantCultureIgnoreCase )) { System.Console.WriteLine( "Invalid credential value has sent, deny login & all services for this client..." ); return false; }
System.Console.WriteLine( "** Credential verified : PASS" ); return true; }}
[In case your server code is using python]
Section titled “[In case your server code is using python]”Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.py)
** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.
from Crypto.Cipher import AESfrom Crypto.Util import Counterimport time
#=============================================================def verifyAppSealingCredential( credential ):
# Need to Change : Get From ADC (via 'Check Credential') --------------------------------------------- ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE" AES_IV = "055772B7434A4174749AFE09B1413472" AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F" #-----------------------------------------------------------------------------------------------------
# decrypt UTC try: counter = Counter.new( 128, initial_value = int.from_bytes( bytes.fromhex( AES_IV ), "big" )) cipher = AES.new( bytes.fromhex( AES_KEY ), AES.MODE_CTR, counter = counter ) descrypted_buffer = cipher.decrypt( bytes.fromhex( credential[:32] ))
decrypted_UTC = int.from_bytes( descrypted_buffer[:8], "little" ) except Exception as e: print( '[Error] ', e ) return 0;
# verfity UTC with current time (+/-) 10sec current_UTC = round( time.time() * 1000 ); # get current UTC in seconds
if abs( current_UTC - decrypted_UTC ) > 10: print( "Invalid UTC value has sent, deny login & all services for this client..." ) #return 0
print( "** UTC verified : ", decrypted_UTC, " (current = ", current_UTC, ", diff = ", abs( current_UTC - decrypted_UTC ), ")" )
# get AES KEY2 aes_key2 = bytearray( 16 ) aes_key2[0:8] = descrypted_buffer[0:8] aes_key2[8:8] = bytes.fromhex( ORG_CREDENTIAL[52:68] ) print( aes_key2[:16].hex() )
for i in range( 0, 16 ): aes_key2[i] ^= bytes.fromhex( AES_IV[i * 2:i * 2 + 2] )[0] print( aes_key2[:16].hex() )
# decrypt credential try: counter2 = Counter.new( 128, initial_value = int.from_bytes( bytes.fromhex( AES_IV ), "big" )) cipher2 = AES.new( aes_key2[:16], AES.MODE_CTR, counter = counter2 ) print( credential[32:288] ) decrypted_credential = cipher2.decrypt( bytes.fromhex( credential[32:288] )) except Exception as e: print( '[Error] ', e ) return 0;
# verfity credential with CREDENTIAL(ADC) # return if fail if ORG_CREDENTIAL.casefold() != decrypted_credential.hex().casefold(): print( "Invalid credential value has sent, deny login & all services for this client..." ) return 0
print( "** Credential verified : PASS" ) return 1
[In case your server code is using ruby script]
Section titled “[In case your server code is using ruby script]”Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.rb)
** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.
require 'securerandom'require 'net/https'require 'json'
# Need to Change : Get From ADC (via 'Check Credential') ---------------------------------------------ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE"AES_IV = "055772B7434A4174749AFE09B1413472"AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F"#-----------------------------------------------------------------------------------------------------
def verifyAppSealingCredential( credential )
# decrypt UTC begin aes = OpenSSL::Cipher::AES.new( "128-CTR" ) aes.decrypt aes.key = [AES_KEY].pack( 'H*' ) aes.iv = [AES_IV].pack( 'H*' ) decrypted_buffer = aes.update( [credential[0..31]].pack( 'H*' )) + aes.final
decrypted_UTC = decrypted_buffer.slice( 0, 8 ).unpack( 'V' ).first rescue => e puts '[Error] ' + e.to_s return 0 end
# verfity UTC with current time (+/-) 10sec current_UTC = Time.now.strftime( '%s%L' ).to_i; # get current UTC in seconds
if ( current_UTC - decrypted_UTC ).abs > 10 puts "Invalid UTC value has sent, deny login & all services for this client..." return 0 end
puts "** UTC verified : " + decrypted_UTC.to_s + " (current = " + current_UTC.to_s + ", diff = " + ( current_UTC - decrypted_UTC ).abs.to_s + ")"
# get AES KEY2 aes_key2 = decrypted_buffer.bytes.slice( 0, 8 ) + [ORG_CREDENTIAL[52..67]].pack( 'H*' ).bytes for i in 0..15 do aes_key2[i] ^= [AES_IV[i * 2..i * 2 + 1]].pack( 'H*' ).bytes[0] end
# decrypt credential begin aes = OpenSSL::Cipher::AES.new( "128-CTR" ) aes.decrypt aes.key = aes_key2.pack( 'C*' ) aes.iv = [AES_IV].pack( 'H*' ) decrypted_credential = aes.update( [credential[32..287]].pack( 'H*' )) + aes.final rescue => e puts '[Error] ' + e.to_s return 0 end
# verfity credential with CREDENTIAL(ADC) # return if fail if ORG_CREDENTIAL.casecmp( decrypted_credential.unpack( 'H*' ).first ) != 0 print( "Invalid credential value has sent, deny login & all services for this client..." ) return 0 end
print( "** Credential verified : PASS" ) return 1
end
[In case your server code is using C++]
Section titled “[In case your server code is using C++]”Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.cpp with aes.hpp/aes.cpp)
** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.
#include <iostream>#include <vector>#include <time.h>#include "aes.cpp"
typedef std::vector<unsigned char> bytes;bytes HexToBytes( const std::string& hex ){ std::vector<unsigned char> bytes; for( unsigned int i = 0; i < hex.length(); i += 2 ) { std::string byteString = hex.substr( i, 2 ); unsigned char byte = ( unsigned char )strtol( byteString.c_str(), NULL, 16 ); bytes.push_back( byte ); } return bytes;}
static bool verifyAppSealingCredential( const char* credential ){ // Need to Change : Get From ADC (via 'Check Credential') --------------------------------------------- const char* ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE"; const char* AES_IV = "055772B7434A4174749AFE09B1413472"; const char* AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F"; //-----------------------------------------------------------------------------------------------------
unsigned char decrypted_buffer[16], decrypted_credential[128];
// decrypt UTC struct AES_ctx ctx; try { memcpy( decrypted_buffer, HexToBytes( std::string( credential ).substr( 0, 32 )).data(), 16 );
AES_init_ctx_iv( &ctx, HexToBytes( AES_KEY ).data(), HexToBytes( AES_IV ).data() ); AES_CTR_xcrypt_buffer( &ctx, decrypted_buffer, 16 );
long decrypted_UTC = *(( long* )decrypted_buffer );
std::cout << "** UTC = " << decrypted_UTC << "\n";
// verfity UTC with current time (+/-) 10sec time_t current_UTC = time( 0 ); current_UTC = 1664439768;//REMOVE if ( abs( current_UTC - decrypted_UTC ) > 10 ) { std::cout << "Invalid UTC value has sent, deny login & all services for this client...\n"; return false; } std::cout << "** UTC verified : " << decrypted_UTC << " (current = " << current_UTC << ", diff = " << abs(current_UTC - decrypted_UTC ) << ")\n";
// get AES KEY2 unsigned char aes_key2[16]; memcpy( aes_key2, decrypted_buffer, 8 ); bytes XOR = HexToBytes( std::string( ORG_CREDENTIAL ).substr( 52, 16 )); memcpy( aes_key2 + 8, XOR.data(), XOR.size() );
for( int i = 0; i < 16; i++ ) aes_key2[i] ^= HexToBytes( std::string( AES_IV ).substr( i * 2, 2 )).data()[0];
// decrypt credential memcpy( decrypted_credential, HexToBytes( std::string( credential ).substr( 32, 256 )).data(), 128 );
AES_init_ctx_iv( &ctx, aes_key2, HexToBytes( AES_IV ).data() ); AES_CTR_xcrypt_buffer( &ctx, decrypted_credential, 128 ); } catch( const std::out_of_range& e ) { std::cout << "pos exceeds string size\n"; }
// verfity credential with CREDENTIAL(ADC) // return if fail if ( memcmp( HexToBytes( ORG_CREDENTIAL ).data(), decrypted_credential, 128 ) != 0 ) { std::cout << "Invalid credential value has sent, deny login & all services for this client..."; return false; } std::cout << "** Credential verified : PASS";}