applet not installing when using javacardx.framework.math.BigNumber - applet

I've encountered a problem while declaring a BigNumber datatype in my javacard applet. The applet loads properly into the simulator if i just comment the declaration. The problem to be precise is while loading the import.cap file
(jcshell: Error code: 6a80 (Wrong data))
java card kit 2.2.2 using
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacardx.framework.math.BigNumber;
public class LargeBal extends Applet {
// CLA byte
public static final byte BANK_CLA = (byte) 0x80;
// INS byte
public static final byte INS_GET_BALANCE = 0X02;
public static final byte INS_CREDIT = 0X04;
public static final byte INS_DEBIT = 0X06;
/**
* SW bytes for Arithmetic exception
*/
final static short INVALID_NUMBER_FORMAT = 0x6308;
/**
* Initial account balance
*/
final static byte[] INITIAL_ACCOUNT_BALANCE = { (byte) 0x01, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
// Amount of money in user's account
private BigNumber accountBalance;
// Big number for temporary calculation
BigNumber tempBigNum;
// temporary buffer used as scratch space
byte[] scratchSpace;
private LargeBal() {
accountBalance = new BigNumber((byte) 8);
// initialize account balance to 100,000.00
accountBalance.init(INITIAL_ACCOUNT_BALANCE, (byte) 0,
(byte) INITIAL_ACCOUNT_BALANCE.length, BigNumber.FORMAT_BCD);
// initialize the temporary big number
tempBigNum = new BigNumber(BigNumber.getMaxBytesSupported());
// initialize the scratchSpace
scratchSpace = JCSystem.makeTransientByteArray((short) 10,
JCSystem.CLEAR_ON_DESELECT);
register();
}
public static void install(byte[] bArray, short bOffset, byte bLength) {
// GP-compliant JavaCard applet registration
new LargeBal();
}
public void process(APDU apdu) {
// Good practice: Return 9000 on SELECT
if (selectingApplet()) {
return;
}
byte[] buf = apdu.getBuffer();
switch (buf[ISO7816.OFFSET_INS]) {
case INS_GET_BALANCE:
getBalance(apdu, buf);
break;
case INS_CREDIT:
break;
case INS_DEBIT:
break;
default:
// good practice: If you don't know the INStruction, say so:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
private void getBalance(APDU apdu, byte[] buffer) {
if (buffer[ISO7816.OFFSET_P1] == BigNumber.FORMAT_BCD) {
accountBalance.toBytes(buffer, (short) 0, (short) 8,
BigNumber.FORMAT_BCD);
} else if (buffer[ISO7816.OFFSET_P1] == BigNumber.FORMAT_HEX) {
accountBalance.toBytes(buffer, (short) 0, (short) 8,
BigNumber.FORMAT_HEX);
} else
ISOException.throwIt(INVALID_NUMBER_FORMAT);
apdu.setOutgoingAndSend((short) 0, (short) 8);
}
}

javacardx.framework.math is an optional package. Thus, not all cards/emulators implement this. In your case, it seems that the card does not implement javacardx.framework.math.BigNumber. Consequently it refuses to load/install the applet.
From the Runtime Environment Specification, Java Card Platform, Version 2.2.2 (section 9.7):
Optional Extension Packages
Some API packages in the Java Card technology are designated as extension
packages and may be optionally supported by an implementation. But, if supported,
all the classes in the package and its subpackages must be implemented by the
platform and reside on the card.
The following are optional Java Card technology extension packages:
javacardx.apdu [...]
javacardx.biometry [...]
javacardx.crypto [...]
javacardx.external [...]
javacardx.framework [...] If implemented, this package must include all the contained subpackages - util, math, and tlv.

Related

How to encrypt data in one app and decrypt it in different Windows app with RSA keys tied to local system?

I have a setup where I need to encrypt blob of data in one app and decrypt it in different app.
I built a sample app that creates a named CngKey object. Then create a RSACng using CngKey object. Then use RSACng object to do encryption/decryption. What I found is that the key changes across restarts of the application even though it is loaded using the name it was created with. I am lost trying to understand the relation between CngKey and RSACng objects.
Below is snippet of code that describes what I am trying to do:
using System;
using System.IO;
using System.Security.Cryptography;
namespace TPMCrypto
{
class Program
{
static byte[] data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
static byte[] privateKey;
private static byte[] encrypted;
private static byte[] decrypted;
static void Main(string[] args)
{
const string MyKey = "MyRSAKey";
CngKey cngKey = null;
string cmd = args.Length > 0 ? args[0] : "";
try
{
CngKeyCreationParameters cng = new CngKeyCreationParameters
{
KeyUsage = CngKeyUsages.AllUsages,
KeyCreationOptions = CngKeyCreationOptions.MachineKey,
Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider
};
if (!CngKey.Exists(MyKey, CngProvider.MicrosoftSoftwareKeyStorageProvider, CngKeyOpenOptions.MachineKey))
{
Console.WriteLine("Creating rsaKey");
cngKey = CngKey.Create(CngAlgorithm.Rsa, MyKey, cng);
}
else
{
Console.WriteLine("Opening rsaKey");
cngKey = CngKey.Open(MyKey, CngProvider.MicrosoftSoftwareKeyStorageProvider, CngKeyOpenOptions.MachineKey);
}
RSACng rsaKey = new RSACng(cngKey)
{
KeySize = 2048
};
privateKey = rsaKey.Key.Export(CngKeyBlobFormat.GenericPrivateBlob);
string prvResult = ByteArrayToHexString(privateKey, 0, privateKey.Length);
Console.WriteLine("\nPrivate key - length = " + privateKey.Length + "\n" + prvResult + "\n");
const string FILE_PATH = #"\temp\tpmtests\encryptedblob.dat";
// Encrypt / decrypt
if (cmd == "readfromfile")
{
Directory.CreateDirectory(Path.GetDirectoryName(FILE_PATH));
encrypted = File.ReadAllBytes(FILE_PATH);
}
else if (cmd == "deletekey")
{
cngKey.Delete();
return;
}
else
{
encrypted = Encrypt(rsaKey, data);
Console.WriteLine("The encrypted blob: ");
Console.WriteLine(ByteArrayToHexString(encrypted, 0, encrypted.Length));
File.WriteAllBytes(FILE_PATH, encrypted);
}
decrypted = Decrypt(rsaKey, encrypted);
bool result = ByteArrayCompare(data, decrypted);
if (result)
Console.WriteLine("Encrypt / decrypt works");
else
Console.WriteLine("Encrypt / decrypt fails");
}
catch (Exception e)
{
Console.WriteLine("Exception " + e.Message);
}
finally
{
if (cngKey != null)
cngKey.Dispose();
}
Console.ReadLine();
}
static bool ByteArrayCompare(byte[] a1, byte[] a2)
{
if (a1.Length != a2.Length)
return false;
for (int i = 0; i < a1.Length; i++)
if (a1[i] != a2[i])
return false;
return true;
}
public static string ByteArrayToHexString(byte[] bytes, int start, int length)
{
string delimitedStringValue = BitConverter.ToString(bytes, start, length);
return delimitedStringValue.Replace("-", "");
}
public static byte[] Sign512(byte[] data, byte[] privateKey)
{
CngKey key = CngKey.Import(privateKey, CngKeyBlobFormat.GenericPrivateBlob);
RSACng crypto = new RSACng(key);
return crypto.SignData(data, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
}
public static bool VerifySignature512(byte[] data, byte[] signature, byte[] publicKey)
{
CngKey key = CngKey.Import(publicKey, CngKeyBlobFormat.GenericPublicBlob);
RSACng crypto = new RSACng(key);
return crypto.VerifyData(data, signature, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
}
public static byte[] Encrypt(byte[] publicKey, byte[] data)
{
CngKey key = CngKey.Import(publicKey, CngKeyBlobFormat.GenericPublicBlob);
RSACng crypto = new RSACng(key);
var result = Encrypt(crypto, data);
return result;
}
public static byte[] Encrypt(RSACng crypto, byte[] data)
{
if (null == crypto)
return null;
var result = crypto.Encrypt(data, RSAEncryptionPadding.OaepSHA512);
return result;
}
public static byte[] Decrypt(byte[] privateKey, byte[] data)
{
CngKey key = CngKey.Import(privateKey, CngKeyBlobFormat.GenericPrivateBlob);
RSACng crypto = new RSACng(key);
var result = Decrypt(crypto, data);
return result;
}
public static byte[] Decrypt(RSACng aKey, byte[] data)
{
if (null == aKey)
return null;
var result = aKey.Decrypt(data, RSAEncryptionPadding.OaepSHA512);
return result;
}
}
}
I am aware of dpapi and how to do this using it. I don't want to use it for this, please don't point me in that direction. I am using CNG flavor of crypto to force C# use NCryptXYZ crypto calls and the desire is to secure the keys in TPM.
Ah, looking at your code again, you've made a goof.
RSACng rsaKey = new RSACng(cngKey)
{
KeySize = 2048
};
Setting the KeySize property on an RSACng does one of two things:
If get_KeySize == value, ignore the input, do nothing.
Else, detach from the current key and the next time the key is used, generate a new key of get_KeySize at the time.
So you're opening an existing key, then discarding it, and generating a new ephemeral key. (Which you could see by checking rsaKey.Key.Name, it won't match your input).
Presumably you did this as a way to create the key with the right size in the first place, but you're too late. The correct way is
CngKeyCreationParameters cng = new CngKeyCreationParameters
{
KeyUsage = CngKeyUsages.AllUsages,
KeyCreationOptions = CngKeyCreationOptions.MachineKey,
Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider,
Parameters =
{
new CngProperty("Length", BitConverter.GetBytes(2048), CngPropertyOptions.Persist),
},
};

Decrypt in chunks a AES 128 CBC encrypted object

I have an Encrypted object in Minio, encrypted using the AES 128 bit CBC algorithm.
The object is quite large (~50 MB) so instead of loading it into the memory completely (which may cause out of memory exception), I am retrieving it in chunks of 1MB. I need to decrypt it before use.
Is it possible to decrypt the object in this way (1MB at a time, the whole object was encrypted in one go)?
If yes, how can I do it?
I have tried decrypting 16-byte chunks which produce the following errors:
javax.crypto.BadPaddingException: Given final block not properly padded
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
To avoid an "out of memory error" you want to decrypt a large (encrypted) file in chunks of 1 mb size - yes it's possible with AES CBC mode.
Below you find a complete example that is generating a sample plaintext file ('plaintext.dat') with random content with the size of 50 mb + 1 byte (the + 1 byte is good to test for file sizes that are not exact multiples of 16 = AES blocksize).
In the next step this file is getting encrypted to 'ciphertext.dat' using a randomly created initialization vector and key.
The last step is the requested decryption method - it decrypts the encrypted file in chunks of 1 mb and in the lines '// obuf holds the decrypted chunk, do what you want to do with the data' and '// final data' you do have the decrypted data in the byte array obuf. For testing I'm writing the decrypted data to the file 'decryptedtext.dat' in appending mode (for that reason this file is deleted in the beginning if it exists).
To prove that decryption was successful I'm comparing the SHA256-hashes of plaintext- and decryptedtext-files.
Two notes: I'm using a 32 byte = 256 bit long key for AES CBC 256. This program has no proper exception handling and is for educational purposes only.
result:
decrypt AES CBC 256 in 1 mb chunks
file with random data created: plaintext.dat
encryption to ciphertext.dat was successfull: true
decryption in chunks of 1 mb
decrypted file written to decryptedtext.dat
plaintext equals decrytedtext file: true
code:
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.security.*;
import java.util.Arrays;
public class AES_CBC_chunk_decryption {
public static void main(String[] args) throws IOException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
System.out.println("https://stackoverflow.com/questions/63325528/decrypt-in-chunks-a-aes-128-cbc-encrypted-object/63325529#63325529");
System.out.println("decrypt AES CBC 256 in 1 mb chunks");
// setup for creation of a 50mb encrypted file
int filesize = (50 * 1024 * 1024) + 1; // 50 mb + 1 byte = 52428801 bytes
String filenamePlaintext = "plaintext.dat";
String filenameCiphertext = "ciphertext.dat";
String filenameDecryptedtext = "decryptedtext.dat";
File file = new File("plaintext.dat");
// fill with random bytes.
try (FileOutputStream out = new FileOutputStream(file)) {
byte[] bytes = new byte[filesize];
new SecureRandom().nextBytes(bytes);
out.write(bytes);
}
System.out.println("\nfile with random data created: " + filenamePlaintext);
// delete decrypted file if it exists
Files.deleteIfExists(new File(filenameDecryptedtext).toPath());
// setup random key & iv
SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[16];
byte[] key = new byte[32]; // I'm using a 32 byte = 256 bit long key for aes 256
secureRandom.nextBytes(iv);
secureRandom.nextBytes(key);
// encrypt complete file
boolean resultEncryption = encryptCbcFileBufferedCipherOutputStream(filenamePlaintext, filenameCiphertext, key, iv);
System.out.println("encryption to " + filenameCiphertext + " was successfull: " + resultEncryption);
// encrypted file is 52428816 bytes long
System.out.println("\ndecryption in chunks of 1 mb");
// decryption in chunks of 1 mb
try (FileInputStream in = new FileInputStream(filenameCiphertext)) {
byte[] ibuf = new byte[(1024 * 1024)]; // chunks of 1 mb
int len;
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
while ((len = in.read(ibuf)) != -1) {
byte[] obuf = cipher.update(ibuf, 0, len);
if (obuf != null)
// obuf holds the decrypted chunk, do what you want to do with the data
// I'm writing it to a file in appending mode
try (FileOutputStream output = new FileOutputStream(filenameDecryptedtext, true)) {
output.write(obuf);
}
}
byte[] obuf = cipher.doFinal();
if (obuf != null)
// final data
try (FileOutputStream output = new FileOutputStream(filenameDecryptedtext, true)) {
output.write(obuf);
}
}
System.out.println("decrypted file written to " + filenameDecryptedtext);
System.out.println("plaintext equals decrytedtext file: " + filecompareSha256Large(filenamePlaintext, filenameDecryptedtext));
}
public static boolean encryptCbcFileBufferedCipherOutputStream(String inputFilename, String outputFilename, byte[] key, byte[] iv)
throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFilename);
FileOutputStream out = new FileOutputStream(outputFilename);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] buffer = new byte[8096];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
public static boolean filecompareSha256Large(String filename1, String filename2) throws IOException, NoSuchAlgorithmException {
boolean result = false;
byte[] hash1 = generateSha256Buffered(filename1);
byte[] hash2 = generateSha256Buffered(filename2);
result = Arrays.equals(hash1, hash2);
return result;
}
private static byte[] generateSha256Buffered(String filenameString) throws IOException, NoSuchAlgorithmException {
// even for large files
byte[] buffer = new byte[8192];
int count;
MessageDigest md = MessageDigest.getInstance("SHA-256");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filenameString));
while ((count = bis.read(buffer)) > 0) {
md.update(buffer, 0, count);
}
bis.close();
return md.digest();
}
}
Yes, with AES-128-CBC, it is possible to decrypt just a single block of cyphertext. Each block is 128 bits (16 bytes).
See the diagram at https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_block_chaining_(CBC). As you can see, to decrypt any block of ciphertext, you AES-decrypt the block of ciphertext, then XOR the plaintext with the previous block of ciphertext. (For the first block, the plaintext is XOR'd with the IV).
The library that you are using is probably throwing these exceptions, because it is checking if the decrypted ciphertext is properly padded. Of course, if you are decrypting just one arbitrary block of ciphertext, it will not have the proper padding. However, you can use a tool like openssl to decrypt a single block of ciphertext, given the ciphertext, the key, and the previous block of ciphertext, like so:
echo -n 'bc6d8afc78e805b7ed7551e42da4d877' | xxd -p -r | openssl aes-128-cbc -d -nopad -K e3e33d2d9591b462c55503f7ec697839 -iv 1d3fa2b7c9008e1cdbc76a1f22388b89
where:
bc6d8afc78e805b7ed7551e42da4d877 is the block of ciphertext that you want to decrypt
e3e33d2d9591b462c55503f7ec697839 is the key
1d3fa2b7c9008e1cdbc76a1f22388b89 is the previous block of ciphertext
Yes, it is possible. However, due to the mode and padding it may be trickier to program than it looks at first sight.
However, I've created a class that will happily decode from any offset and to any size. Note that the ciphertext should not contain the IV.
In hindsight I might better have used ByteBuffer to make it a bit more flexible, but yeah, that will require an entire rewrite...
package com.stackexchange.so;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* A class that helps you to partially decrypt a CBC ciphertext. Although this class helps you to partially decrypt any
* part, you'd probably want to decrypt chunks that consists of a specific number of blocks; both the <code>off</code>
* and <code>len</code> parameter should be a modulus the block size. If you know the exact plaintext length then you
* can size the last chunk precisely.
*
* #author maartenb
*/
public class CBCDecryptByOffset {
private enum State {
UNINITIALIZED, INITIALIZED, RUNNING;
};
private final Cipher cbcCipher;
private SecretKey symKey;
private IvParameterSpec iv;
private State state = State.UNINITIALIZED;
/**
* Creates the CBC decryptor class and initializes it.
* #param blockCipher the block cipher, without block cipher mode or padding indication e.g. <code>"AES"</code>
* #throws NoSuchAlgorithmException if the block cipher is not available for <code>"CBC"</code>
* #throws NoSuchPaddingException if the block cipher in CBC mode is not available with <code>"NoPadding"</code>
*/
public CBCDecryptByOffset(String blockCipher) throws NoSuchAlgorithmException, NoSuchPaddingException {
this.cbcCipher = Cipher.getInstance(blockCipher + "/CBC/NoPadding");
}
/**
* Mimics {#link Cipher#init(int, java.security.Key, java.security.spec.AlgorithmParameterSpec)} except that it
* doesn't include options for encryption, wrapping or unwrapping.
*
* #param symKey the key to use
* #param iv the IV to use
* #throws InvalidKeyException if the key is not valid for the block cipher
* #throws InvalidAlgorithmParameterException if the IV is not valid for CBC, i.e. is not the block size
*/
public void init(SecretKey symKey, IvParameterSpec iv)
throws InvalidKeyException, InvalidAlgorithmParameterException {
this.symKey = symKey;
this.iv = iv;
// init directly, probably we want to start here, and it will perform a cursory check of the key and IV
this.cbcCipher.init(Cipher.DECRYPT_MODE, symKey, iv);
this.state = State.INITIALIZED;
}
/**
* Decrypts a partial number of bytes from a CBC encrypted ciphertext with PKCS#7 compatible padding.
*
* #param fullCT the full ciphertext
* #param off the offset within the full ciphertext to start decrypting
* #param len the amount of bytes to decrypt
* #return the plaintext of the partial decryption
* #throws BadPaddingException if the ciphertext is not correctly padded (only checked for the final CT block)
* #throws IllegalBlockSizeException if the ciphertext is empty or not a multiple of the block size
*/
public byte[] decryptFromOffset(byte[] fullCT, int off, int len)
throws BadPaddingException, IllegalBlockSizeException {
if (state == State.UNINITIALIZED) {
throw new IllegalStateException("Instance should be initialized before decryption");
}
int n = cbcCipher.getBlockSize();
if (fullCT.length == 0 || fullCT.length % n != 0) {
throw new IllegalBlockSizeException(
"Ciphertext must be a multiple of the blocksize, and should contain at least one block");
}
if (off < 0 || off > fullCT.length) {
throw new IllegalArgumentException("Invalid offset: " + off);
}
if (len < 0 || off + len < 0 || off + len > fullCT.length) {
throw new IllegalArgumentException("Invalid len");
}
if (len == 0) {
return new byte[0];
}
final int blockToDecryptFirst = off / n;
final int blockToDecryptLast = (off + len - 1) / n;
final int bytesToDecrypt = (blockToDecryptLast - blockToDecryptFirst + 1) * n;
final byte[] pt;
try {
// determine the IV to use
if (state != State.INITIALIZED || off != 0) {
IvParameterSpec vector;
final int blockWithVector = blockToDecryptFirst - 1;
if (blockWithVector == -1) {
vector = iv;
} else {
vector = new IvParameterSpec(fullCT, blockWithVector * n, n);
}
cbcCipher.init(Cipher.DECRYPT_MODE, symKey, vector);
}
// perform the actual decryption (note that offset and length are in bytes)
pt = cbcCipher.doFinal(fullCT, blockToDecryptFirst * n, bytesToDecrypt);
} catch (GeneralSecurityException e) {
throw new RuntimeException("Incorrectly programmed, error should never appear", e);
}
// we need to unpad if the last block is the final ciphertext block
int sigPadValue = 0;
final int finalCiphertextBlock = (fullCT.length - 1) / n;
if (blockToDecryptLast == finalCiphertextBlock) {
int curPaddingByte = bytesToDecrypt - 1;
int padValue = Byte.toUnsignedInt(pt[curPaddingByte]);
if (padValue == 0 || padValue > n) {
throw new BadPaddingException("Invalid padding");
}
for (int padOff = curPaddingByte - 1; padOff > curPaddingByte - padValue; padOff--) {
if (Byte.toUnsignedInt(pt[padOff]) != padValue) {
throw new BadPaddingException("Invalid padding");
}
}
// somebody tries to decrypt just padding bytes
if (off >= (blockToDecryptLast + 1) * n - padValue) {
sigPadValue = len;
} else {
// calculate if any (significant) padding bytes need to be ignored within the plaintext
int bytesInFinalBlock = (off + len - 1) % n + 1;
sigPadValue = padValue - (n - bytesInFinalBlock);
if (sigPadValue < 0) {
sigPadValue = 0;
}
}
}
int ptStart = off - blockToDecryptFirst * n;
int ptSize = len - sigPadValue;
state = State.RUNNING;
if (pt.length == ptSize) {
return pt;
}
return Arrays.copyOfRange(pt, ptStart, ptStart + ptSize);
}
}
Note that I've tested the general functionality but I'd make sure that I wrap it with some JUnit tests if I were you.

How to communicate from android application to Patient monitoring System (PMS) in the form of packets by using Socket connection?

We are having patient monitoring system which is Wi-Fi enabled. Here the requirement to establish a communication between android application and Patient Monitoring System (PMS) device using Socket connection and communication should be happen in the form of packets. Once initiate the request, it continuously sends the data over Socket, but I am unable to form the data in the form of packets. Please can anyone help to how to send data in the form of data packets?
The request format is given below.
Header field denotes the type of the operation that is to be performed.
Data payload field contains two fields Length and Actual Data bytes.
The Data length field indicates the number of bytes of data that
follow.
The Data Payload field is optional.
The header field is shown below :–
1st bit is direction of transfer. 0 – Read, 1 – Write.
The Command field (7 bits) specifies the command to be executed.
The Option1 field will give the information about the parameter
that is under consideration.
The Option2 (lower 4 bits) and Option3 (upper 4 bits) fields
are optional. For certain commands, they carry data with specific
meaning. For certain other commands, they are just ignored.
Commands standards given below:-
Code snippet given below
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#2D3238"
android:padding="10dp">
<Button
android:id="#+id/connectBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="10dp"
android:onClick="onClick"
android:text="Connect" />
</RelativeLayout>
MainActivity.java
package com.test.pms.app;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.test.pms.app.R;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class MainActivity extends Activity implements View.OnClickListener {
private Button connectBtn;
private Socket socket = null;
private boolean isRunning = false;
private String ipAddress = "192.168.1.55";
private int portNumber = 4561;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
connectBtn = (Button) findViewById(R.id.connectBtn);
}
#Override
protected void onResume() {
super.onResume();
isRunning = true;
}
#Override
protected void onPause() {
isRunning = false;
super.onPause();
}
#Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.connectBtn:
new CommunicateTask().execute();
break;
}
}
private class CommunicateTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
sendData();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
private void sendData() {
DataOutputStream dataOutputStream = null;
DataInputStream dataInputStream = null;
try {
socket = new Socket(ipAddress, portNumber);
dataOutputStream = new DataOutputStream(socket.getOutputStream());
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
connectBtn.setText(getString(R.string.connected));
}
});
/* byte[] byteArray = new byte[5];
byteArray[0] = (byte) 1; //direction 0-write and 1- read
byteArray[1] = (byte) 3; // Command Type 3 - CMD_TIMESTAMP
byteArray[2] = (byte) 1; //Option1- SPO2
byteArray[3] = (byte) 0; //Option2: Nothing
byteArray[4] = (byte) 1; //Option3 : RED*/
String command = "CMD_TIMESTAMP";
// byte arr[] = {1, 0, 0, 0, 0, 1, 0, 0};
dataOutputStream.writeUTF(command);
dataInputStream = new DataInputStream(socket.getInputStream());
//dataOutputStream.write(byteArray);
//dataOutputStream.writeUTF(command);
// dataInputStream.write(arr);
// dataOutputStream.writeUTF("Hello Server");
while (isRunning) {
if (socket.isConnected()) {
String b = dataInputStream.readLine();
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (dataOutputStream != null) {
try {
dataOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (dataInputStream != null) {
try {
dataInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Here Socket connection is established successfully , but when I try read data from inputStream unable to read. Execution stopped at dataInputStream.readLine();.
I tried multiple methods to read data from InputStream like
dataInputStream.readLine();
dataInputStream.readByte();
dataInputStream.read();
Please can anyone help me to form request format in the form of packets and read the data in packets?
Thanks in advance.
This is an example on how to set each byte of the header.
To form the header, you can set each byte using hex numbers.
For example:
byte b = 0xA8;
A == 10 == 0011
8 == 8 == 1000
then b = A8 = 00111000;
Example:
Dir: 1
Command: 1010101 (just invented a command)
First byte is 11010101
We separate in two sets of four bits: 1101 0101
Thse binaries reperesent 13 5 in decimal and D 5 in hex
The first byte value for this command is 0xD5

DF is lost after JCOP restart?

I am working on a small ticketing prototype.
What I want to do is - if the top-level ADF is selected I want to return the AID of all the containing DFs and on first sight it works quite well.
I create the ADF, and 1 or 2 DFs. When the ADF is selected, the AIDs of those DFs are returned fine and I can add EFs (or DFs), ...
Now when I restart the whole thing (I am using JCOP btw.) I can still select the ADF but the AIDs from the DFs are not returned anymore, in fact I get a 6F00 "no precise diagnosis".
For my data structure - first you see the minimal constructor for the ADF, which has no parents
public DirectoryFile(byte[] aid) {
super(aid);
this.aid = aid;
numApp = 1;
created = true;
}
the second structure is the same but for a "usual" DirectoryFile with a parentDirectoryFile and an array (arrayFiles) of Elementary Files:
public DirectoryFile(byte[] aid, DirectoryFile parent) {
super(aid, parent);
for (byte i = 0; i < numberFiles; i++) {
arrayFiles[i].setActive(false);
}
}
both inherit from the same File.class
public File (byte aid[]) {
Util.arrayCopy(aid, (short) 0, this.aid, (short) 0, (short) 6);
}
public File (byte[] aid, DirectoryFile parentFile) {
this.parentFile = parentFile;
Util.arrayCopy(aid, (short) 0, this.aid, (short) 0, (short) 6);
}
This should be a very basic Filesystem and it does work as long as the card is connected to the terminal, but the information seems to be lost after restart of the program although I am not using transient arrays at all for this.
The return code is always "6F00 - no precise diagnosis" which leads to an unreferred byte[] or something like that, which I cannot find any except of the DF objects, that get instanciated when the new Object is created.
EDIT: just figured out it might be a more "general" problem and that is what I am doing wrong.
Now, if I take a "Hello World" like http://umer555.wordpress.com/2012/05/17/java-card-hello-world-applet/ and add some INS like I did here:
public class HalloWeltApplet extends Applet {
private static byte[] helloWorld = new byte[11];
private static final byte HW_CLA = (byte)0x80;
private static final byte HW_INS = (byte)0x00;
private static final byte HW_INS1 = (byte)0x01;
private static final byte HW_INS2 = (byte)0x02;
public static void install(byte[] bArray, short bOffset, byte bLength) {
new HalloWeltApplet().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
}
public void process(APDU apdu) {
if (selectingApplet()) {
return;
}
byte[] buffer = apdu.getBuffer();
byte CLA = (byte) (buffer[ISO7816.OFFSET_CLA] & 0xFF);
byte INS = (byte) (buffer[ISO7816.OFFSET_INS] & 0xFF);
if(CLA != HW_CLA) {
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
}
switch(INS) {
case HW_INS:
getHelloWorld(apdu);
break;
case HW_INS1:
getHelloWorld1(apdu);
break;
case HW_INS2:
getHelloWorld2(apdu);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
private void getHelloWorld( APDU apdu) {
byte[] buffer = apdu.getBuffer();
short length = (short) helloWorld.length;
byte[] test = {(byte)'H',(byte)'e',(byte)'l',(byte)'l',(byte)'o',(byte)' ',(byte)'W',(byte)'o',(byte)'r',(byte)'l',(byte)'d',};
Util.arrayCopy(test, (short) 0, helloWorld, (short) 0, (short) test.length);
}
private void getHelloWorld1( APDU apdu) {
byte[] buffer = apdu.getBuffer();
short length = (short) helloWorld.length;
byte[] test = {(byte)'H',(byte)'i',(byte)' ',(byte)'W',(byte)'o',(byte)'r',(byte)'l',(byte)'d'};
Util.arrayCopy(test, (short) 0, helloWorld, (short) 0, (short) test.length);
}
private void getHelloWorld2( APDU apdu) {
byte[] buffer = apdu.getBuffer();
apdu.setOutgoing();
apdu.setOutgoingLength((short) helloWorld.length);
apdu.sendBytesLong(helloWorld, (short) 0, (short) helloWorld.length);
}
}
So this should in my eyes save 'Hello World' or 'Hi World' into helloWorld and with INS2 I can show which one is saved. But whenever I restart the program, helloWorld will be empty due to initiation, right? Could that be my problem after all, and if so, how can it be resolved?
The problem is probably here:
super(aid);
this.aid = aid;
First you correctly copy the data, then you overwrite the field in File with the one you've used in the DirectoryFile constructor. If that is a transient buffer or worse, the JCRE owned APDU buffer, then your code will fail as JCRE owned objects should not be used through persistent references.
Note that AID's are Application Identifiers. They identify applications such as your Java Card applet. Normally files and non-application DF's (especially child DF's) are not identified or selected with an AID but with a file identifier or a (related) short file identifier. See ISO/IEC 7816-4 (any version I guess) for details.
Note that resets work in the JCOP simulator, but that all information is lost when you restart the process; data is not saved to disk and applets must be reloaded.

Conversion from .NET 3.5 to .NET 2.0

How I can convert the following methods to C# 2.0?
private static string ToHexString(byte[] bytes)
{
return string.Join(string.Empty, bytes.Select(x => x.ToString("X2")).ToArray());
}
private static byte[] ToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length).
Where(x => 0 == x % 2).
Select(x => Convert.ToByte(hex.Substring(x, 2), 16)).
ToArray();
}
I haven't got experience with .NET 2.0. Thanks!
void Main()
{
string s = ToHexString(new byte[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15});
Console.WriteLine(s);
foreach (byte b in ToByteArray(s))
Console.WriteLine(b);
}
private static string ToHexString(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
sb.Append(b.ToString("X2"));
return sb.ToString();
}
private static byte[] ToByteArray(string hex)
{
byte[] b = new byte[hex.Length/2];
for (int i = 0; i < b.Length; i++)
{
b[i] = Convert.ToByte(hex.Substring(i*2,2), 16);
}
return b;
}
You should be able to do this conversion yourself. Obviously you'll want to convert it into a for loop. Enumerable.Range essentially provides an int[] array upon which to loop. After that, the Where equates to an if check, the Select is a transformation from the int to a substring and finally to a byte, and last, you stuff all that into an array, probably by adding them to a List<byte> declared outside the loop. (and when you're done, you can call ToArray on the list)
I could provide a complete answer, but I think this sort of exercise is best left to you so you can learn from it.