JavaCard
Il concetto fondamentale è che la card è un server che può solo rispondere a richieste inviatele da un host. Normalmente non dispongono di batterie, quindi non hanno neanche modo di tenere traccia del trascorrere del tempo... a meno che non gli venga "detto" dall'host. Inoltre dispongono solo di una linea seriale (spesso non particolarmente veloce) per interfacciarsi all'esterno.
Quindi perché utilizzarle?
Semplice: perché una cosa la possono fare bene: difendere i segreti che gli vengono affidati!
Bene, dopo le ovvietà di rito, vediamo come programmarle, che è la cosa che mi ha fatto perdere più tempo.
Intanto bisogna cercare un fornitore di card, meglio se compatibili con GlobalPlatform. Io ho acquistato alcune SmartCafe Expert 3.2 144k e JCOP 41 v2.3.1. Entrambi i tipi di card richiedono l'uso di JavaCard Development Kit 2.2.1 (non una versione più recente!) o l'invio dell'applet fallirà! Altre card richiedono versioni più recenti e supportano più facilmente "grandi" trasferimenti (queste gestiscono solo pacchetti fino a 256 byte).
L'applet può essere compilata con un JDK anche non Sun (ora Oracle): io ho utilizzato OpenJDK 6. Può essere importante usare Java 6 (1.6) e non una versione più recente anche del JDK, ma non ho provato.
Un bell'esempio di applet è JMRTD: basta sistemare un paio di righe del makefile per adattare JAVA_HOME e JCPATH alla propria configurazione e la compilazione andrà bene.
Per il caricamento sulla card, JMRTD fornisce uno script per gpshell. E qui iniziano i problemi: gpshell non l'ho trovato pacchettizzato, quindi va compilato.
Gpshell richiede GlobalPlatform 6 o successivo, ma questo è pacchettizzato solo per Ubuntu fino alla 11.10, quindi anche lui va compilato. In più, già che il compilatore è caldo, tanto vale compilare anche gppcscconnectionplugin altrimenti si avrà un errore più tardi. Qualche altro pacchetto (libpcsclite-dev, per esempio) potrebbe essere richiesto ma verrà indicato dal fallimento di make.
Il caricamento sulla carta JCOP va tranquillamente a buon fine con lo script fornito.
Per la SmartCafe invece è necessaria una modifica alla linea open_sc :
open_sc -scp 2 -scpimpl 0x15 -security 3 -keyind 0 -keyver 0 -key 404142434445464748494a4b4c4d4e4f -keyDerivation emvcps11
come chiaramente indicato da Alexander Petric nel suo blog.
Purtroppo, malgrado la modifica, JMRTD non può essere caricato su questa card perché manca il supporto HW per ECC.
Un Capability detector può essere molto utile per capire cosa è esattamente supportato dalle card che si hanno o che si devono usare. Volevo realizzarlo io, ma trovarlo già pronto è più comodo...
Purtroppo l'app Java da far girare sul PC mi ha dato qualche problema, ma questo mi ha permesso di capire meglio come funziona gpshell: per vedere le risposte a send_apdu
è necessario avere enable_trace
abilitato!
La mia card JCOP ha ATR 3B FA 18 00 FF 81 31 FE 45 4A 43 4F 50 34 31 56 32 33 31 63
e permette l'accesso sia contact, che contactless che USB con un semplice connettore (anche se non ho ancora capito se i livelli elettrici sono gli stessi o se serve un adattatore di livello).
Questo è lo script utilizzato per caricare l'applet e lanciare un test (più di uno potrebbe facilmente fallire per esaurimento della memoria):
mode_211 establish_context card_connect select -AID a000000003000000 open_sc -security 1 -keyind 0 -keyver 0 -mac_key 404142434445464748494a4b4c4d4e4f -enc_key 404142434445464748494a4b4c4d4e4f // Open secure channel delete -AID 6D7970616330303031 delete -AID 6D797061636B616731 install -file AlgTest.cap -priv 00 -nvDataLimit 8000 -AID 6D7970616330303031 -pkgAID 6D797061636B616731 select -AID 6D7970616330303031 enable_trace # needed to see answers!!! send_apdu -sc 0 -APDU b072010000f0 send_apdu -sc 0 -APDU b072020000f0 send_apdu -sc 0 -APDU b072030000f0 send_apdu -sc 0 -APDU b072040000f0 send_apdu -sc 0 -APDU b072050000f0
(il comando 72 va eseguito in 5 passi, a differenza degli altri che possono essere lanciati lasciando P1 a 00).
Ed ecco i risultati, decodificati e commentati.
Comando 0x70 (TestSupportedModes):
11 Class javacardx.crypto.Cipher 01 ALG_DES_CBC_NOPAD 01 ALG_DES_CBC_ISO9797_M1 01 ALG_DES_CBC_ISO9797_M2 00 ALG_DES_CBC_PKCS5 01 ALG_DES_ECB_NOPAD 01 ALG_DES_ECB_ISO9797_M1 01 ALG_DES_ECB_ISO9797_M2 00 ALG_DES_ECB_PKCS5 00 ALG_RSA_ISO14888 01 ALG_RSA_PKCS1 00 ALG_RSA_ISO9796 01 ALG_RSA_NOPAD 01 ALG_AES_BLOCK_128_CBC_NOPAD 01 ALG_AES_BLOCK_128_ECB_NOPAD 00 ALG_RSA_PKCS1_OAEP 00 ALG_KOREAN_SEED_ECB_NOPAD 00 ALG_KOREAN_SEED_CBC_NOPAD 12 Class javacard.security.Signature 00 ALG_DES_MAC4_NOPAD 01 ALG_DES_MAC8_NOPAD 00 ALG_DES_MAC4_ISO9797_M1 01 ALG_DES_MAC8_ISO9797_M1 00 ALG_DES_MAC4_ISO9797_M2 01 ALG_DES_MAC8_ISO9797_M2 00 ALG_DES_MAC4_PKCS5 00 ALG_DES_MAC8_PKCS5 01 ALG_RSA_SHA_ISO9796 01 ALG_RSA_SHA_PKCS1 01 ALG_RSA_MD5_PKCS1 00 ALG_RSA_RIPEMD160_ISO9796 00 ALG_RSA_RIPEMD160_PKCS1 00 ALG_DSA_SHA 00 ALG_RSA_SHA_RFC2409 00 ALG_RSA_MD5_RFC2409 01 ALG_ECDSA_SHA 01 ALG_AES_MAC_128_NOPAD 00 ALG_DES_MAC4_ISO9797_1_M2_ALG3 01 ALG_DES_MAC8_ISO9797_1_M2_ALG3 00 ALG_RSA_SHA_PKCS1_PSS 00 ALG_RSA_MD5_PKCS1_PSS 00 ALG_RSA_RIPEMD160_PKCS1_PSS 00 (JC2.2.2) ALG_HMAC_SHA1 00 (JC2.2.2) ALG_HMAC_SHA_256 00 (JC2.2.2) ALG_HMAC_SHA_384 00 (JC2.2.2) ALG_HMAC_SHA_512 00 (JC2.2.2) ALG_HMAC_MD5 00 (JC2.2.2) ALG_HMAC_RIPEMD160 00 (JC2.2.2) ALG_RSA_SHA_ISO9796_MR 00 (JC2.2.2) ALG_RSA_RIPEMD160_ISO9796_MR 00 (JC2.2.2) ALG_SEED_MAC_NOPAD 15 Class javacard.security.MessageDigest 01 ALG_SHA 01 ALG_MD5 00 ALG_RIPEMD160 00 ALG_SHA_256 00 ALG_SHA_384 00 ALG_SHA_512 16 Class javacard.security.RandomData 01 ALG_PSEUDO_RANDOM 01 ALG_SECURE_RANDOM 20 Class javacard.security.KeyBuilder B0 01 TYPE_DES_TRANSIENT_RESET 01 TYPE_DES_TRANSIENT_DESELECT 01 TYPE_DES, LENGTH_DES 01 TYPE_DES, LENGTH_DES3_2KEY 01 TYPE_DES, LENGTH_DES3_3KEY B1 01 TYPE_AES_TRANSIENT_RESET, LENGTH_AES_128 01 TYPE_AES_TRANSIENT_DESELECT, LENGTH_AES_128 01 TYPE_AES, LENGTH_AES_128 01 TYPE_AES, LENGTH_AES_192 01 TYPE_AES, LENGTH_AES_256 B2 01 TYPE_RSA_PUBLIC, LENGTH_RSA_512 01 TYPE_RSA_PUBLIC, LENGTH_RSA_736 01 TYPE_RSA_PUBLIC, LENGTH_RSA_768 01 TYPE_RSA_PUBLIC, LENGTH_RSA_896 01 TYPE_RSA_PUBLIC, LENGTH_RSA_1024 01 TYPE_RSA_PUBLIC, LENGTH_RSA_1280 01 TYPE_RSA_PUBLIC, LENGTH_RSA_1536 01 TYPE_RSA_PUBLIC, LENGTH_RSA_1984 01 TYPE_RSA_PUBLIC, LENGTH_RSA_2048 B3 01 TYPE_RSA_PRIVATE, LENGTH_RSA_512 01 TYPE_RSA_PRIVATE, LENGTH_RSA_736 01 TYPE_RSA_PRIVATE, LENGTH_RSA_768 01 TYPE_RSA_PRIVATE, LENGTH_RSA_896 01 TYPE_RSA_PRIVATE, LENGTH_RSA_1024 01 TYPE_RSA_PRIVATE, LENGTH_RSA_1280 01 TYPE_RSA_PRIVATE, LENGTH_RSA_1536 01 TYPE_RSA_PRIVATE, LENGTH_RSA_1984 01 TYPE_RSA_PRIVATE, LENGTH_RSA_2048 B4 01 TYPE_RSA_CRT_PRIVATE, LENGTH_RSA_512 00 TYPE_RSA_CRT_PRIVATE, LENGTH_RSA_736 01 TYPE_RSA_CRT_PRIVATE, LENGTH_RSA_768 01 TYPE_RSA_CRT_PRIVATE, LENGTH_RSA_896 01 TYPE_RSA_CRT_PRIVATE, LENGTH_RSA_1024 01 TYPE_RSA_CRT_PRIVATE, LENGTH_RSA_1280 01 TYPE_RSA_CRT_PRIVATE, LENGTH_RSA_1536 01 TYPE_RSA_CRT_PRIVATE, LENGTH_RSA_1984 01 TYPE_RSA_CRT_PRIVATE, LENGTH_RSA_2048 B5 00 TYPE_DSA_PRIVATE, LENGTH_DSA_512 00 TYPE_DSA_PRIVATE, LENGTH_DSA_768 00 TYPE_DSA_PRIVATE, LENGTH_DSA_1024 B6 00 TYPE_DSA_PUBLIC, LENGTH_DSA_512 00 TYPE_DSA_PUBLIC, LENGTH_DSA_768 00 TYPE_DSA_PUBLIC, LENGTH_DSA_1024 B7 01 TYPE_EC_F2M_PRIVATE, LENGTH_EC_F2M_113 01 TYPE_EC_F2M_PRIVATE, LENGTH_EC_F2M_131 01 TYPE_EC_F2M_PRIVATE, LENGTH_EC_F2M_163 01 TYPE_EC_F2M_PRIVATE, LENGTH_EC_F2M_193 B8 00 TYPE_EC_FP_PRIVATE, LENGTH_EC_FP_112 00 TYPE_EC_FP_PRIVATE, LENGTH_EC_FP_128 00 TYPE_EC_FP_PRIVATE, LENGTH_EC_FP_160 00 TYPE_EC_FP_PRIVATE, LENGTH_EC_FP_192 B9 00 TYPE_KOREAN_SEED_TRANSIENT_RESET, LENGTH_KOREAN_SEED_128 00 TYPE_KOREAN_SEED_TRANSIENT_DESELECT, LENGTH_KOREAN_SEED_128 00 TYPE_KOREAN_SEED, LENGTH_KOREAN_SEED_128 BA 00 TYPE_HMAC_TRANSIENT_RESET, LENGTH_HMAC_SHA_1_BLOCK_64 00 TYPE_HMAC_TRANSIENT_DESELECT, LENGTH_HMAC_SHA_1_BLOCK_64 00 TYPE_HMAC, LENGTH_HMAC_SHA_1_BLOCK_64 00 TYPE_HMAC, LENGTH_HMAC_SHA_256_BLOCK_64 00 TYPE_HMAC, LENGTH_HMAC_SHA_384_BLOCK_64 00 TYPE_HMAC, LENGTH_HMAC_SHA_512_BLOCK_64 18 Class javacard.security.KeyPair RSA 00 LENGTH_RSA_512 00 LENGTH_RSA_736 00 LENGTH_RSA_768 00 LENGTH_RSA_896 00 LENGTH_RSA_1024 00 LENGTH_RSA_1280 00 LENGTH_RSA_1536 00 LENGTH_RSA_1984 00 LENGTH_RSA_2048 19 Class javacard.security.KeyPair RSA_CRT 01 LENGTH_RSA_512 00 LENGTH_RSA_736 01 LENGTH_RSA_768 01 LENGTH_RSA_896 01 LENGTH_RSA_1024 01 LENGTH_RSA_1280 01 LENGTH_RSA_1536 01 LENGTH_RSA_1984 01 LENGTH_RSA_2048 1A Class javacard.security.KeyPair DSA 00 LENGTH_DSA_512 00 LENGTH_DSA_768 00 LENGTH_DSA_1024 1B Class javacard.security.KeyPair EC_F2M 01 LENGTH_EC_F2M_113 01 LENGTH_EC_F2M_131 01 LENGTH_EC_F2M_163 01 LENGTH_EC_F2M_193 1C Class javacard.security.KeyPair EC_FP 00 LENGTH_EC_FP_112 00 LENGTH_EC_FP_128 00 LENGTH_EC_FP_160 00 LENGTH_EC_FP_192 13 Class javacard.security.KeyAgreement 01 ALG_EC_SVDP_DH 01 ALG_EC_SVDP_DHC 17 Class javacard.security.Checksum 01 ALG_ISO3309_CRC16 00 ALG_ISO3309_CRC32 FF End of data 05...05 Filler 9000 => OK
Comando 0x7100 (TestAvailableMemory(RAM)):
07A4 => 1956 byte 9000 => OK
Comando 0x7101 (TestAvailableMemory(EEPROM)):
7530 \ 3A98 | 3A98 | 3A98 | 16B6 > Valori da sommare: totale 81194 byte! 017C | 0000 | 0000 | 0000 / 9000 => OK
Comando 0x7205 (TestRSAExponentSet, dopo aver dato in sequenza P1 da 01 a 04):
6CA8...309B Blocco criptato casuale 9000 => OK
Comando 0x73 (JCSYSTEMinfo):
0202 => JavaCard v2.2 01 => deletion supported 7FFF => 32767 byte persistenti 07B6 => 1974 byte transient select 07B6 => 1974 byte transient deselect 9000 => OK
Volendo semplificare veramente tanto, si può scaricare una VM già pronta per lo sviluppo!
Edit: Sono riuscito a far funzionare AlgTestJClient.jar
. A causa di una svista veniva tentata la connessione solo con T=0, fallendo se questo non era supportato. Fix rapidissimo che verrà incluso nella nuova release. Allego i file csv generati, contenenti anche un grossolano "benchmark" ed alcune annotazioni interessanti. I dati saranno inclusi anche sulla pagina di Petr Svenda.
Da notare che la memoria indicata dal Java client è minore di quella riportata dai miei test precedenti. Questo è da imputare al garbage collector, che "non fa" il suo lavoro (per meglio dire, il test viene eseguito quando il GC non ha ancora fatto il suo lavoro).
Altra cosa da notare: pare che la generazione delle chiavi RSA sia possibile solo con CRT (Chinese Remainder Theorem), ma questo, se da un lato velocizza molto le operazioni, dall'altro espone a fail attacks che possono portare alla compromissione della chiave segreta se non si verifica ogni operazione RSA. Vedi, per esempio un articolo sull'importanza del padding (en) che mostra anche l'attacco al CRT.
Come mi è già capitato di dire: fare sicurezza è un lavoro difficile!
La SmartCafé Expert, invece, può gestire senza CRT anche chiavi da 2048 bit, ma come ho già detto non supporta ECC.
Altro test che potrebbe essere interessante è quello dello spazio disponibile per le transazioni, per poter "assemblare" tanti piccoli trasferimenti come se fossero uno unico (per es. per caricare un certificato). Petr probabilmente lo includerà in AlgTest 1.2... No, forse nella versione 1.3...
Comunque sono finalmente riuscito a ricompilare l'applet e a reinviarla sulla JCOP41. Lo spazio per le transazioni è di 512 byte, decisamente insufficiente per l'uso che volevo farne...
Visto che volevo capire esattamente che comandi utilizzare, non ho utilizzato ant
come "normale" in Java, ma un Makefile usato quasi come script. Eccolo:
# Config section SOURCES=AlgTest.java APPLET_AID=0x6D:0x79:0x70:0x61:0x63:0x30:0x30:0x30:0x31 APPLET_NAME=AlgTest PACKAGE_AID=0x6D:0x79:0x70:0x61:0x63:0x6B:0x61:0x67:0x31 PACKAGE_NAME=AlgTest PACKAGE_VERSION=1.0 JAVA_HOME=/usr #JAVA_HOME=/home/ndk/t/jdk1.7.0_15 JC_HOME=/home/ndk/java_card_kit-2_2_1 BUILD_DIR = ./build BUILD_CLASSES_DIR = $(BUILD_DIR)/classes BUILD_JAVACARD_DIR = $(BUILD_DIR)/javacard JAVACARD_EXPORT_DIR = $(JC_HOME)/api_export_files CLASSPATH = $(JC_HOME)/lib/api.jar # Note that -g is needed for convert tool! JFLAGS = -target 1.1 -source 1.3 -g -d $(BUILD_CLASSES_DIR) -classpath $(CLASSPATH) JC = javac .SUFFIXES: .java .class .java.class: mkdir -p $(BUILD_CLASSES_DIR) mkdir -p $(BUILD_JAVACARD_DIR) $(JC) $(JFLAGS) $*.java JAVA_HOME=$(JAVA_HOME) $(JC_HOME)/bin/converter \ -d $(BUILD_JAVACARD_DIR) \ -classdir $(BUILD_CLASSES_DIR) \ -exportpath $(JAVACARD_EXPORT_DIR) \ -applet $(APPLET_AID) $(APPLET_NAME) \ $(PACKAGE_NAME) $(PACKAGE_AID) $(PACKAGE_VERSION) default: classes classes: $(SOURCES:.java=.class) clean: $(RM) -rf $(BUILD_DIR)