Skip to main content

Embed Code

The source code is written in Java for native Android development.


Step 1 : Implement ECC Libraries

Implement ECC Libraries in build.gradle from .../android/app.

android {
...
defaultConfig {
...
multiDexEnabled true
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE.md'
exclude 'META-INF/NOTICE.md'
}
...
}

dependencies {
...
// Crypto
implementation 'com.nimbusds:nimbus-jose-jwt:8.9'
implementation 'org.bouncycastle:bcprov-jdk15on:1.64'
...
}

Step 2 : Private and Public Keys

Generate both the public and private keys.

Step 2.1 : Generate Keys

Please refer to Appendix 4 to generate EC Keypair.


Step 2.2 : Initialize Keys

Initialize the keys that will be used for the encryption and decryption process.

TestPos(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
try {
thirdAppPrivateKey = loadECPrivateKey(reactContext.getAssets().open("ec_private_key_pkcs8.pem"));
thirdAppPublicKey = loadECPublicKey(reactContext.getAssets().open("ec_public_key.pem"));
yippiePosPublicKey = loadECPublicKey(reactContext.getAssets().open("yippiepos_public_key.key"));
String refNum = "1.0";
secretKey = generateECDHSecret(thirdAppPrivateKey, yippiePosPublicKey, refNum);
} catch (IOException ex) {
ex.printStackTrace();
}
}

Step 3 : Load the Initialized Keys

Load the initialized keys from the key file.

For Private Key
public static ECPrivateKey loadECPrivateKey(InputStream certFile) {
ECPrivateKey ecPrivateKey = null;
PrivateKey privateKey = null;
try {
byte[] encodedPrivateKey = new byte[certFile.available()];
certFile.read(encodedPrivateKey);
certFile.close();
try {
String certFileContent = new String(encodedPrivateKey)
.replace("-----BEGIN CERTIFICATE-----", "")
.replace("-----END CERTIFICATE-----","")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "");
encodedPrivateKey = android.util.Base64.decode(certFileContent, android.util.Base64.DEFAULT);
} catch (Exception ex) {
ex.printStackTrace();
}
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
privateKey = keyFactory.generatePrivate(privateKeySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) {
e.printStackTrace();
}
if (privateKey instanceof ECPrivateKey){
ecPrivateKey = (ECPrivateKey) privateKey;
}
return ecPrivateKey;
}

Step 4 : Declare Input

Declare all input parameters in the request and create a JSON string.

Enquiry Request Parameters
public void receiveMessage(ReadableMap data) {
String msgVer = data.getString("msgVer");
String callerDevice = data.getString("callerDevice");
String callerDeviceVer = data.getString("callerDeviceVer");
String txnID = data.getString("txnID");
String email = data.getString("email");
String password = data.getString("password");

String jsonMessage = "{'MsgVer' : '" + msgVer +
"', 'CallerDeviceType' : '" + callerDevice +
"', 'CallerDeviceVer' : '" + callerDeviceVer +
"', 'TxnID' : '" + txnID +
"', 'Email' : '" + email +
"', 'Pwd' : '" + password + "'}";

ECKey publicKeyASJWK = new ECKey.Builder(Curve.P_256, thirdAppPublicKey)
.keyID(UUID.randomUUID().toString())
.build();

String jsonMessageEncrypted = encryptWithFormatJWE(txnID,
publicKeyASJWK.toJSONString(), secretKey, JWEAlgorithm.DIR, jsonMessage);

startActivityAIDL(REQ_ENQUIRY, jsonMessageEncrypted);
}

Step 5 : Encrypt String

After obtaining the jsonMessage as a string, encrypt it using the encryptWithFormatJWE function.

