Package rsidlib

Source Code of rsidlib.RSIDCard

/*
* rsidlib: Open source library for PKI functions on Serbian eID (GNU LGPLv3)
* Copyright (C) 2011 Aleksandar Nikolic
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version
* 3.0 as published by the Free Software Foundation.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, see
* http://www.gnu.org/licenses/.
*/

package rsidlib;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;


/**
* Klasa za rukovanje elektronskom licnom kartom.
* @author Aleksandar Nikolic
*
*/
@SuppressWarnings("restriction")
public class RSIDCard {

  /**
   * Terminal na kom se nalazi elektronska licna  karta
   */
  private CardTerminal terminal;
  private Card card = null;
  private CardChannel channel = null;
 
 
  /**
   * Podaci o dokumentu
   */
    private static byte[] DOCUMENT_FILE  = {0x0F, 0x02};
    /**
     *  Licni podaci
     */
    private static byte[] PERSONAL_FILE  = {0x0F, 0x03};
    /**
     * Podaci o mestu prebivalista
     */
    private static byte[] RESIDENCE_FILE = {0x0F, 0x04}; // Podaci o mestu prebivalista
    /**
     * Licna slika u JPEG formatu
     */
    private static byte[] PHOTO_FILE     = {0x0F, 0x06}; // Personal photo in JPEG format
    /**
     * Kvalifikovani javni sertifikat
     */
    private static byte[] QUALIFIED_CERTIFICATE = {0x0F, 0x10}; // Public X.509 certificate for qualified (Non Repudiation) signing
    /**
     * Nekvalifikovani, standardni, sertifikat
     */
    private static byte[] STANDARD_CERTIFICATE  = {0x0F, 0x08}; // Public X.509 certificate for standard signing
    private static byte[] FIRST_UNKNOWN = {0x0F, (byte) 0xA3}; // cita se pre VERIFY
    /**
     * Enkriptovani pin i tajni kljuc za dekriptovanje privatnog kljuca
     */
    private static byte[] ENCRYPTED_PIN = {0x0F, 0x13}; // drugi se cita ppre VERIFY
    private static byte[] XOR_VALUE = {0x0F, (byte) 0xA1}; // treci se cita pre VERIFY
    /**
     * Podaci privatnog kljuca
     */
    public byte[] PRIVATE_KEY = {0x0F, 0x09}; // unknown , selektuje ga pri potpisivanju
 
    private static final int BLOCK_SIZE = 0xFF;
 
    /**
     * Konstruise RSIDCard objekat vezan za dati terminal
     * @param terminal
     */
    public RSIDCard(CardTerminal terminal){
    this.terminal = terminal;
    }
   
    /**
     * Povezivanje sa karticom
     * @throws CardException
     */
    private void connect() throws CardException
    {
      card = terminal.connect("*");
      channel = card.getBasicChannel();
    }
   
    /**
     * Kraj rada sa karticom
     * @throws CardException
     */
    private void disconnect() throws CardException
    {
      card.disconnect(false);
      card = null;
    }
   
    /**
     * Selektovanje elementarnog fajla po oznaci
     * @param name
     * @throws CardException
     */
    private void selectFile(byte[] name) throws CardException
    {
    ResponseAPDU r = channel.transmit(new CommandAPDU(0x00, 0xA4, 0x08, 0x00, name));
    if(r.getSW() != 0x9000) {
      throw new CardException("Select failed: " + RSIDUtils.int2Hex(r.getSW()));
    }
    }
   
