I want get the UID of the Mifare Ultralight NFC tag.
In Java I have this code:
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
System.out.println("Terminals: " + terminals);
CardTerminal terminal = terminals.get(0);
Card card = terminal.connect("*");
System.out.println("card: " + card);
CardChannel channel = card.getBasicChannel();
ResponseAPDU answer = channel.transmit(new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 0x00));
byte[] uid = answer.getBytes();
The problem is that I receive two bytes and not the UID.
What's the problem? Is the APDU correct?
The command you are actually using is not what you might have expected.
The correct command APDU to get the UID/serial number/enumeration identifier with this reader is:
+------+------+------+------+------+
| CLA | INS | P1 | P2 | Le |
+------+------+------+------+------+
| 0xFF | 0xCA | 0x00 | 0x00 | 0x00 |
+------+------+------+------+------+
However, the constructor you are using is defined as:
public CommandAPDU(int cla, int ins, int p1, int p2, int ne);
So with
new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 0x00)
you are creating a C-APDU with the following parameters CLA = 0xFF, INS = 0xCA, P1 = 0x00, P2 = 0x00. So far this is the same as the above APDU. But the last parameter is Ne = 0x00. Ne = 0 means that the number of expected response bytes is zero (whereas Le = 0 would mean that the number of expected response bytes is (up to) 256).
This results in effectively creating the following Case-1 APDU:
+------+------+------+------+
| CLA | INS | P1 | P2 |
+------+------+------+------+
| 0xFF | 0xCA | 0x00 | 0x00 |
+------+------+------+------+
So at most you will get the 2-byte status word as a response (either indicating success with 0x90 0x00 or indicating an error with a status code like 0x6X 0xXX).
So you can either use a byte array to form your APDU:
new CommandAPDU(new byte[] { (byte)0xFF, (byte)0xCA, (byte)0x00, (byte)0x00, (byte)0x00 } )
Or you can specify a proper value for Ne:
new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 256)
import java.nio.ByteBuffer;
import java.util.List;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.TerminalFactory;
public class Read {
public Read() {
try {
CardTerminal terminal = null;
// show the list of available terminals
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
String readerName = "";
for (int i = 0; i < terminals.size(); i++) {
readerName = terminals.get(i).toString()
.substring(terminals.get(i).toString().length() - 2);
//terminal = terminals.get(i);
if (readerName.equalsIgnoreCase(" 0")) {
terminal = terminals.get(i);
}
}
// Establish a connection with the card
System.out.println("Waiting for a card..");
if(terminal==null)
return;
terminal.waitForCardPresent(0);
Card card = terminal.connect("T=0");
CardChannel channel = card.getBasicChannel();
// Start with something simple, read UID, kinda like Hello World!
byte[] baReadUID = new byte[5];
baReadUID = new byte[] { (byte) 0xFF, (byte) 0xCA, (byte) 0x00,
(byte) 0x00, (byte) 0x00 };
System.out.println("UID: " + send(baReadUID, channel));
// If successfull, the output will end with 9000
// OK, now, the real work
} catch (Exception ex) {
ex.printStackTrace();
}
}
public String send(byte[] cmd, CardChannel channel) {
String res = "";
byte[] baResp = new byte[258];
ByteBuffer bufCmd = ByteBuffer.wrap(cmd);
ByteBuffer bufResp = ByteBuffer.wrap(baResp);
// output = The length of the received response APDU
int output = 0;
try {
output = channel.transmit(bufCmd, bufResp);
}` catch (CardException ex) {
ex.printStackTrace();
}`
for (int i = 0; i < output; i++) {
res += String.format("%02X", baResp[i]);
// The result is formatted as a hexadecimal integer
}
return res;
}
public static void main(String[] args) {
new Read();
}
}
After read this code For read and write purpose use bellow commands.
And Read From page : 04 to page : 07 Command is:
read_four_to_seven = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x05, (byte) 0x0D4, (byte) 0x40, (byte) 0x01,
(byte) 0x30, (byte) 0x04, (byte) 0x07 };
System.out.println("Read : " + send(read_four_to_seven, channel));
Write into Page 04:
Write_Page_Four = new byte[] { (byte) 0xFF, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x15, (byte) 0xD4, (byte) 0x40,
(byte) 0x01, (byte) 0xA0, (byte) 0x04, (byte) 0x4D,
(byte) 0x65, (byte) 0x73, (byte) 0x75, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00 };
System.out.println("Read : " + send(Write_Page_Four, channel));
Related
I am currently struggling with getting a picture from a camera connected to the uart circuit of the raspberry pi. I am trying to do so with the aid of pi4j. Since I was able to initialize the camera, I do not think that the problem is related to the commands I am sending. However, when I try to open the generated .jpg the file is corrupt.
Has anyone an idea what I am doing wrong or has managed to get a picture form a camera connected to uart of the raspberry pi with java?
Camera: Grove - Serial Camera Kit
Datasheet: Grove - Serial Camera Datasheet PDF
Example Code: Python Code
private void getPicture(long pictureLength) {
try {
byte[] receiveDataPackageCommand = { (byte) 0xaa, (byte) 0x0e, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00 };
byte[] ackPackageEndCommand = { (byte) 0xaa, (byte) 0x0e, (byte) 0x00, (byte) 0x00, (byte) 0xf0,
(byte) 0xF0 };
File picture = new File(getFileName());
console.println("created file " + picture.getName());
if (picture.createNewFile()) {
FileOutputStream stream = new FileOutputStream(picture.getName());
int i = 0;
while (pictureLength > 0) {
receiveDataPackageCommand[4] = (byte) (i & 0xff);
receiveDataPackageCommand[5] = (byte) ((i >> 8) & 0xff);
serial.write(receiveDataPackageCommand);
byte[] bytes = pictureLength >= 128 ? serial.read(128) : serial.read((int) pictureLength);
stream.write(bytes);
pictureLength = pictureLength - 128;
i++;
}
stream.close();
serial.write(ackPackageEndCommand);
console.println("picture received and saved");
} else {
console.println("file already exists");
}
} catch (Exception ex) {
console.println(ex);
}
}
printer : oki ml3320, emulation IBM
I can't print this barcode...
Now I took 22. example from https://files.support.epson.com/pdf/general/escp2ref.pdf
My last try looking that
final char[] x = {0x1B, 0x28, 0x42, 0x10, 0x00, 0x06, 0x02, 0x00, 0x7D, 0x00, 0x02, 0x42, 0x32, 0x33, 0x40, 0x61, 0x42, 0x63, 0x44, 0x5B, 0x5D};
if (printer.getPrinterByName("OKI")){
InputStream is = new ByteArrayInputStream(
(new String(x)).getBytes(Charset.forName(DOSCharsetProvider.MAZOVIA_CHARSET_NAME)));
Result is printed " B4}B23#aBcDC] "
For example 1. : 3}012345678012
okey got it!
char[] b = "DWZ:3:1585543:5".toCharArray();
final char[] start = {
27,16,66,
15, //length of code
66 //CODE 128 mode B
};
Last question... how Can I put a length of b to start (in 15 place)
Hi am new to java card I have following data
CLA '00'
INS 'A2' nb not real value
P1 '00'
P2 '00'
LC '08'
Data In 'EF08'
Le '0D'
How can I write above instructions into bytes and send to this function? I need to get 9000 as success response and Data out.
ResponseAPDU respApdu = channel.transmit(
new CommandAPDU(cmd));
There are multiple ways to do that:
Case 1: (Not encouraged)
int cla = 0x00;
int ins = 0xA2;
int p1 = 0x00;
int p2 = 0x00;
//int LC = 0x08;'
byte[] data = new byte[] {(byte) 0xEF, (byte) 0x08};
int le = 0x0D;
ResponseAPDU respApdu = channel.transmit(
new CommandAPDU(cla, ins, p1, p2, data, le));
Case 2: (Encouraged)
byte[] apdu = new byte[] {(byte) 0x00, (byte) 0xA2, (byte) 0x00, (byte)
0x00, (byte) 0x02, (byte) 0xEF, (byte) 0x08, (byte) 0x0D};
ResponseAPDU respApdu = channel.transmit(
new CommandAPDU(apdu));
Read more about CommandAPDU and CardChannel.
Case 3: (Mostly used way)
String apdu = "00A2000002EF080D"; //also u can append strings into apdu
ResponseAPDU respApdu = channel.transmit(
new CommandAPDU(toByteArray(apdu)));
You need a Helper function:
import javax.xml.bind.DatatypeConverter;
public static byte[] toByteArray(String s) {
return DatatypeConverter.parseHexBinary(s);
}
Attention: The example APDU values you showed, LC '08' means the data will be 8 bytes long, but your data field is only 2 bytes long. So check LC again.
I am a student and as part of my internship I am looking to make an applet that lets me read a nfc tag from the ACR122 reader.
I use the package javax.smartcardio.
However, I can not read the data found on the tag or even the uid.
the transmit methode returns me many data that I do not really understand ... and completely random.
I think not spend good hexadecimal values in my CommandAPDU object.
The code for the run method of my thread:
http://pastebin.com/u6m0jArd
Here are the results:
ResponseAPDU : ResponseAPDU: 2 bytes, SW=6300
ResponseAPDU getBytes: [B#5c12c55c
ResponseAPDU getData: [B#6c63e398
ResponseAPDU : ResponseAPDU: 2 bytes, SW=6300
ResponseAPDU getBytes: [B#5c12c55c
ResponseAPDU getData: [B#6c63e39
UPDATE
right now I can read Data on the card but only when they are empty, when I write on it with another application (like GoToTag) I can't authenticate a block.
Moreover I can't write on it neither.
Here is my code to read:
if(MyReader.waitForCardPresent(0))
{
if(!Lu)
System.out.println("détectée");
card = MyReader.connect("*");
if(card != null)
{
if(!Lu)
{
System.out.println("Carte connectée");
System.out.println("ATR: " + arrayToHex(((ATR) card.getATR()).getBytes()));
}
ch = card.getBasicChannel();
/*Get UID*/
byte[] ApduArrayUID = {
(byte) 0xff,
(byte) 0xca,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00
};
/*Iso Card*/
byte[] ApduArrayISO = {
(byte) 0xff,
(byte) 0xca,
(byte) 0x01,
(byte) 0x00,
(byte) 0x00
};
/* load Authentification */
byte[] ApduArrayLoadAuth= {
(byte) 0xff, //Class
(byte) 0x82, //INS
(byte) 0x00,//emplacement volatile du lecteur
(byte) 0x00, //emplacement sur le lecteur
(byte) 0x06, //LC
(byte) 0xff, // Valeur de la clé sur 6 bytes
(byte) 0xff,
(byte) 0xff,
(byte) 0xff,
(byte) 0xff,
(byte) 0xff
};
/*Authentication du Block 00h*/
byte[] ApduArrayAuth = {
(byte) 0xff,// Class
(byte) 0x86, //INS
(byte) 0x00,//P1
(byte) 0x00,//P2
(byte) 0x05,//LC
//Authentication Data bytes
(byte) 0x01, //Version
(byte) 0x00, // Byte 2
(byte) 0x00,//Block number
(byte) 0x60,// Clé de type A
(byte) 0x00 //Emplacement de la clé
};
/*Read Block 1*/
byte[] ApduArrayRead = {
(byte) 0xff, //Class
(byte) 0xb0,//INS
(byte) 0x00,//P1
(byte) 0x00,//P2 = Block number
(byte) 0x10, // Le = Number of bytes to read
};
/***** UID *****/
CommandAPDU GetDataUID = new CommandAPDU(ApduArrayUID);
ResponseAPDU CardApduResponseUID = ch.transmit(GetDataUID);
//nfc.AfficheUID(byteToString(CardApduResponseUID.getBytes()));
if(CardApduResponseUID.getSW() == 36864)
{
String Uid=byteToString(CardApduResponseUID.getBytes());
if(! Uid.equals(UidCourant))// Si une nouvelle carte les blocks mémoire ne sont pas authentifié
{
Auth=false;
Lu=false;
}
else
{
Auth=true; Lu=true;
}
UidCourant= byteToString(CardApduResponseUID.getBytes());
uid.setText(UidCourant);
if(!Lu)
System.out.println("UID : "+UidCourant);
}
/***** Charger authentification *****/
//System.out.println("UID response: " + byteToString(CardApduResponseUID.getBytes()));
if(!Charge)
{
System.out.println("Chargement de l'authentification dans le lecteur ");
CommandAPDU GetDataLoadAuth = new CommandAPDU(ApduArrayLoadAuth);
ResponseAPDU CardApduResponse = ch.transmit(GetDataLoadAuth);
if(CardApduResponse.getSW() == 36864) // 90 00h = success
{
Charge=true;
System.out.println("Chargement authentification réussie :" );
}
}
if(Charge)
{
if(!Auth)
{
/******* authentification du block 00h*********/
System.out.println("Authentification d'un block");
CommandAPDU GetDataAuth = new CommandAPDU(ApduArrayAuth);
ResponseAPDU CardApduResponseAuth = ch.transmit(GetDataAuth);
System.out.println("ResponseAPDU auth : "+ CardApduResponseAuth);
if(CardApduResponseAuth.getSW() == 36864)
{
Auth=true;
System.out.println("Authentification d'un block réussie ! ");
}
else
System.out.println("Impossible d'authentifier le block :'(");
}
if(Auth && !Lu) // si l'authentification est faite et qu'on a pas encore Lu, on lit
{
/*Lecture du block 0x00*/
System.out.println("Lecture d'un block");
CommandAPDU GetDataRead = new CommandAPDU(ApduArrayRead);
ResponseAPDU CardApduResponseRead = ch.transmit(GetDataRead);
System.out.println("ResponseAPDU auth : "+ CardApduResponseRead);
if(CardApduResponseRead.getSW() == 36864)
{
System.out.println("Read response: " + byteToString(CardApduResponseRead.getBytes()));
Lu=true;
}
}
}
card.disconnect(true);
}
else
System.out.println("Carte non connectée");
}
Here is my code to write:
while(!isStop())
{
try
{
uid.setText("");
if(MyReader.waitForCardPresent(0))
{
if(!Lu)
System.out.println("détectée");
card = MyReader.connect("*");
if(card != null)
{
if(!Lu)
{
System.out.println("Carte connectée");
System.out.println("ATR: " + arrayToHex(((ATR) card.getATR()).getBytes()));
}
ch = card.getBasicChannel();
/*Get UID*/
byte[] ApduArrayUID = {
(byte) 0xff,
(byte) 0xca,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00
};
/*Iso Card*/
byte[] ApduArrayISO = {
(byte) 0xff,
(byte) 0xca,
(byte) 0x01,
(byte) 0x00,
(byte) 0x00
};
/* load Authentification */
byte[] ApduArrayLoadAuth= {
(byte) 0xff, //Class
(byte) 0x82, //INS
(byte) 0x00,//emplacement volatile du lecteur
(byte) 0x00, //emplacement sur le lecteur
(byte) 0x06, //LC
(byte) 0xff, // Valeur de la clé sur 6 bytes
(byte) 0xff,
(byte) 0xff,
(byte) 0xff,
(byte) 0xff,
(byte) 0xff
};
/*Authentication du Block 00h*/
byte[] ApduArrayAuth = {
(byte) 0xff,// Class
(byte) 0x86, //INS
(byte) 0x00,//P1
(byte) 0x00,//P2
(byte) 0x05,//LC
//Authentication Data bytes
(byte) 0x01, //Version
(byte) 0x00, // Byte 2
(byte) 0x00,//Block number
(byte) 0x60,// Clé de type A
(byte) 0x00 //Emplacement de la clé
};
/*Write Block 1*/
byte[] ApduArrayWrite = {
(byte) 0xff, //Class
(byte) 0xd6,//INS
(byte) 0x00,//P1
(byte) 0x00,//P2 = Block number
(byte) 0x02, // Lc = Number of bytes to update
//Data to be written
(byte)0x00,
(byte)0x01
};
/***** UID *****/
CommandAPDU GetDataUID = new CommandAPDU(ApduArrayUID);
ResponseAPDU CardApduResponseUID = ch.transmit(GetDataUID);
//nfc.AfficheUID(byteToString(CardApduResponseUID.getBytes()));
if(CardApduResponseUID.getSW() == 36864)
{
String Uid=byteToString(CardApduResponseUID.getBytes());
if(! Uid.equals(UidCourant))// Si une nouvelle carte les blocks mémoire ne sont pas authentifié
{
Auth=false;
Lu=false;
}
else
{
Auth=true; Lu=true;
}
UidCourant= byteToString(CardApduResponseUID.getBytes());
uid.setText(UidCourant);
if(!Lu)
System.out.println("UID : "+UidCourant);
}
/***** Charger authentification *****/
//System.out.println("UID response: " + byteToString(CardApduResponseUID.getBytes()));
if(!Charge)
{
System.out.println("Chargement de l'authentification dans le lecteur ");
CommandAPDU GetDataLoadAuth = new CommandAPDU(ApduArrayLoadAuth);
ResponseAPDU CardApduResponse = ch.transmit(GetDataLoadAuth);
if(CardApduResponse.getSW() == 36864) // 90 00h = success
{
Charge=true;
System.out.println("Chargement authentification réussie :" );
}
}
if(Charge)
{
if(!Auth)
{
/******* authentification du block 00h*********/
System.out.println("Authentification d'un block");
CommandAPDU GetDataAuth = new CommandAPDU(ApduArrayAuth);
ResponseAPDU CardApduResponseAuth = ch.transmit(GetDataAuth);
System.out.println("ResponseAPDU auth : "+ CardApduResponseAuth);
if(CardApduResponseAuth.getSW() == 36864)
{
Auth=true;
System.out.println("Authentification d'un block réussie ! ");
}
else
System.out.println("Impossible d'authentifier le block :'(");
}
if(Auth && !Lu) // si l'authentification est faite et qu'on a pas encore Lu, on lit
{
/*Ecriture du block 0x00*/
System.out.println("Ecriture d'un block");
CommandAPDU GetDataWrite = new CommandAPDU(ApduArrayWrite);
ResponseAPDU CardApduResponseWrite = ch.transmit(GetDataWrite);
System.out.println("ResponseAPDU Write : "+ CardApduResponseWrite);
if(CardApduResponseWrite.getSW() == 36864)
{
System.out.println("Ecriture réussite!");
System.out.println("Write response: " + byteToString(CardApduResponseWrite.getBytes()));
Lu=true;
}
else
System.out.println("Echec de l'écriture :/");
}
}
card.disconnect(true);
}
else
System.out.println("Carte non connectée");
}
Reading data from the MIFARE Classic card is possible while it is empty, but does not work once another application (like GoToTags) wrote to the card
You are currently authenticating to sector 0 with key A FF FF FF FF FF FF. Apps like GoToTags or NFC TagWriter will transform the MIFARE Classic card into an NDEF tag according to NXP's application note "NFC Type MIFARE Classic Tag Operation". As a consequence, the access conditions for sector 0 (the MIFARE Application Directory sector) and the sectors used for storing NDEF data are modified. Typically, this means that
for sector 0, key A is set to A0 A1 A2 A3 A4 A5 ("MAD key"), key B is set to FF FF FF FF FF FF, and the access conditions are set in a way that key A can only be used for reading while key B can be used for reading and writing.
for all other sectors, key A is set to D3 F7 D3 F7 D3 F7, key B is set to FF FF FF FF FF FF, and the access conditions are set in a way that key A can only be used for reading while key B can be used for reading and writing.
Hence, once you used such an app, authenticating to sector 0 with key A FF FF FF FF FF FF will no longer work, so you have to update the key in your byte[] ApduArrayLoadAuth command definition accordingly (or change the key type in byte[] ApduArrayAuth to key B).
Writing to the MIFARE Classic card does not work
Your write command in byte[] ApduArrayWrite currently tries to write two bytes:
byte[] ApduArrayWrite = { (byte) 0xff, //Class
(byte) 0xd6, //INS
(byte) 0x00, //P1
(byte) 0x00, //P2 = Block number
(byte) 0x02, // Lc = Number of bytes to update
//Data to be written
(byte)0x00, (byte)0x01 };
MIFARE Classic blocks consist of 16 bytes, so writing only two bytes is not possible. You always have to write a complete block:
byte[] ApduArrayWrite = { (byte) 0xff, //Class
(byte) 0xd6, //INS
(byte) 0x00, //P1
(byte) 0x00, //P2 = Block number
(byte) 0x10, // Lc = Number of bytes to update
//Data to be written
(byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
Following Google's advice here and using their code as a guide. I've tried using their encrypted hyperlocal string to test the decryption method that I wrote (most of it from Google though as my own attempts at writing such a method caused me much headache).
I'm getting Signature Mismatch for some reason even though I'm providing the proper integrity and encryption keys and I'm using the byte array that they provided in their example decryption (lower down Google's RTB decrypt hyperlocal page).
I have the code here:
package anon.bidder.adx;
import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import org.apache.log4j.Logger;
public class AdxBidRequestDecryptor {
private static final int INITIALIZATION_VECTOR_SIZE = 16;
private static final int SIGNATURE_SIZE = 4;
private static final int BLOCK_SIZE = 20;
public static class DecrypterException extends Exception {
public DecrypterException(String message) {
super(message);
}
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static void main(String args[]){
byte[] ciphertext = hexStringToByteArray("E2014EA201246E6F6E636520736F7572636501414243C0ADF6B9B6AC17DA218FB50331EDB376701309CAAA01246E6F6E636520736F7572636501414243C09ED4ECF2DB7143A9341FDEFD125D96844E25C3C202466E6F6E636520736F7572636502414243517C16BAFADCFAB841DE3A8C617B2F20A1FB7F9EA3A3600256D68151C093C793B0116DB3D0B8BE9709304134EC9235A026844F276797");
byte[] encryptionKey = {(byte)0x02, (byte)0xEE, (byte)0xa8, (byte)0x3c, (byte)0x6c, (byte)0x12, (byte)0x11, (byte)0xe1, (byte)0x0b,
(byte) 0x9f, (byte) 0x88, (byte) 0x96, (byte) 0x6c, (byte) 0xee, (byte) 0xc3, (byte) 0x49, (byte) 0x08, (byte) 0xeb, (byte) 0x94, (byte) 0x6f, (byte) 0x7e,
(byte) 0xd6, (byte) 0xe4, (byte) 0x41, (byte) 0xaf, (byte) 0x42, (byte) 0xb3, (byte) 0xc0, (byte) 0xf3, (byte) 0x21, (byte) 0x81, (byte) 0x40};
byte[] integrityKey = {(byte) 0xbf, (byte) 0xFF, (byte) 0xec, (byte) 0x55, (byte) (byte) 0xc3, (byte) 0x01, (byte) 0x30, (byte) 0xc1, (byte) 0xd8,
(byte) 0xcd, (byte) 0x18, (byte) 0x62, (byte) 0xed, (byte) 0x2a, (byte) 0x4c, (byte) 0xd2, (byte) 0xc7, (byte) 0x6a, (byte) 0xc3, (byte) 0x3b, (byte) 0xc0,
(byte) 0xc4, (byte) 0xce, (byte) 0x8a, (byte) 0x3d, (byte) 0x3b, (byte) 0xbd, (byte) 0x3a, (byte) 0xd5, (byte) 0x68, (byte) 0x77, (byte) 0x92};
try {
byte[] plain = decrypt(ciphertext, new SecretKeySpec(encryptionKey,"HmacSHA1"),new SecretKeySpec(integrityKey,"HmacSHA1"));
} catch (DecrypterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static byte[] decrypt(byte[] ciphertext,
SecretKey encryptionKey,
SecretKey integrityKey)
throws DecrypterException {
try {
// Step 1. find the length of initialization vector and clear text.
final int plaintext_length =
ciphertext.length - INITIALIZATION_VECTOR_SIZE - SIGNATURE_SIZE;
if (plaintext_length < 0) {
throw new RuntimeException("The plain text length can't be negative.");
}
byte[] iv = Arrays.copyOf(ciphertext, INITIALIZATION_VECTOR_SIZE);
// Step 2. recover clear text
final Mac hmacer = Mac.getInstance("HmacSHA1");
final int ciphertext_end = INITIALIZATION_VECTOR_SIZE + plaintext_length;
final byte[] plaintext = new byte[plaintext_length];
boolean add_iv_counter_byte = true;
for (int ciphertext_begin = INITIALIZATION_VECTOR_SIZE, plaintext_begin = 0;
ciphertext_begin < ciphertext_end;) {
hmacer.reset();
hmacer.init(encryptionKey);
final byte[] pad = hmacer.doFinal(iv);
int i = 0;
while (i < BLOCK_SIZE && ciphertext_begin != ciphertext_end) {
plaintext[plaintext_begin++] =
(byte)(ciphertext[ciphertext_begin++] ^ pad[i++]);
}
if (!add_iv_counter_byte) {
final int index = iv.length - 1;
add_iv_counter_byte = ++iv[index] == 0;
}
if (add_iv_counter_byte) {
add_iv_counter_byte = false;
iv = Arrays.copyOf(iv, iv.length + 1);
}
}
// Step 3. Compute integrity hash. The input to the HMAC is clear_text
// followed by initialization vector, which is stored in the 1st section
// or ciphertext.
hmacer.reset();
hmacer.init(integrityKey);
hmacer.update(plaintext);
hmacer.update(Arrays.copyOf(ciphertext, INITIALIZATION_VECTOR_SIZE));
final byte[] computedSignature = Arrays.copyOf(hmacer.doFinal(), SIGNATURE_SIZE);
final byte[] signature = Arrays.copyOfRange(
ciphertext, ciphertext_end, ciphertext_end + SIGNATURE_SIZE);
if (!Arrays.equals(signature, computedSignature)) {
throw new DecrypterException("Signature mismatch.");
}
return plaintext;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("HmacSHA1 not supported.", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("Key is invalid for this purpose.", e);
}
}
}
The example in the documentation appears to be broken. The test encrypted data embedded in the example archive at the end of java/Decrypter.java conveniently uses the same encryption and integrity keys, and provides us with known encrypted and plaintext data (all bytes are 0xfe).
When I patch your test main with the archive data it passes the signature test and your plaintext result is correct. When I patch Google's program with the data from the documentation page, it fails to match the signature. I believe your code is working but your test data is bad.