Skip to main content

Embed Code

note

The source codes are using Android native with Java language.


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 : Declare Input

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

Sales Request Parameters
public void receiveMessage(ReadableMap data) {
String msgVer = data.getString("msgVer");
String pmtType = data.getString("pmtType");
String callerDevice = data.getString("callerDevice");
String callerDeviceVer = data.getString("callerDeviceVer");
String txnID = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
String txnDateTime = data.getString("txnDateTime");
String email = data.getString("email");
String password = data.getString("password");
String crcyCde = data.getString("crcyCde");
String amt = data.getString("amt");
String isOptInPrintReceipt = data.getString("isOptInPrintReceipt");
String isOptInSendEReceipt = data.getString("isOptInSendEReceipt");
String isOptInEReceipt = data.getString("isOptInEReceipt");
String isOptInPmtAckmnt = data.getString("isOptInPmtAckmnt");
String desc = data.getString("desc");

String jsonMessage = "{'TxnTyp' : '" + TestPos.TransactionType.STX +
"', 'MsgVer' : '" + msgVer +
"', 'PmtType' : '" + pmtType +
"', 'CallerDeviceType' : '" + callerDevice +
"', 'CallerDeviceVer' : '" + callerDeviceVer +
"', 'TxnID' : '" + txnID +
"', 'LocalTxnDTTime' : '" + txnDateTime +
"', 'Email' : '" + email +
"', 'Pwd' : '" + password +
"', 'AmtTxn' : '" + amt +
"', 'CrcyTxn' : '" + crcyCde +
"', 'OptInPrintReceipt' : '" + isOptInPrintReceipt +
"', 'OptInSendEReceipt' : '" + isOptInSendEReceipt +
"', 'OptInEReceipt' : '" + isOptInEReceipt +
"', 'OptInPmtAckmnt' : '" + isOptInPmtAckmnt +
"', 'Description' : '" + desc + "'}";

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

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

startActivity(REQ_SALE, jsonMessageEncrypted);
}

Step 3 : Encrypt Request Message

Encrypt the request message that will be sent to Kayaaku POS application using encryptWithFormatJWE.

In UtilCrypto.java
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 4 : Perform App-to-app calling using INTENT

info

To use INTENT, both the Kayaaku POS app and merchant's POS app must be installed in the same device.

Use startActivityForResult() as per the sample code below.

INTENT
// start intent activity
private void startActivity (int messageType, String encryptedMessage) {
if (messageType == REQ_SALE) {
Intent intent = new Intent(ACTION_SALE); // ACTION_SALE = "com.finexuscards.yippiepos.xxx.SALES"
intent.putExtra("Data", encryptedMessage);
startActivityForResult(intent, REQ_SALE); // REQ_SALE = 0
} else if (messageType == REQ_VOID) {
Intent intent = new Intent(ACTION_VOID); // ACTION_VOID = "com.finexuscards.yippiepos.xxx.VOID"
intent.putExtra("Data", encryptedMessage);
startActivityForResult(intent, REQ_VOID); // REQ_VOID = 1
} else if (messageType == REQ_REFUND) {
Intent intent = new Intent(ACTION_REFUND); // ACTION_VOID = "com.finexuscards.yippiepos.xxx.REFUND"
intent.putExtra("Data", encryptedMessage);
startActivityForResult(intent, REQ_REFUND); // REQ_REFUND = 2
}
}

// get response from onActivityResult()
protected void onActivityResult(int requestCode,int resultCode,Intent data){
super.onActivityResult(requestCode,resultCode,data);
String encryptedResponse = data.getStringExtra("Data");
}

Step 5 : Private and Public Keys

Step 5.1 : Generate Keys

Generate the public and private keys.

import java.io.FileOutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;

public void generateECCKeys(String outputPath) {
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
pg.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
KeyPair keyPair = kpg.generateKeyPair();
saveKeyPair(outputPath, keyPair);
} catch (Exception ex) {
ex.printStackTrace();
}
}

public void saveKeyPair(String outputPath, KeyPair keyPair) {
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
try {

StringBuilder content = new StringBuilder();
FileOutputStream fos = new FileOutputStream(outputPath + "./public.key");
content.append(Base64.getEncoder().encodeToString(publicKey.getEncoded()));
content.insert(0, "-----BEGIN PUBLIC KEY-----\n");
content.insert(content.length(), "\n-----END PUBLIC KEY-----");
fos.write(content.toString().getBytes());
fos.close();
content.setLength(0);
fos = new FileOutputStream(outputPath + "./private.key");
content.append(Base64.getEncoder().encodeToString(privateKey.getEncoded()));
content.insert(0, "-----BEGIN PRIVATE KEY-----\n");
content.insert(content.length(), "\n-----END PRIVATE KEY-----");
fos.write(content.toString().getBytes());
fos.close();

} catch (Exception ex) {
ex.printStackTrace();
}
}


Step 5.2 : Initialize Keys

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

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

BA = BluetoothAdapter.getDefaultAdapter();
}

Step 6 : Create Class

Create a new JAVA class, UtilCrypto for the encryption and decryption functions.


Step 7 : Load the Initialized Keys

Load the initialized Keys from the key file.

For Private Key in UtilCrypto.java
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 8 : Decrypt Response Message

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

In UtilCrypto.java
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;
}