public static String encryptWithFormatJWE(String transactionID, String sdkPublicKeyAsJWK, SecretKey secretKey, JWEAlgorithm jweAlgorithm, String message) {
String encryptedMessage = "";

try {
//header
JWEHeader.Builder builder = new JWEHeader.Builder(jweAlgorithm, EncryptionMethod.A128CBC_HS256);
if (!TextUtils.isEmpty(transactionID)) {
builder.keyID(transactionID);
}
if (!TextUtils.isEmpty(sdkPublicKeyAsJWK)) {
builder.ephemeralPublicKey(ECKey.parse(sdkPublicKeyAsJWK));
}
JWEHeader header = builder.build();

//encrypt
if(jweAlgorithm == JWEAlgorithm.DIR) {
Provider bouncyCastleProvider = BouncyCastleProviderSingleton.getInstance();
Security.addProvider((bouncyCastleProvider));
JWEObject jweObject = new JWEObject(header, new Payload(message));
jweObject.encrypt(new DirectEncrypter(secretKey));
encryptedMessage = jweObject.serialize();
} else if (jweAlgorithm == JWEAlgorithm.ECDH_ES) {
// build JWE structure
JWECryptoParts jweParts = ContentCryptoProvider.encrypt(header, message.getBytes(), secretKey, null, new JWEJCAContext());

// JWE Protected Header
StringBuilder stringBuilder = new StringBuilder(header.toBase64URL().toString());
stringBuilder.append('.');

// Encrypted Key
if (jweParts.getEncryptedKey() != null) {
stringBuilder.append(jweParts.getEncryptedKey().toString());
}
stringBuilder.append('.');

// Initialization Vector
if (jweParts.getInitializationVector() != null) {
stringBuilder.append(jweParts.getInitializationVector().toString());
}
stringBuilder.append('.');

// Ciphertext
stringBuilder.append(jweParts.getCipherText().toString());
stringBuilder.append('.');

// Authentication Tag
if (jweParts.getAuthenticationTag() != null) {
stringBuilder.append(jweParts.getAuthenticationTag().toString());
}
encryptedMessage = stringBuilder.toString();
} else {
Log.d("Tag", "Unsupported other algorithms");
}
} catch (Exception ex) {
ex.printStackTrace();
}
return encryptedMessage;
}

Step 6 : Perform App-to-App Request

Bind to the AIDL service and perform the request.

For Android 11 and above, add queries tag with package android:name="com.finexuscards.yippiepos.sandbox" above the application tag in AndroidManifest.xml.

AIDL Request

private IA2AAidlInterface a2aService;

private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
a2aService = IA2AAidlInterface.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
a2aService = null;
}
};

private void bindA2AService() {
Intent intent = new Intent();

intent.setAction("com.finexuscards.kayaakupos.BIND_SERVICE");
intent.setPackage("com.finexuscards.yippiepos.xxx");

bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

}

private String startActivityAIDL(int messageType, String encryptedMessage) {
mMessageType = messageType;

String action = null;
switch (mMessageType) {
case REQ_VOID:// REQ_VOID = 1
action = ACTION_VOID;// ACTION_VOID = "com.finexuscards.yippiepos.xxx.VOID"
break;
case REQ_COMPLETION:// REQ_COMPLETION = 2
action = ACTION_COMPLETION;// ACTION_COMPLETION = "com.finexuscards.yippiepos.xxx.SALESCOMPLETION"
break;
case REQ_REFUND:// REQ_REFUND = 3
action = ACTION_REFUND;// ACTION_REFUND = "com.finexuscards.yippiepos.xxx.REFUND"
break;
case REQ_ENQUIRY:// REQ_ENQUIRY = 4
action = ACTION_ENQUIRY;// ACTION_ENQUIRY = "com.finexuscards.yippiepos.ENQUIRY"
break;
}

String encryptedResponse = a2aService.request(action, encryptedMessage);
}

Step 7 : Decrypt Response Message

Decrypt the response message retrieved from the Kayaaku POS application using the decryptWithFormatJWE function.

public static String decryptWithFormatJWE(SecretKey secretKey, String message) {
String decryptedMessage = "";
try {
// parse message to Object
JWEObject jweObject = JWEObject.parse(message);

// get header
JWEAlgorithm algorithm = jweObject.getHeader().getAlgorithm();
EncryptionMethod encryptionMethod = jweObject.getHeader().getEncryptionMethod();

// decrypt
if (algorithm.equals(JWEAlgorithm.DIR)) {
if (encryptionMethod == EncryptionMethod.A128CBC_HS256 || encryptionMethod == EncryptionMethod.A128GCM) {
Provider bouncyCastleProvider = BouncyCastleProviderSingleton.getInstance();
Security.addProvider(bouncyCastleProvider);

// start decrypting
jweObject.decrypt(new DirectDecrypter(secretKey));
decryptedMessage = jweObject.getPayload().toString();
} else {
Log.d("Tag", "Unsupported other encryption method:" + encryptionMethod);
}
} else {
Log.d("Tag", "Unsupported other algorithms");
}
} catch (Exception ex) {
Log.e("Tag", "decryptedMessage Failed:" + ex.getMessage());
ex.printStackTrace();
}
return decryptedMessage;
}