    /**
     * Citanje dela selektovanog elementarnog fajla
     * @param offset
     * @param length
     * @return niz procitanih bajtova
     * @throws CardException
     */
    private byte[] readBinary(int offset, int length) throws CardException
    {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        while(length > 0)
        {
          int block = Math.min(length, BLOCK_SIZE);
            ResponseAPDU r = channel.transmit(new CommandAPDU(0x00, 0xB0, offset >> 8, offset & 0xFF, block));
            if(r.getSW() != 0x9000) {
              throw new CardException("Read binary failed: " + RSIDUtils.int2Hex(r.getSW()));
            }

            try {
                byte[] data = r.getData();
                int data_len = data.length;

                out.write(data);
              offset += data_len;
              length -= data_len;
      } catch (IOException e) {
        throw new CardException("Read binary failed: Could not write byte stream");
      }
        }

        return out.toByteArray();
    }

    /**
     * Citanje celog elementarnog fajla
     * @param name
     * @return
     * @throws CardException
     */
    private byte[] readElementaryFile(byte[] name) throws CardException
    {
      selectFile(name);

      // Read first 6 bytes from the EF
    byte[] header = readBinary(0, 6);

    // Missing files have header filled with 0xFF
    int i = 0;
    while(i < header.length && header[i] == 0xFF) i++;
    if(i == header.length) {
      throw new CardException("Read EF file failed: File header is missing");
    }
   
    // Total EF length: data as 16bit LE at 4B offset
    int length = ((0xFF&header[5])<<8) + (0xFF&header[4]);
     
    // Read binary into buffer
    return readBinary(6, length);
    }
   
   
    /**
     * Potpisivanje prosledjenih podataka standardnim privatnim kljucem sa
     * elektronske licne karte gradjana.
     * Koraci:
     * 1. Dekriptovanje PINa za pristup kartici i tajnog kljuca za dekripciju privatnog kljuca
     * 2. Autorizacija korisnika ekriptovanim PINom
     * 3. Citanje privatnog kljuca sa kartice
     * 4. Potpisivanje podataka
     *
     * @param data - podaci koji se potpisuju
     * @param password - lozinka kartice
     * @return
     * @throws RSIDCardException
     */
    public byte[] SignData(byte[] data, String password) throws RSIDCardException{
     
      // prvi korak : dekriptovanje pina i tajnog kljuca
      String id;
      byte[] signedData  = null;
      HashMap<Integer, byte[]> document = null;
      try {
      connect();
    } catch (CardException e2) {
      throw new RSIDCardException("Greska pri povezivanju na karticu");
    }
    try {
      document = RSIDUtils.parseTLV(readElementaryFile(DOCUMENT_FILE));
    } catch (CardException e) {
      System.out.println("Could not parse DOCUMENT_FILE" + e.getMessage());
    }
    if(document == null){
      throw new RSIDCardException("Greska pri citanju podataka o licnoj karti");
    }
      id = RSIDUtils.bytes2UTFString(document.get(10));
     
      /* SHA1 hash vrednost xorKey ucestvuje u dekripciji PINa za pristup kartici i
       * dekripciji tajnog kljuca za dekriptovanje privatnog kljuca.
       */
       String xorKey =  "ID" + id + '\u0001' + password;
       byte[] xorKeyBytes = xorKey.getBytes();
      
         MessageDigest md = null;
         try {
         md = MessageDigest.getInstance("SHA-1");
       } catch (NoSuchAlgorithmException e) {
         e.printStackTrace();
       }
      
         byte[] xorKeyHash;
         md.update(xorKeyBytes, 0, xorKeyBytes.length);
         xorKeyHash = md.digest();
         byte[] encryptedPinAndSecretKey = null;
         /*
          * ENCRYPTED_PIN elementarni fajl sadrzi enkriptovani PIN duzine 8 fajta pocevsi od cetvrtog bajta.
          * Na ofsetu 17 se nalazi tajni kljuc za dekriptovanje privatnog kljuca. Duzina tajnog kljuca je 32 bajta.
          * ----------------------------------------------------------------------------------------------------
          * |tlv tag(4 bajta)| enkriptovani pin (8 bajta)|tlv tag (4 bajta)| enkriptovani tajni kljuc (32 bajta)|
          * ----------------------------------------------------------------------------------------------------
          */
         try {
      encryptedPinAndSecretKey = readElementaryFile(ENCRYPTED_PIN);
    } catch (CardException e) {
      throw new RSIDCardException("Greska pri citanju enkriptovanig pina i tajnog kljuca.");
    }
   
    // enkriptvotani pin
    byte[] encryptedPin = new byte[8];
    // enkriptovatni tajni kljuc
    byte[] encryptedSecretKey = new byte[32];
   
    System.arraycopy(encryptedPinAndSecretKey, 4, encryptedPin, 0, 8);
    System.arraycopy(encryptedPinAndSecretKey, 17, encryptedSecretKey, 0, 32);
   
    byte[] xorValueWhole = null;
    try {
      xorValueWhole = readElementaryFile(XOR_VALUE);
    } catch (CardException e) {
      throw new RSIDCardException("Greska pri citanju podataka za dekriptovanje PINa!");
    }
     
    // dekriptovani pin
    byte[] pin = rsidXorDecryptPIN(encryptedPin, xorKeyHash, xorValueWhole);
    // dekriptovani tajni kljuc
    byte[] secretKey = rsidXorDecryptSecretKey(encryptedSecretKey, xorKeyHash, xorValueWhole);
    // drugi korak: autorizacija korisnika
   
    verifyUser(pin);
    // treci korak: citanje i dekripcija privatnog kljuca
    RSAPrivateCrtKeySpec privateKeySpec = getPrivateKey(secretKey);
    // cetvrti korak: potpisivanje podataka
        KeyFactory factory;

        try {
            factory = KeyFactory.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
       
        PrivateKey privateKey = null;
    try {
      privateKey = factory.generatePrivate(privateKeySpec);
    } catch (InvalidKeySpecException e1) {
      e1.printStackTrace();
    }
       
    if(privateKey == null){
      throw new RSIDCardException("Greska pri generisanju privatnog kljuca!");
    }
   
    Signature sig;
    try {
      sig = Signature.getInstance("SHA1withRSA");
      //inicijalizacija privatnim kljucem
      sig.initSign(privateKey);
      //postavljamo podatke koje potpisujemo
      sig.update(data);
     
      signedData = sig.sign();

    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (InvalidKeyException e) {
      e.printStackTrace();
    } catch (SignatureException e) {
      e.printStackTrace();
   
      if(signedData == null){
        throw new RSIDCardException("Greska pri potpisivanju podataka!");
      }
      try {
      disconnect();
    } catch (CardException e) {
      throw new RSIDCardException("Greska pri oslobadjanju kartice.");
    }
      return signedData;
    }

    public boolean verifySignature(byte[] signature, byte[] data) throws RSIDCardException{
     
      try {
      connect();
    } catch (CardException e2) {
      throw new RSIDCardException("Greska pri povezivanju na karticu");
    }
    // citamo sertifikat zbog podataka o javnom kljucu
    byte[] standardFileBytes;
    try {
      standardFileBytes = readElementaryFile(STANDARD_CERTIFICATE);
    } catch (CardException e) {
      throw new RSIDCardException("Greska pri citanju standardnog sertifikata!");
    }
    byte[] certificateBytes = new byte[standardFileBytes.length];
    System.arraycopy(standardFileBytes, 4, certificateBytes, 0, standardFileBytes.length-4);
    ByteArrayInputStream certificateInputStream = new ByteArrayInputStream(certificateBytes);
    CertificateFactory cf = null;
    try {
      cf = CertificateFactory.getInstance("X.509");
    } catch (CertificateException e) {
      e.printStackTrace();
    }
    X509Certificate x509Cert = null;
    try {
      x509Cert = (X509Certificate) cf.generateCertificate(certificateInputStream);
    } catch (CertificateException e) {
      e.printStackTrace();
    }
   
    // javni kljuc se sastoji od modula i javnog eksponenta u odgovarajucoj ASN1 strukturi
    ASN1InputStream is = new ASN1InputStream(x509Cert.getPublicKey().getEncoded());
      DERObject obj = null;
    try {
      obj = is.readObject();
    } catch (IOException e) {
      e.printStackTrace();
    }
      ASN1Sequence seq = (ASN1Sequence)obj;
      RSAPublicKeyStructure pubk = null;
      SubjectPublicKeyInfo pkInfo = SubjectPublicKeyInfo.getInstance(seq);
      try {
       pubk = RSAPublicKeyStructure.getInstance(pkInfo.getPublicKey());
    } catch (IOException e) {
      e.printStackTrace();
    }

    RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(pubk.getModulus(), pubk.getPublicExponent());
        KeyFactory factory;

        try {
            factory = KeyFactory.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
       
    PublicKey publicKey = null;
    try {
      publicKey = factory.generatePublic(rsaPubKey);
    } catch (InvalidKeySpecException e) {
      e.printStackTrace();
    }
    Signature sig = null;
    try {
      sig = Signature.getInstance("SHA1withRSA");
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }   
    try {
      sig.initVerify(publicKey);
    } catch (InvalidKeyException e) {
      e.printStackTrace();
    }
    try {
      sig.update(data);
    } catch (SignatureException e) {
      e.printStackTrace();
    }
       
    boolean res = false;
    try {
      res = sig.verify(signature);
    } catch (SignatureException e) {
      e.printStackTrace();
    }
    try {
      disconnect();
    } catch (CardException e) {
      e.printStackTrace();
    }
      return res;
    }
    /**
     * Dekripcija i rekonstrukcija privatnog kljuca.
     * Privatni kljuc je enkriptovan AESom sa tajnim kljucem od 256 bita u CFB modu.
     * Inicijalizacioni vektor za CFB mod su prvih 16 bajta SHA1 hasha tajnog kljuca
     * U dekriptovanim podacima privatni kljuc se nalazi u ASN1 strukturi nakon TLV
     * polja od 4 bajta. ASN1 struktura sadrzi prvi prosti faktor, drugi prosti faktor,
     * eksponent prvog prostog faktora, eksponent drugog prostog faktora i koeficijent
     * za teoremu o kineskom ostatku.
     * @param secretKey
     * @return
     * @throws RSIDCardException
     */
    private RSAPrivateCrtKeySpec getPrivateKey(byte[] secretKey) throws RSIDCardException{

      // Enkriptovani privatni kljuc
      byte[] encryptedPrivateKey = null;
      try {
      encryptedPrivateKey = readElementaryFile(PRIVATE_KEY);
    } catch (CardException e) {
      throw new RSIDCardException("Greska pri citanju enkriptovanog privatnog kljuca.");
    }
   
    // Privatni kljuc je enkriptovan AESom sa tajnim kljucem od 256 bita u CFB modu
        Security.addProvider(new BouncyCastleProvider());
        SecretKeySpec                     key;
        Cipher                  cipher = null;
       
        // zbog CFB moda potreban nam je IV
        byte[] iv = new byte[16];

        //Inicijalizacioni vektor za CFB mod su prvih 16 bajta SHA1 hasha tajnog kljuca
        MessageDigest md = null;
        try {
        md = MessageDigest.getInstance("SHA-1");
      } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
      }
        byte[] sha1hash = new byte[40];
        md.update(secretKey, 0, secretKey.length);
        sha1hash = md.digest();
        System.arraycopy(sha1hash, 0, iv, 0, 16);

        //postavljanje IVa
        IvParameterSpec ivSpec = new IvParameterSpec(iv);

        // Postavljanje tajnog kljuca za dekripciju
        key = new SecretKeySpec(secretKey, "AES");
       
        // Instanciranje AES sifrara u CFB modu
        try {
      cipher = Cipher.getInstance("AES/CFB/NoPadding", "BC");
    } catch (NoSuchAlgorithmException e1) {
      e1.printStackTrace();
    } catch (NoSuchProviderException e1) {
      e1.printStackTrace();
    } catch (NoSuchPaddingException e1) {
      e1.printStackTrace();
    }
   
    // inicijalizacija AESa tajnim kljucem i inicijalizacionim vektorom
        try {
      cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
    } catch (InvalidKeyException e) {
      e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
      e.printStackTrace();
    }
   
    // DEKRIPCIJA
        byte[] plainText = new byte[cipher.getOutputSize(encryptedPrivateKey.length)];

        int ptLength = 0;
    try {
      ptLength = cipher.update(encryptedPrivateKey, 0, encryptedPrivateKey.length, plainText, 0);
    } catch (ShortBufferException e) {
      e.printStackTrace();
    }
       
        try {
      ptLength += cipher.doFinal(plainText, ptLength);
    } catch (IllegalBlockSizeException e) {
      e.printStackTrace();
    } catch (ShortBufferException e) {
      e.printStackTrace();
    } catch (BadPaddingException e) {
      e.printStackTrace();
    }
    // u dekriptovanim podacima privatni kljuc se nalazi u ASN1 strukturi nakon TLV polja od 4 bajta

    byte[] decryptedPrivateKey = new byte[plainText.length];
    System.arraycopy(plainText, 4, decryptedPrivateKey, 0, plainText.length-4);
       
    RSAPrivateCrtKeySpec privKeySpec = reconstructPrivateKey(decryptedPrivateKey);
    if(privKeySpec == null){
      throw new RSIDCardException("Greska pri rekonstrukciji privatnog kljuca.");
    }
   
    return privKeySpec;
    }
   
    /**
     * Rekonstrukcija privatnog kljuca iz dekriptovanih podataka.
     * Podaci se nalaze unutar sledece ASN1 sturkture:
     * PrivateKey ::= SEQUENCE {
   *      primeP BITSTRING,
   *      primeQ BITSTRING,
   *      primeExponentP BITSTRING,
   *      primeExponentQ BITSTRING,
   *       crtCoeficient  BITSTRING
   *  }
   * Privatni kljuc je u obliku spremnom za potpisivanje koriscenjem teoreme
   * kineskog ostatka, stoga privatni eksponent nije potreban.
     * @param privateKey
     * @return
     */
  private RSAPrivateCrtKeySpec reconstructPrivateKey(byte[] privateKey){
   
    RSAPrivateCrtKeySpec privKeySpec = null;
    ASN1InputStream asn1InputStream = new ASN1InputStream(privateKey);
    //parsiranje ASN1 strukture
    ArrayList<BigInteger> privateKeyParts = new ArrayList<BigInteger>();
    try {
      DERObject derObject = asn1InputStream.readObject();
      ASN1Sequence asn1Seq = (ASN1Sequence) derObject;
     
      Enumeration<DERObject> en = asn1Seq.getObjects();
      while(en.hasMoreElements()){
        DERObject obj = en.nextElement();
        DERBitString bitString = DERBitString.getInstance(obj);
        byte[] prependZero = new byte[bitString.getBytes().length+1];
        // zbog "cudnog" BigInteger konstruktora mora se dodati 0x0 na pocetak,
        // u suprotnom u pojedinim slucajevima pogresno se protumaci znak i brojevi ispadnu negativni
        prependZero[0] = 0x0;
        System.arraycopy(bitString.getBytes(), 0, prependZero, 1, bitString.getBytes().length);
        privateKeyParts.add(new BigInteger(prependZero));
      }

      // citamo sertifikat zbog podataka o javnom kljucu
      byte[] standardFileBytes = readElementaryFile(STANDARD_CERTIFICATE);
      byte[] certificateBytes = new byte[standardFileBytes.length];
      System.arraycopy(standardFileBytes, 4, certificateBytes, 0, standardFileBytes.length-4);
      ByteArrayInputStream certificateInputStream = new ByteArrayInputStream(certificateBytes);
      CertificateFactory cf = CertificateFactory.getInstance("X.509");
      X509Certificate x509Cert = (X509Certificate) cf.generateCertificate(certificateInputStream);
     
      // javni kljuc se sastoji od modula i javnog eksponenta u odgovarajucoj ASN1 strukturi
      ASN1InputStream is = new ASN1InputStream(x509Cert.getPublicKey().getEncoded());
        DERObject obj = is.readObject();
        ASN1Sequence seq = (ASN1Sequence)obj;
        SubjectPublicKeyInfo pkInfo = SubjectPublicKeyInfo.getInstance(seq);
        RSAPublicKeyStructure pubk = RSAPublicKeyStructure.getInstance(pkInfo.getPublicKey());
        BigInteger expo = pubk.getPublicExponent();
        BigInteger mod = pubk.getModulus();
       
        // sada imamo sve potrebne podatke za privatni kljuc
      privKeySpec = new RSAPrivateCrtKeySpec(mod,
                          expo,
                          null,
                          privateKeyParts.get(0),
                          privateKeyParts.get(1),
                          privateKeyParts.get(2),
                          privateKeyParts.get(3),
                          privateKeyParts.get(4));
    } catch (IOException e) {
      e.printStackTrace();
    } catch (CertificateException e) {
      e.printStackTrace();
    } catch (CardException e) {
      e.printStackTrace();
    }
   
    return privKeySpec;
  }
    /**
     * Verifikacija korisnika PINom pomocu VERIFY APDU komande
     * {@link http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_6_basic_interindustry_commands.aspx#chap6_12}
     * @param pin
     * @throws RSIDCardException
     */
    private void verifyUser(byte[] pin) throws RSIDCardException{
       
      final byte CLA = 0x00;
        final byte INS = 0x20;
        final byte P1  = 0x00;
        final byte P2 =  0x01;
        final byte LC  = 0x08;
      byte[] cmd = new byte[14];
     
      cmd[0] = CLA;
      cmd[1] = INS;
      cmd[2] = P1;
      cmd[3] = P2;
      cmd[4] = LC;
      System.arraycopy(pin, 0, cmd, 5, 8);
      cmd[13] 0x00;
      ResponseAPDU r = null;
    try {
      r = channel.transmit(new CommandAPDU(cmd));
    } catch (CardException e) {
      throw new RSIDCardException("Greska pri autorizaciji korisnika. Pogresna loznika?");
    }
    if(r.getSW() != 0x9000) {
      throw new RSIDCardException("Greska pri autorizaciji korisnika. Pogresna loznika?");
    }
    }
   
    /**
    *Dekriptovanje podataka se vrsi u dva koraka:
   * 1. Enkriptovani podaci (encryptedData) se XORuju sa xorValue
   * 2. Rezultat se XORuje sa xorKeyHash, tj hash vrednosti xorKey-a
   * u koji ulazi korisnikova lozinka.
   * Rezultat drugog koraka su dekriptovani dekriptovani pocaci.
   * U slucaju da su podaci duzi od kljuca za XOR, kljuc se koristi ciklicno.
     * @param encryptedData  - enkriptovani pin
     * @param xorKeyHash - SHA1 hash stringa u koji ulazi lozinka
     * @param xorValueWhole - XOR_VALUE podaci
     * @return - dekriptovani podaci
     */
    private byte[] rsidXorDecryptPIN(byte[] encryptedPIN, byte[] xorKeyHash, byte[] xorValueWhole){
    // ucestvuje kao XOR kljuc u prvom delu dekriptovanja PINa i kljuca
    byte[] xorValue = new byte[xorValueWhole.length];
    System.arraycopy(xorValueWhole, 4, xorValue, 0,8);

    /* dekriptovanje PINa se vrsi u dva koraka:
     * 1. Enkriptovani pin (encryptedPin) se XORuje sa xorValue
     * 2. Rezultat se XORuje sa prvih 8 bajta xorKeyHash, tj hash vrednosti xorKey-a
     * u koji ulazi korisnikova lozinka.
     * Rezultat drugog koraka je dekriptovani PIN.
     */
   
    // prvi korak
    byte[] xoredPIN = new byte[8];
    for(int i = 0; i<8;i++){
      xoredPIN[i] = (byte) (xorValue[i] ^ encryptedPIN[i]);
    }
   
    //drugi korak
    // nakon ovoga imamo dekriptovani PIN
    byte[] decryptedData = new byte[xoredPIN.length];
    for(int i = 0 ; i<8; i++){
     
      decryptedData[i] = (byte) (xoredPIN[i] ^ xorKeyHash[i]);
    }
      return decryptedData;
    }
   
    /**
     * Slicno kao rsidXorDecryptPIN samo umesto 8 radi sa 16 bajta
     * @param encryptedSecretKey
     * @param xorKeyHash
     * @param xorValueWhole
     * @return
     */
    private byte[] rsidXorDecryptSecretKey(byte[] encryptedSecretKey, byte[] xorKeyHash, byte[] xorValueWhole){
    // ucestvuje kao XOR kljuc u prvom delu dekriptovanja PINa i kljuca
    byte[] xorValue = new byte[xorValueWhole.length];
    System.arraycopy(xorValueWhole, 4, xorValue, 0, 16);

    /* dekriptovanje PINa se vrsi u dva koraka:
     * 1. Enkriptovani pin (encryptedPin) se XORuje sa xorValue
     * 2. Rezultat se XORuje sa prvih 8 bajta xorKeyHash, tj hash vrednosti xorKey-a
     * u koji ulazi korisnikova lozinka.
     * Rezultat drugog koraka je dekriptovani PIN.
     */
   
    // priv korak
    byte[] xoredPIN = new byte[32];
    for(int i = 0; i<32;i++){
      xoredPIN[i] = (byte) (xorValue[i%16] ^ encryptedSecretKey[i]);
    }
   
    //drugi korak
    // nakon ovoga imamo dekriptovani PIN
    byte[] decryptedData = new byte[32];
    for(int i = 0 ; i<32; i++){
     
      decryptedData[i] = (byte) (xoredPIN[i] ^ xorKeyHash[i%16]);
    }
      return decryptedData;
    }
    /**
     * Test metoda
     * @param args
     */
    public static void main(String[] args){
     
      if(args.length != 1){
        System.out.println("Lozinka mora biti prvi argument!");
        System.exit(0);
      }
      String password =  args[0];
      TerminalFactory factory = TerminalFactory.getDefault();
        List<CardTerminal> terminals = null;
    try {
      terminals = factory.terminals().list();
    } catch (CardException e) {
      e.printStackTrace();
    }
        CardTerminal terminal = terminals.get(0);
      RSIDCard rsidCard = new RSIDCard(terminal);
     
    byte[] dataToSign = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};
    byte[] signedData = null;
    try {
      signedData = rsidCard.SignData(dataToSign, password);
    } catch (RSIDCardException e) {
      System.out.println(e.getMessage());
      e.printStackTrace();
      System.exit(0);
    }
    System.out.println("Data to sign: " + RSIDUtils.bytes2Hex(dataToSign));
    System.out.println("Signed data: " + RSIDUtils.bytes2Hex(signedData));
    try {
      System.out.println("Verifying signature... " + rsidCard.verifySignature(signedData, dataToSign) );
    } catch (RSIDCardException e) {
      System.out.println(e.getMessage());
      e.printStackTrace();
      System.exit(0);
    }
    }
}


@SuppressWarnings("serial")
class RSIDCardException extends Exception{
 
  public RSIDCardException(String msg) {
    super(msg);
  }
}


TOP

Related Classes of rsidlib.RSIDCard

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.