1256 | *
1257 | * @param apdu
1258 | * @param status Status to send
1259 | */
1260 | private void sendNext(APDU apdu, short status) {
1261 | byte[] buf = APDU.getCurrentAPDUBuffer();
1262 | apdu.setOutgoing();
1263 |
1264 | // Determine maximum size of the messages
1265 | short max_length;
1266 | if(sm_success) {
1267 | max_length = RESPONSE_SM_MAX_LENGTH;
1268 | }
1269 | else {
1270 | max_length = RESPONSE_MAX_LENGTH;
1271 | }
1272 |
1273 | Util.arrayCopyNonAtomic(buffer, out_sent, buf, _0, max_length);
1274 |
1275 | short len = 0;
1276 |
1277 | if (out_left > max_length) {
1278 | len = max_length;
1279 |
1280 | // Compute byte left and sent
1281 | out_left -= max_length;
1282 | out_sent += max_length;
1283 |
1284 | // Determine new status word
1285 | if (out_left > max_length) {
1286 | status = (short) (SW_BYTES_REMAINING_00 | max_length);
1287 | } else {
1288 | status = (short) (SW_BYTES_REMAINING_00 | out_left);
1289 | }
1290 | }
1291 | else {
1292 | len = out_left;
1293 |
1294 | // Reset buffer
1295 | out_sent = 0;
1296 | out_left = 0;
1297 | }
1298 |
1299 | // If SM is used, wrap response
1300 | if(sm_success) {
1301 | len = sm.wrapResponseAPDU(buf, _0, len, status);
1302 | }
1303 |
1304 | // Send data in buffer
1305 | apdu.setOutgoingLength(len);
1306 | apdu.sendBytes(_0, len);
1307 |
1308 | // Send status word
1309 | if(status != SW_NO_ERROR)
1310 | ISOException.throwIt(status);
1311 | }
1312 |
1313 | /**
1314 | * Get length of TLV element.
1315 | *
1316 | * @param data
1317 | * Byte array
1318 | * @param offset
1319 | * Offset within byte array containing first byte
1320 | * @return Length of value
1321 | */
1322 | private short getLength(byte[] data, short offset) {
1323 | short len = 0;
1324 |
1325 | if ((data[offset] & (byte) 0x80) == (byte) 0x00) {
1326 | len = data[offset];
1327 | } else if ((data[offset] & (byte) 0x7F) == (byte) 0x01) {
1328 | len = (short)(0xFF & data[(short) (offset + 1)]);
1329 | } else if ((data[offset] & (byte) 0x7F) == (byte) 0x02) {
1330 | len = Util.makeShort(data[(short) (offset + 1)], data[(short) (offset + 2)]);
1331 | } else {
1332 | ISOException.throwIt(SW_UNKNOWN);
1333 | }
1334 |
1335 | return len;
1336 | }
1337 |
1338 | /**
1339 | * Get number of bytes needed to represent length for TLV element.
1340 | *
1341 | * @param length
1342 | * Length of value
1343 | * @return Number of bytes needed to represent length
1344 | */
1345 | private short getLengthBytes(short length) {
1346 | if (length <= 127)
1347 | return 1;
1348 | else if (length <= 255)
1349 | return 2;
1350 | else
1351 | return 3;
1352 | }
1353 |
1354 | /**
1355 | * Return the key of the type requested: - B6: Digital signatures - B8:
1356 | * Confidentiality - A4: Authentication
1357 | *
1358 | * @param type
1359 | * Type of key to be returned
1360 | * @return Key of requested type
1361 | */
1362 | private PGPKey getKey(byte type) {
1363 | PGPKey key = sig_key;
1364 |
1365 | if (type == (byte) 0xB6)
1366 | key = sig_key;
1367 | else if (type == (byte) 0xB8)
1368 | key = dec_key;
1369 | else if (type == (byte) 0xA4)
1370 | key = auth_key;
1371 | else
1372 | ISOException.throwIt(SW_UNKNOWN);
1373 |
1374 | return key;
1375 | }
1376 |
1377 | /**
1378 | * Increase the digital signature counter by one. In case of overflow
1379 | * SW_WARNING_STATE_UNCHANGED will be thrown and nothing will
1380 | * change.
1381 | */
1382 | private void increaseDSCounter() {
1383 | for (short i = (short) (ds_counter.length - 1); i >= 0; i--) {
1384 | if ((short) (ds_counter[i] & 0xFF) >= 0xFF) {
1385 | if (i == 0) {
1386 | // Overflow
1387 | ISOException.throwIt(SW_WARNING_STATE_UNCHANGED);
1388 | } else {
1389 | ds_counter[i] = 0;
1390 | }
1391 | } else {
1392 | ds_counter[i]++;
1393 | break;
1394 | }
1395 | }
1396 | }
1397 | }
1398 |
--------------------------------------------------------------------------------
/src/openpgpcard/OpenPGPSecureMessaging.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java Card implementation of the OpenPGP card
3 | *
4 | * Copyright (C) 2011 Joeri de Ruiter
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU General Public License
8 | * as published by the Free Software Foundation; either version 2
9 | * of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program; if not, write to the Free Software
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | *
20 | * OpenPGPSecureMessaging.java is based on OVSecureMessaging.java which is part
21 | * of OVchip-ng
22 | *
23 | * Copyright (C) Pim Vullers, Radboud University Nijmegen, February 2011.
24 | */
25 |
26 | package openpgpcard;
27 |
28 | import javacard.framework.APDU;
29 | import javacard.framework.ISO7816;
30 | import javacard.framework.ISOException;
31 | import javacard.framework.JCSystem;
32 | import javacard.framework.Util;
33 | import javacard.security.DESKey;
34 | import javacard.security.KeyBuilder;
35 | import javacard.security.Signature;
36 | import javacardx.crypto.Cipher;
37 |
38 | /**
39 | * OV secure messaging functionality.
40 | *
41 | * OVSecureMessaging is based on PassportCrypto which is part of the
42 | * e-passport Java Card applet from the JMRTD project (http://jmrtd.org/).
43 | *
44 | * @author Pim Vullers
45 | * @version $Revision: 12 $ by $Author: joeridr $
46 | * $LastChangedDate: 2012-02-23 15:31:33 +0100 (Thu, 23 Feb 2012) $
47 | */
48 | public class OpenPGPSecureMessaging {
49 | private static final short SW_INTERNAL_ERROR = (short) 0x6D66;
50 | private static final byte[] PAD_DATA = {(byte) 0x80, 0, 0, 0, 0, 0, 0, 0};
51 | private static final short SSC_SIZE = 8;
52 | private static final short TMP_SIZE = 256;
53 | private static final short MAC_SIZE = 8;
54 | private static final short KEY_SIZE = 16;
55 | private static final byte[] EMPTY_KEY = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
56 |
57 | /**
58 | * The needed cryptographic functionality.
59 | */
60 | private Signature signer;
61 | private Signature verifier;
62 | private Cipher cipher;
63 | private Cipher decipher;
64 |
65 | /**
66 | * The needed keys.
67 | */
68 | private DESKey keyMAC;
69 | private DESKey keyENC;
70 |
71 | /**
72 | * The send sequence counter.
73 | */
74 | private byte[] ssc;
75 |
76 | /**
77 | * Storage for temporary data.
78 | */
79 | private byte[] tmp;
80 |
81 | private boolean[] ssc_set;
82 |
83 | /**
84 | * Construct a new secure messaging wrapper.
85 | */
86 | public OpenPGPSecureMessaging() {
87 | ssc = JCSystem.makeTransientByteArray(SSC_SIZE,
88 | JCSystem.CLEAR_ON_DESELECT);
89 | tmp = JCSystem.makeTransientByteArray(TMP_SIZE,
90 | JCSystem.CLEAR_ON_DESELECT);
91 | signer = Signature.getInstance(
92 | Signature.ALG_DES_MAC8_ISO9797_1_M2_ALG3, false);
93 | verifier = Signature.getInstance(
94 | Signature.ALG_DES_MAC8_ISO9797_1_M2_ALG3, false);
95 | cipher = Cipher.getInstance(
96 | Cipher.ALG_DES_CBC_ISO9797_M2, false);
97 | decipher = Cipher.getInstance(
98 | Cipher.ALG_DES_CBC_ISO9797_M2, false);
99 |
100 | keyMAC = (DESKey) KeyBuilder.buildKey(
101 | KeyBuilder.TYPE_DES_TRANSIENT_DESELECT,
102 | KeyBuilder.LENGTH_DES3_2KEY, false);
103 | keyENC = (DESKey) KeyBuilder.buildKey(
104 | KeyBuilder.TYPE_DES_TRANSIENT_DESELECT,
105 | KeyBuilder.LENGTH_DES3_2KEY, false);
106 |
107 | ssc_set = JCSystem.makeTransientBooleanArray((short)1, JCSystem.CLEAR_ON_DESELECT);
108 | ssc_set[0] = false;
109 | }
110 |
111 | /**
112 | * Set the MAC and encryption (and decryption) session keys. Each key is a
113 | * 16 byte 3DES EDE key. This method may be called at any time and will
114 | * immediately replace the session key.
115 | *
116 | * @param buffer byte array containing the session keys.
117 | * @param offset location of the session keys in the buffer.
118 | */
119 | public void setSessionKeys(byte[] buffer, short offset) {
120 | // Check for empty keys
121 | if(Util.arrayCompare(buffer, (short)0, EMPTY_KEY, (short)0, KEY_SIZE) == 0 ||
122 | Util.arrayCompare(buffer, KEY_SIZE, EMPTY_KEY, (short)0, KEY_SIZE) == 0) {
123 | keyMAC.clearKey();
124 | keyENC.clearKey();
125 | }
126 | else {
127 | keyMAC.setKey(buffer, offset);
128 | keyENC.setKey(buffer, (short) (offset + KEY_SIZE));
129 |
130 | signer.init(keyMAC, Signature.MODE_SIGN);
131 | verifier.init(keyMAC, Signature.MODE_VERIFY);
132 |
133 | cipher.init(keyENC, Cipher.MODE_ENCRYPT);
134 | decipher.init(keyENC, Cipher.MODE_DECRYPT);
135 | }
136 | }
137 |
138 | /**
139 | * Set the MAC session key. Each key is a 16 byte 3DES EDE key. This method
140 | * may be called at any time and will immediately replace the session key.
141 | *
142 | * @param buffer byte array containing the session key.
143 | * @param offset location of the session key in the buffer.
144 | */
145 | public void setSessionKeyMAC(byte[] buffer, short offset) {
146 | // Check for empty keys
147 | if(Util.arrayCompare(buffer, (short)0, EMPTY_KEY, (short)0, KEY_SIZE) == 0) {
148 | keyMAC.clearKey();
149 | keyENC.clearKey();
150 | }
151 | else {
152 | keyMAC.setKey(buffer, offset);
153 |
154 | signer.init(keyMAC, Signature.MODE_SIGN);
155 | verifier.init(keyMAC, Signature.MODE_VERIFY);
156 | }
157 | }
158 |
159 | /**
160 | * Set the encryption session key. Each key is a 16 byte 3DES EDE key. This method
161 | * may be called at any time and will immediately replace the session key.
162 | *
163 | * @param buffer byte array containing the session key.
164 | * @param offset location of the session key in the buffer.
165 | */
166 | public void setSessionKeyEncryption(byte[] buffer, short offset) {
167 | // Check for empty keys
168 | if(Util.arrayCompare(buffer, (short)0, EMPTY_KEY, (short)0, KEY_SIZE) == 0) {
169 | keyMAC.clearKey();
170 | keyENC.clearKey();
171 | }
172 | else {
173 | keyENC.setKey(buffer, (short) (offset + KEY_SIZE));
174 |
175 | cipher.init(keyENC, Cipher.MODE_ENCRYPT);
176 | decipher.init(keyENC, Cipher.MODE_DECRYPT);
177 | }
178 | }
179 |
180 | /**
181 | * Set the MAC and encryption (and decryption) 3DES session keys to zero.
182 | */
183 | public void clearSessionKeys() {
184 | keyMAC.clearKey();
185 | keyENC.clearKey();
186 | }
187 |
188 | /**
189 | * Unwraps (verify and decrypt) the command APDU located in the APDU buffer.
190 | * The command buffer has to be filled by the APDU.setIncomingAndReceive()
191 | * method beforehand. The verified and decrypted command data get placed at
192 | * the start of the APDU buffer.
193 | *
194 | * @return the length value encoded by DO97, 0 if this object is missing.
195 | */
196 | public short unwrapCommandAPDU() {
197 | byte[] buf = APDU.getCurrentAPDUBuffer();
198 | short apdu_p = (short) (ISO7816.OFFSET_CDATA & 0xff);
199 | short start_p = apdu_p;
200 | short le = 0;
201 | short do87DataLen = 0;
202 | short do87Data_p = 0;
203 | short do87LenBytes = 0;
204 | short hdrLen = 4;
205 | short hdrPadLen = (short) (8 - hdrLen);
206 |
207 | incrementSSC();
208 |
209 | if (buf[apdu_p] == (byte) 0x87) {
210 | apdu_p++;
211 | // do87
212 | if ((buf[apdu_p] & 0xff) > 0x80) {
213 | do87LenBytes = (short) (buf[apdu_p] & 0x7f);
214 | apdu_p++;
215 | } else {
216 | do87LenBytes = 1;
217 | }
218 | if (do87LenBytes > 2) { // sanity check
219 | ISOException.throwIt(SW_INTERNAL_ERROR);
220 | }
221 | for (short i = 0; i < do87LenBytes; i++) {
222 | do87DataLen += (short) ((buf[(short)(apdu_p + i)] & 0xff) << (short) ((do87LenBytes - 1 - i) * 8));
223 | }
224 | apdu_p += do87LenBytes;
225 |
226 | if (buf[apdu_p] != 1) {
227 | ISOException.throwIt(SW_INTERNAL_ERROR);
228 | }
229 | // store pointer to data and defer decrypt to after mac check (do8e)
230 | do87Data_p = (short) (apdu_p + 1);
231 | apdu_p += do87DataLen;
232 | do87DataLen--; // compensate for 0x01 marker
233 | }
234 |
235 | if (buf[apdu_p] == (byte) 0x97) {
236 | // do97
237 | if (buf[++apdu_p] != 1)
238 | ISOException.throwIt(SW_INTERNAL_ERROR);
239 | le = (short) (buf[++apdu_p] & 0xff);
240 | apdu_p++;
241 | }
242 |
243 | // do8e
244 | if (buf[apdu_p] != (byte) 0x8e) {
245 | ISOException.throwIt(SW_INTERNAL_ERROR);
246 | }
247 | if (buf[++apdu_p] != 8) {
248 | ISOException.throwIt(ISO7816.SW_DATA_INVALID);
249 | }
250 |
251 | // verify mac
252 | verifier.update(ssc, (short)0, (short)ssc.length);
253 | verifier.update(buf, (short)0, hdrLen);
254 | verifier.update(PAD_DATA, (short)0, hdrPadLen);
255 | if (!verifier.verify(buf, start_p, (short) (apdu_p - 1 - start_p), buf,
256 | (short)(apdu_p + 1), MAC_SIZE)) {
257 | ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
258 | }
259 |
260 | short lc = 0;
261 | if (do87DataLen != 0) {
262 | // decrypt data, and leave room for lc
263 | lc = decipher.doFinal(buf, do87Data_p, do87DataLen, buf,
264 | (short) (hdrLen + 1));
265 | buf[hdrLen] = (byte) (lc & 0xff);
266 | }
267 |
268 | return le;
269 | }
270 |
271 | /**
272 | * Wraps (encrypts and build MAC) the response data and places it in the
273 | * APDU buffer starting at offset 0. The buffer can be any buffer including
274 | * the APDU buffer itself. If the length is zero the buffer will not be
275 | * addressed and no response data will be present in the wrapped output.
276 | *
277 | * @param buffer byte array containing the data which needs to be wrapped.
278 | * @param offset location of the data in the buffer.
279 | * @param length of the data in the buffer (in bytes).
280 | * @param status word which has to be wrapped in the response APDU.
281 | * @return the length of the wrapped data in the buffer
282 | */
283 | public short wrapResponseAPDU(byte[] buffer, short offset, short length,
284 | short status) {
285 | byte[] apdu = APDU.getCurrentAPDUBuffer();
286 | short apdu_p = 0;
287 | // smallest multiple of 8 strictly larger than plaintextLen (length + padding)
288 | short do87DataLen = (short) ((((short) (length + 8)) / 8) * 8);
289 | // for 0x01 marker (indicating padding is used)
290 | do87DataLen++;
291 | short do87DataLenBytes = (short)(do87DataLen > 0xff? 2 : 1);
292 | short do87HeaderBytes = getApduBufferOffset(length);
293 | short do87Bytes = (short)(do87HeaderBytes + do87DataLen - 1); // 0x01 is counted twice
294 | boolean hasDo87 = length > 0;
295 |
296 | incrementSSC();
297 |
298 | short ciphertextLength=0;
299 | if(hasDo87) {
300 | // Copy the plain text to temporary buffer to avoid data corruption.
301 | Util.arrayCopyNonAtomic(buffer, offset, tmp, (short) 0, length);
302 | // Put the cipher text in the proper position.
303 | ciphertextLength = cipher.doFinal(tmp, (short) 0, length, apdu,
304 | do87HeaderBytes);
305 | }
306 | //sanity check
307 | //note that this check
308 | // (possiblyPaddedPlaintextLength != (short)(do87DataLen -1))
309 | //does not always hold because some algs do the padding in the final, some in the init.
310 | if (hasDo87 && (((short) (do87DataLen - 1) != ciphertextLength)))
311 | ISOException.throwIt(SW_INTERNAL_ERROR);
312 |
313 | if (hasDo87) {
314 | // build do87
315 | apdu[apdu_p++] = (byte) 0x87;
316 | if(do87DataLen < 0x80) {
317 | apdu[apdu_p++] = (byte)do87DataLen;
318 | } else {
319 | apdu[apdu_p++] = (byte) (0x80 + do87DataLenBytes);
320 | for(short i = (short) (do87DataLenBytes - 1); i >= 0; i--) {
321 | apdu[apdu_p++] = (byte) ((do87DataLen >>> (i * 8)) & 0xff);
322 | }
323 | }
324 | apdu[apdu_p++] = 0x01;
325 | }
326 |
327 | if(hasDo87) {
328 | apdu_p = do87Bytes;
329 | }
330 |
331 | // build do99
332 | apdu[apdu_p++] = (byte) 0x99;
333 | apdu[apdu_p++] = 0x02;
334 | Util.setShort(apdu, apdu_p, status);
335 | apdu_p += 2;
336 |
337 | // calculate and write mac
338 | signer.update(ssc, (short) 0, (short) ssc.length);
339 | signer.sign(apdu, (short) 0, apdu_p, apdu, (short) (apdu_p + 2));
340 |
341 | // write do8e
342 | apdu[apdu_p++] = (byte) 0x8e;
343 | apdu[apdu_p++] = 0x08;
344 | apdu_p += 8; // for mac written earlier
345 |
346 | return apdu_p;
347 | }
348 |
349 | /**
350 | * Increment the send sequence counter.
351 | */
352 | private void incrementSSC() {
353 | if (ssc == null || ssc.length <= 0) {
354 | return;
355 | }
356 |
357 | for (short s = (short) (ssc.length - 1); s >= 0; s--) {
358 | if ((short) ((ssc[s] & 0xff) + 1) > 0xff) {
359 | ssc[s] = 0;
360 | } else {
361 | ssc[s]++;
362 | break;
363 | }
364 | }
365 | }
366 |
367 | /***
368 | * Get the amount of space to reserve in the buffer when using secure
369 | * messaging.
370 | *
371 | * @param length length of plain text in which this offset depends.
372 | * @return the amount of space to reserve.
373 | */
374 | private short getApduBufferOffset(short length) {
375 | short do87Bytes = 2; // 0x87 length data 0x01
376 | // smallest multiple of 8 strictly larger than plaintextLen + 1
377 | // byte is probably the length of the cipher text (including do87 0x01)
378 | short do87DataLen = (short) ((((short) (length + 8) / 8) * 8) + 1);
379 |
380 | if (do87DataLen < 0x80) {
381 | do87Bytes++;
382 | } else if (do87DataLen <= 0xff) {
383 | do87Bytes += 2;
384 | } else {
385 | do87Bytes += (short) (length > 0xff ? 2 : 1);
386 | }
387 |
388 | return do87Bytes;
389 | }
390 |
391 | /**
392 | * Set the SSC
393 | *
394 | * @param buffer byte array containing the SSC
395 | * @param offset location of the data in the buffer
396 | */
397 | public void setSSC(byte[] buffer, short offset) {
398 | Util.arrayCopy(buffer, offset, ssc, (short)0, SSC_SIZE);
399 | ssc_set[0] = true;
400 | }
401 |
402 | /**
403 | * @return size in bytes of the SSC
404 | */
405 | public short getSSCSize() {
406 | return SSC_SIZE;
407 | }
408 |
409 | /**
410 | * @return boolean indicating whether SSC has been set
411 | */
412 | public boolean isSetSSC() {
413 | return ssc_set[0];
414 | }
415 | }
416 |
--------------------------------------------------------------------------------
/src/openpgpcard/PGPKey.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java Card implementation of the OpenPGP card
3 | * Copyright (C) 2011 Joeri de Ruiter
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 | */
19 | package openpgpcard;
20 |
21 | import javacard.framework.*;
22 | import javacard.security.*;
23 |
24 | /**
25 | * @author Joeri de Ruiter (joeri@cs.ru.nl)
26 | * @version $Revision: 12 $ by $Author: joeridr $
27 | * $LastChangedDate: 2012-02-23 15:31:33 +0100 (Thu, 23 Feb 2012) $
28 | */
29 | public class PGPKey implements ISO7816 {
30 | public static final short KEY_SIZE = 2048;// 2368;
31 | public static final short KEY_SIZE_BYTES = KEY_SIZE / 8;
32 | public static final short EXPONENT_SIZE = 17;
33 | public static final short EXPONENT_SIZE_BYTES = 3;
34 | public static final short FP_SIZE = 20;
35 |
36 | private KeyPair key;
37 | private byte[] fp;
38 | private byte[] time = { 0x00, 0x00, 0x00, 0x00 };
39 | private byte[] attributes = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x02 };
40 |
41 | public PGPKey() {
42 | key = new KeyPair(KeyPair.ALG_RSA_CRT, KEY_SIZE);
43 |
44 | fp = new byte[FP_SIZE];
45 | Util.arrayFillNonAtomic(fp, (short) 0, (short) fp.length, (byte) 0);
46 |
47 | Util.setShort(attributes, (short) 1, KEY_SIZE);
48 | Util.setShort(attributes, (short) 3, EXPONENT_SIZE);
49 | }
50 |
51 | /**
52 | * Generate the key pair.
53 | */
54 | public void genKeyPair() {
55 | key.genKeyPair();
56 | }
57 |
58 | /**
59 | * Set the fingerprint for the public key.
60 | *
61 | * @param data
62 | * Byte array
63 | * @param offset
64 | * Offset within byte array containing first byte
65 | */
66 | public void setFingerprint(byte[] data, short offset) {
67 | // Check whether there are enough bytes to copy
68 | if ((short) (offset + fp.length) > data.length)
69 | ISOException.throwIt(SW_UNKNOWN);
70 |
71 | Util.arrayCopy(data, offset, fp, (short) 0, (short) fp.length);
72 | }
73 |
74 | /**
75 | * Set the generation time for the key pair.
76 | *
77 | * @param data
78 | * Byte array
79 | * @param offset
80 | * Offset within byte array containing first byte
81 | */
82 | public void setTime(byte[] data, short offset) {
83 | // Check whether there are enough bytes to copy
84 | if ((short) (offset + time.length) > data.length)
85 | ISOException.throwIt(SW_UNKNOWN);
86 |
87 | Util.arrayCopy(data, offset, time, (short) 0, (short) 4);
88 | }
89 |
90 | /**
91 | * Get the fingerprint for the public key.
92 | *
93 | * @param data
94 | * Byte array
95 | * @param offset
96 | * Offset within byte array indicating first byte
97 | */
98 | public short getFingerprint(byte[] data, short offset) {
99 | Util.arrayCopyNonAtomic(fp, (short) 0, data, offset, (short) fp.length);
100 | return (short) (offset + fp.length);
101 | }
102 |
103 | /**
104 | * Get the generation time for the key pair.
105 | *
106 | * @param data
107 | * Byte array
108 | * @param offset
109 | * Offset within byte array indicating first byte
110 | */
111 | public short getTime(byte[] data, short offset) {
112 | Util.arrayCopyNonAtomic(time, (short) 0, data, offset,
113 | (short) time.length);
114 | return (short) (offset + time.length);
115 | }
116 |
117 | /**
118 | * Get the algorithm attributes for the key pair.
119 | *
120 | * @param data
121 | * Byte array
122 | * @param offset
123 | * Offset within byte array indicating first byte
124 | */
125 | public short getAttributes(byte[] data, short offset) {
126 | Util.arrayCopyNonAtomic(attributes, (short) 0, data, offset,
127 | (short) attributes.length);
128 | return (short) (offset + attributes.length);
129 | }
130 |
131 | /**
132 | * @return Public key of the key pair
133 | */
134 | public RSAPublicKey getPublic() {
135 | return (RSAPublicKey) key.getPublic();
136 | }
137 |
138 | /**
139 | * @return Private key of the key pair
140 | */
141 | public RSAPrivateCrtKey getPrivate() {
142 | return (RSAPrivateCrtKey) key.getPrivate();
143 | }
144 |
145 | /**
146 | * @return Length in bytes of the exponent
147 | */
148 | public short getExponentLength() {
149 | // Fixed value of 65537 for exponent
150 | return EXPONENT_SIZE_BYTES;
151 | }
152 |
153 | /**
154 | * @return Length in bytes of the modulus
155 | */
156 | public short getModulusLength() {
157 | return KEY_SIZE_BYTES;
158 | }
159 |
160 | /**
161 | * Sets the value of the DP1 parameter. The plain text data format is
162 | * big-endian and right-aligned (the least significant bit is the least
163 | * significant bit of last byte). Input DP1 parameter data is copied into
164 | * the internal representation.
165 | *
166 | * @param buffer
167 | * The input buffer
168 | * @param offset
169 | * The offset into the input buffer at which the parameter value
170 | * begins
171 | * @param length
172 | * The length of the parameter
173 | */
174 | public void setDP1(byte[] buffer, short offset, short length) {
175 | ((RSAPrivateCrtKey) key.getPrivate()).setDP1(buffer, offset, length);
176 | }
177 |
178 | /**
179 | * Sets the value of the DQ1 parameter. The plain text data format is
180 | * big-endian and right-aligned (the least significant bit is the least
181 | * significant bit of last byte). Input DQ1 parameter data is copied into
182 | * the internal representation.
183 | *
184 | * @param buffer
185 | * The input buffer
186 | * @param offset
187 | * The offset into the input buffer at which the parameter value
188 | * begins
189 | * @param length
190 | * The length of the parameter
191 | */
192 | public void setDQ1(byte[] buffer, short offset, short length) {
193 | ((RSAPrivateCrtKey) key.getPrivate()).setDQ1(buffer, offset, length);
194 | }
195 |
196 | /**
197 | * Sets the value of the P parameter. The plain text data format is
198 | * big-endian and right-aligned (the least significant bit is the least
199 | * significant bit of last byte). Input P parameter data is copied into the
200 | * internal representation.
201 | *
202 | * @param buffer
203 | * The input buffer
204 | * @param offset
205 | * The offset into the input buffer at which the parameter value
206 | * begins
207 | * @param length
208 | * The length of the parameter
209 | */
210 | public void setP(byte[] buffer, short offset, short length) {
211 | ((RSAPrivateCrtKey) key.getPrivate()).setP(buffer, offset, length);
212 | }
213 |
214 | /**
215 | * Sets the value of the PQ parameter. The plain text data format is
216 | * big-endian and right-aligned (the least significant bit is the least
217 | * significant bit of last byte). Input PQ parameter data is copied into the
218 | * internal representation.
219 | *
220 | * @param buffer
221 | * The input buffer
222 | * @param offset
223 | * The offset into the input buffer at which the parameter value
224 | * begins
225 | * @param length
226 | * The length of the parameter
227 | */
228 | public void setPQ(byte[] buffer, short offset, short length) {
229 | ((RSAPrivateCrtKey) key.getPrivate()).setPQ(buffer, offset, length);
230 | }
231 |
232 | /**
233 | * Sets the value of the Q parameter. The plain text data format is
234 | * big-endian and right-aligned (the least significant bit is the least
235 | * significant bit of last byte). Input Q parameter data is copied into the
236 | * internal representation.
237 | *
238 | * @param buffer
239 | * The input buffer
240 | * @param offset
241 | * The offset into the input buffer at which the parameter value
242 | * begins
243 | * @param length
244 | * The length of the parameter
245 | */
246 | public void setQ(byte[] buffer, short offset, short length) {
247 | ((RSAPrivateCrtKey) key.getPrivate()).setQ(buffer, offset, length);
248 | }
249 | }
250 |
--------------------------------------------------------------------------------