001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.security.jceadapter; 020 021import org.javatuples.Pair; 022import org.jpos.core.Configuration; 023import org.jpos.core.ConfigurationException; 024import org.jpos.core.Environment; 025import org.jpos.iso.ISODate; 026import org.jpos.iso.ISOException; 027import org.jpos.iso.ISOUtil; 028import org.jpos.security.*; 029import org.jpos.util.LogEvent; 030import org.jpos.util.Logger; 031import org.jpos.util.SimpleMsg; 032 033import javax.crypto.SecretKey; 034import java.io.BufferedInputStream; 035import java.io.BufferedOutputStream; 036import java.io.File; 037import java.io.FileInputStream; 038import java.io.FileOutputStream; 039import java.io.InputStream; 040import java.io.OutputStream; 041import java.nio.ByteBuffer; 042import java.security.*; 043import java.util.*; 044import java.util.regex.Matcher; 045import java.util.regex.Pattern; 046import javax.crypto.Cipher; 047 048/** 049 * JCESecurityModule is an implementation of a security module in software. 050 * <p> 051 * It doesn't require any hardware device to work.<br> 052 * JCESecurityModule also implements the SMAdapter, so you can view it: either 053 * as a self contained security module adapter that doesn't need a security module 054 * or a security module that plugs directly to jpos, so doesn't need 055 * a separate adapter.<br> 056 * It relies on Java(tm) Cryptography Extension (JCE), hence its name.<br> 057 * JCESecurityModule relies on the JCEHandler class to do the low level JCE work. 058 * <p> 059 * WARNING: This version of JCESecurityModule is meant for testing purposes and 060 * NOT for life operation, since the Local Master Keys are stored in CLEAR on 061 * the system's disk. Comming versions of JCESecurity Module will rely on 062 * java.security.KeyStore for a better protection of the Local Master Keys. 063 * 064 * @author Hani Samuel Kirollos 065 * @author Robert Demski 066 * @version $Revision$ $Date$ 067 */ 068@SuppressWarnings("unchecked") 069public class JCESecurityModule extends BaseSMAdapter<SecureDESKey> { 070 /** 071 * Pattern representing key type string value. 072 */ 073 private static final Pattern KEY_TYPE_PATTERN = Pattern.compile("([^:;]*)([:;])?([^:;])?([^:;])?"); 074 075 /** 076 * Pattern for split two clear pins. 077 */ 078 private static final Pattern SPLIT_PIN_PATTERN = Pattern.compile("[ :;,/.]"); 079 080 /** 081 * NUmber of LMK pairs 082 */ 083 private final static int LMK_PAIRS_NO = 0xe; 084 085 /** 086 * LMK variants appled to first byte of LMK pair 087 */ 088 private final static int[] variants = { 089 0x00, 0xa6, 0x5a, 0x6a, 0xde, 0x2b, 0x50, 0x74, 0x9c,0xfa 090 }; 091 092 /** 093 * LMK scheme variants appiled to first byte of second key in pair 094 */ 095 private final static int[] schemeVariants = { 096 0x00, 0xa6, 0x5a, 0x6a, 0xde, 0x2b 097 }; 098 099 /** 100 * Index of scheme variant for left LMK key for double length keys 101 */ 102 private final static int KEY_U_LEFT = 1; 103 /** 104 * Index of scheme variant for right LMK key for double length keys 105 */ 106 private final static int KEY_U_RIGHT = 2; 107 /** 108 * Index of scheme variant for left LMK key for triple length keys 109 */ 110 private final static int KEY_T_LEFT = 3; 111 /** 112 * Index of scheme variant for middle LMK key for triple length keys 113 */ 114 private final static int KEY_T_MEDIUM = 4; 115 /** 116 * Index of scheme variant for right LMK key for triple length keys 117 */ 118 private final static int KEY_T_RIGHT = 5; 119 120 private static MessageDigest SHA1_MESSAGE_DIGEST = null; 121 122 private static final byte[] _VARIANT_RIGHT_HALF = new byte[] 123 { 124 (byte) 0xC0, (byte) 0xC0, (byte) 0xC0, (byte) 0xC0, 125 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 126 }; 127 128 static { 129 try { 130 SHA1_MESSAGE_DIGEST = MessageDigest.getInstance("SHA-1"); 131 } catch (NoSuchAlgorithmException ex) {} //NOPMD: SHA-1 is a standard digest 132 } 133 134 /** 135 * Creates an uninitialized JCE Security Module, you need to setConfiguration to initialize it 136 */ 137 public JCESecurityModule () { 138 super(); 139 } 140 141 /** 142 * @param lmkFile Local Master Keys filename of the JCE Security Module 143 * @throws SMException 144 */ 145 public JCESecurityModule (String lmkFile) throws SMException 146 { 147 Objects.requireNonNull(lmkFile); 148 init(null, lmkFile, false); 149 } 150 151 public JCESecurityModule (String lmkFile, String jceProviderClassName) throws SMException 152 { 153 Objects.requireNonNull(lmkFile); 154 init(jceProviderClassName, lmkFile, false); 155 } 156 157 public JCESecurityModule (Configuration cfg, Logger logger, String realm) throws ConfigurationException 158 { 159 setLogger(logger, realm); 160 setConfiguration(cfg); 161 } 162 163 /** 164 * Configures a JCESecurityModule 165 * @param cfg The following properties are read:<br> 166 * lmk: Local Master Keys file (The only required parameter)<br> 167 * jce: JCE Provider Class Name, if not provided, it defaults to: com.sun.crypto.provider.SunJCE<br> 168 * rebuildlmk: (true/false), rebuilds the Local Master Keys file with new keys (WARNING: old keys will be erased)<br> 169 * cbc-mac: Cipher Block Chaining MAC algorithm name for given JCE Provider.<br> 170 * Default is ISO9797ALG3MACWITHISO7816-4PADDING from BouncyCastle provider (known as Retail-MAC)<br> 171 * that is suitable for most of interfaces with double length MAC key<br> 172 * ANSI X9.19 aka ISO/IEC 9797-1 MAC algorithm 3 padding method 2 - ISO7816<br> 173 * ede-mac: Encrypt Decrypt Encrypt MAC algorithm name for given JCE Provider.<br> 174 * Default is DESEDEMAC from BouncyCastle provider<br> 175 * that is suitable for BASE24 with double length MAC key<br> 176 * ANSI X9.19<br> 177 * @throws ConfigurationException 178 */ 179 @Override 180 public void setConfiguration (Configuration cfg) throws ConfigurationException { 181 super.setConfiguration(cfg); 182 try { 183 init(cfg.get("provider"), cfg.get("lmk", null), cfg.getBoolean("rebuildlmk")); 184 } catch (SMException e) { 185 throw new ConfigurationException(e); 186 } 187 } 188 189 @Override 190 public SecureDESKey generateKeyImpl (short keyLength, String keyType) throws SMException { 191 Key generatedClearKey = jceHandler.generateDESKey(keyLength); 192 return encryptToLMK(keyLength, keyType, generatedClearKey); 193 } 194 195 @Override 196 public SecureDESKey importKeyImpl (short keyLength, String keyType, byte[] encryptedKey, 197 SecureDESKey kek, boolean checkParity) throws SMException { 198 // decrypt encrypted key 199 Key clearKEY = jceHandler.decryptDESKey(keyLength, encryptedKey, decryptFromLMK(kek), 200 checkParity); 201 // Encrypt Key under LMK 202 return encryptToLMK(keyLength, keyType, clearKEY); 203 } 204 205 @Override 206 public byte[] exportKeyImpl (SecureDESKey key, SecureDESKey kek) throws SMException { 207 // get key in clear 208 Key clearKey = decryptFromLMK(key); 209 // Encrypt key under kek 210 return jceHandler.encryptDESKey(key.getKeyLength(), clearKey, decryptFromLMK(kek)); 211 } 212 213 private int getKeyTypeIndex (short keyLength, String keyType) throws SMException { 214 int index; 215 if (keyType==null) 216 return 0; 217 String majorType = getMajorType(keyType); 218 if (!keyTypeToLMKIndex.containsKey(majorType)) 219 throw new SMException("Unsupported key type: " + majorType); 220 index = keyTypeToLMKIndex.get(majorType); 221 index |= getVariant(keyType) << 8; 222 return index; 223 } 224 225 private static String getMajorType (String keyType) { 226 Matcher m = KEY_TYPE_PATTERN.matcher(keyType); 227 m.find(); 228 if (m.group(1) != null) 229 return m.group(1); 230 throw new IllegalArgumentException("Missing key type"); 231 } 232 233 private static int getVariant (String keyType) { 234 int variant = 0; 235 Matcher m = KEY_TYPE_PATTERN.matcher(keyType); 236 m.find(); 237 if (m.group(3) != null) 238 try { 239 variant = Integer.valueOf(m.group(3)); 240 } catch (NumberFormatException ex){ 241 throw new NumberFormatException("Value "+m.group(4)+" is not valid key variant"); 242 } 243 return variant; 244 } 245 246 private static KeyScheme getScheme (int keyLength, String keyType) { 247 KeyScheme scheme = KeyScheme.Z; 248 switch (keyLength){ 249 case SMAdapter.LENGTH_DES: 250 scheme = KeyScheme.Z; break; 251 case SMAdapter.LENGTH_DES3_2KEY: 252 scheme = KeyScheme.X; break; 253 case SMAdapter.LENGTH_DES3_3KEY: 254 scheme = KeyScheme.Y; break; 255 } 256 if (keyType==null) 257 return scheme; 258 Matcher m = KEY_TYPE_PATTERN.matcher(keyType); 259 m.find(); 260 if (m.group(4) != null) 261 try { 262 scheme = KeyScheme.valueOf(m.group(4)); 263 } catch (IllegalArgumentException ex){ 264 throw new IllegalArgumentException("Value "+m.group(4)+" is not valid key scheme"); 265 } 266 return scheme; 267 } 268 269 @Override 270 public EncryptedPIN encryptPINImpl (String pin, String accountNumber) throws SMException { 271 byte[] clearPINBlock = calculatePINBlock(pin, FORMAT00, accountNumber); 272 // Encrypt 273 byte[] translatedPINBlock = jceHandler.encryptData(clearPINBlock, getLMK(PINLMKIndex)); 274 return new EncryptedPIN(translatedPINBlock, FORMAT00, accountNumber, false); 275 } 276 277 @Override 278 protected EncryptedPIN encryptPINImpl(String pin, String accountNumber, SecureDESKey pek) throws SMException { 279 byte[] clearPINBlock = calculatePINBlock(pin, FORMAT00, accountNumber); 280 Key clearPEK = decryptFromLMK(pek); 281 byte[] translatedPINBlock = jceHandler.encryptData(clearPINBlock, clearPEK); 282 return new EncryptedPIN(translatedPINBlock, FORMAT00, accountNumber, false); 283 } 284 285 @Override 286 public String decryptPINImpl (EncryptedPIN pinUnderLmk) throws SMException { 287 byte[] clearPINBlock = jceHandler.decryptData(pinUnderLmk.getPINBlock(), 288 getLMK(PINLMKIndex)); 289 return calculatePIN(clearPINBlock, pinUnderLmk.getPINBlockFormat(), pinUnderLmk.getAccountNumber()); 290 } 291 292 @Override 293 public EncryptedPIN importPINImpl (EncryptedPIN pinUnderKd1, SecureDESKey kd1) throws SMException { 294 // read inputs 295 String accountNumber = pinUnderKd1.getAccountNumber(); 296 // Use FORMAT00 for encrypting PIN under LMK 297 byte destinationPINBlockFormat = FORMAT00; 298 // get clear PIN 299 byte[] clearPINBlock = jceHandler.decryptData(pinUnderKd1.getPINBlock(), 300 decryptFromLMK(kd1)); 301 // extract clear pin (as entered by card holder) 302 String pin = calculatePIN(clearPINBlock, pinUnderKd1.getPINBlockFormat(), 303 accountNumber); 304 // Format PIN Block using proprietary FORMAT00 to be encrypetd under LMK 305 clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, accountNumber); 306 // encrypt PIN 307 byte[] translatedPINBlock = jceHandler.encryptData(clearPINBlock, getLMK(PINLMKIndex)); 308 return new EncryptedPIN(translatedPINBlock, destinationPINBlockFormat, 309 accountNumber, false); 310 } 311 312 @Override 313 public EncryptedPIN exportPINImpl (EncryptedPIN pinUnderLmk, SecureDESKey kd2, 314 byte destinationPINBlockFormat) throws SMException { 315 String accountNumber = pinUnderLmk.getAccountNumber(); 316 // process 317 // get clear PIN 318 byte[] clearPINBlock = jceHandler.decryptData(pinUnderLmk.getPINBlock(), 319 getLMK(PINLMKIndex)); 320 // extract clear pin 321 String pin = calculatePIN(clearPINBlock, pinUnderLmk.getPINBlockFormat(), 322 accountNumber); 323 clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, accountNumber); 324 // encrypt PIN 325 byte[] translatedPINBlock = jceHandler.encryptData(clearPINBlock, decryptFromLMK(kd2)); 326 return new EncryptedPIN(translatedPINBlock, destinationPINBlockFormat, 327 accountNumber, false); 328 } 329 330 @Override 331 public EncryptedPIN generatePINImpl(String accountNumber, int pinLen, List<String> excludes) 332 throws SMException { 333 if (excludes==null) 334 excludes = Arrays.asList(); 335 String pin; 336 { 337 Random rd = new SecureRandom(); 338 int max = (int)Math.pow(10, Math.min(pinLen, 9)); 339 int max2 = (int)Math.pow(10, Math.max(pinLen - 9,0)); 340 do { 341 long pinl = rd.nextInt(max); 342 if (pinLen > 9){ 343 pinl *= max2; 344 pinl += rd.nextInt(max2); 345 } 346 pin = ISOUtil.zeropad(pinl, pinLen); 347 } while (excludes.contains(pin)); 348 } 349 350 return encryptPINImpl(pin, accountNumber); 351 } 352 353 /** 354 * Visa way to decimalize data block 355 * @param b 356 * @return decimalized data string 357 */ 358 private static String decimalizeVisa(byte[] b){ 359 char[] bec = ISOUtil.hexString(b).toUpperCase().toCharArray(); 360 char[] bhc = new char[bec.length]; 361 int k = 0; 362 //Select 0-9 chars 363 for ( char c : bec ) 364 if (c<'A') 365 bhc[k++] = c; 366 //Select A-F chars and map them to 0-5 367 char adjust = 'A'-'0'; 368 for ( char c : bec ) 369 if (c>='A') 370 bhc[k++] = (char) (c-adjust); 371 return new String(bhc); 372 } 373 374 protected Key concatKeys(SecureDESKey keyA, SecureDESKey keyB) 375 throws SMException { 376 if ( keyA!=null && keyA.getKeyLength()==SMAdapter.LENGTH_DES 377 && keyB!=null && keyB.getKeyLength()==SMAdapter.LENGTH_DES) { 378 Key cvkAclear = decryptFromLMK(keyA); 379 Key cvkBclear = decryptFromLMK(keyB); 380 return jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY 381 ,ISOUtil.concat(cvkAclear.getEncoded(), cvkBclear.getEncoded())); 382 } 383 if (keyA!=null && keyA.getKeyLength()!=SMAdapter.LENGTH_DES) 384 return decryptFromLMK(keyA); 385 if (keyB!=null && keyB.getKeyLength()!=SMAdapter.LENGTH_DES) 386 return decryptFromLMK(keyB); 387 return null; 388 } 389 390 protected String calculateCVV(String accountNo, Key cvk, Date expDate, 391 String serviceCode) throws SMException { 392 String ed = ISODate.formatDate(expDate, "yyMM"); 393 return calculateCVD(accountNo, cvk, ed, serviceCode); 394 } 395 396 protected String calculateCVD(String accountNo, Key cvk, String expDate, 397 String serviceCode) throws SMException { 398 Key udka = jceHandler.formDESKey(SMAdapter.LENGTH_DES 399 ,Arrays.copyOfRange(cvk.getEncoded(), 0, 8)); 400 401 byte[] block = ISOUtil.hex2byte( 402 ISOUtil.zeropadRight(accountNo 403 + expDate 404 + serviceCode, 32)); 405 byte[] ba = Arrays.copyOfRange(block, 0, 8); 406 byte[] bb = Arrays.copyOfRange(block, 8,16); 407 408 //Encrypt ba with udka 409 byte[] bc = jceHandler.encryptData(ba, udka); 410 byte[] bd = ISOUtil.xor(bc, bb); 411 //Encrypt bd Tripple DES 412 byte[] be = jceHandler.encryptData(bd, cvk); 413 return decimalizeVisa(be).substring(0,3); 414 } 415 416 @Override 417 protected String calculateCVVImpl(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB, 418 Date expDate, String serviceCode) throws SMException { 419 return calculateCVV(accountNo,concatKeys(cvkA, cvkB),expDate,serviceCode); 420 } 421 422 @Override 423 protected String calculateCVDImpl(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB, 424 String expDate, String serviceCode) throws SMException { 425 return calculateCVD(accountNo, concatKeys(cvkA, cvkB), expDate, serviceCode); 426 } 427 428 protected void checkCAVVArgs(String upn, String authrc, String sfarc) 429 throws SMException { 430 if (upn == null) 431 throw new SMException("Unpredictable Number can not be null"); 432 if (authrc == null) 433 throw new SMException("Authorization Result Code can not be null"); 434 if (sfarc == null) 435 throw new SMException("Secend Factor Authorization Result Code" 436 + " can not be null"); 437 if (upn.length() != 4 ) 438 throw new SMException("Length of Unpredictable Number" 439 + " must be 4 but got "+upn.length()); 440 if (authrc.length() != 1 ) 441 throw new SMException("Length of Authorization Result Code" 442 + " must be 1 but got "+authrc.length()); 443 if (sfarc.length() != 2 ) 444 throw new SMException("Length of Secend Factor Authorization Result" 445 + " Code must be 2 but got "+sfarc.length()); 446 } 447 448 @Override 449 protected String calculateCAVVImpl(String accountNo, SecureDESKey cvk, String upn, 450 String authrc, String sfarc) throws SMException { 451 checkCAVVArgs(upn, authrc,sfarc); 452 return calculateCVD(accountNo,concatKeys(cvk, null),upn,authrc+sfarc); 453 } 454 455 @Override 456 protected boolean verifyCVVImpl(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB, 457 String cvv, Date expDate, String serviceCode) throws SMException { 458 String result = calculateCVV(accountNo, concatKeys(cvkA, cvkB), expDate, serviceCode); 459 return result.equals(cvv); 460 } 461 462 @Override 463 protected boolean verifyCVVImpl(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB, 464 String cvv, String expDate, String serviceCode) throws SMException { 465 String result = calculateCVD(accountNo, concatKeys(cvkA, cvkB), expDate, serviceCode); 466 return result.equals(cvv); 467 } 468 469 @Override 470 protected boolean verifyCAVVImpl(String accountNo, SecureDESKey cvk, String cavv, 471 String upn, String authrc, String sfarc) throws SMException { 472 checkCAVVArgs(upn, authrc,sfarc); 473 String result = calculateCVD(accountNo, concatKeys(cvk, null), upn, authrc+sfarc); 474 return result.equals(cavv); 475 } 476 477 protected String calculatedCVV(String accountNo, SecureDESKey imkac, 478 String expDate, String serviceCode, byte[] atc, MKDMethod mkdm) 479 throws SMException { 480 481 if (mkdm==null) 482 mkdm = MKDMethod.OPTION_A; 483 484 byte[] panpsn = formatPANPSN_dCVD(accountNo, null, mkdm); 485 Key mkac = deriveICCMasterKey(decryptFromLMK(imkac), panpsn); 486 487 String alteredPAN = ISOUtil.hexString(atc) + accountNo.substring(4); 488 489 return calculateCVD(alteredPAN, mkac, expDate, serviceCode); 490 } 491 492 @Override 493 protected boolean verifydCVVImpl(String accountNo, SecureDESKey imkac, String dcvv, 494 Date expDate, String serviceCode, byte[] atc, MKDMethod mkdm) 495 throws SMException { 496 497 String ed = ISODate.formatDate(expDate, "yyMM"); 498 String res = calculatedCVV(accountNo, imkac, ed, 499 serviceCode, atc, mkdm 500 ); 501 return res.equals(dcvv); 502 } 503 504 @Override 505 protected boolean verifydCVVImpl(String accountNo, SecureDESKey imkac, String dcvv, 506 String expDate, String serviceCode, byte[] atc, MKDMethod mkdm) 507 throws SMException { 508 509 String res = calculatedCVV(accountNo, imkac, expDate, 510 serviceCode, atc, mkdm 511 ); 512 return res.equals(dcvv); 513 } 514 515 protected String calculateCVC3(SecureDESKey imkcvc3, String accountNo, String acctSeqNo, 516 byte[] atc, byte[] upn, byte[] data, MKDMethod mkdm) 517 throws SMException { 518 if (mkdm==null) 519 mkdm = MKDMethod.OPTION_A; 520 byte[] panpsn = formatPANPSN_dCVD(accountNo, acctSeqNo, mkdm); 521 Key mkcvc3 = deriveICCMasterKey(decryptFromLMK(imkcvc3), panpsn); 522 byte[] ivcvc3 = data; 523 if (ivcvc3.length != 2) 524 //Compute IVCVC3 525 ivcvc3 = calculateIVCVC3(mkcvc3, data); 526 //Concatenate IVCVC3, UPN and ATC 527 byte[] b = ISOUtil.concat(ivcvc3, upn); 528 b = ISOUtil.concat(b, atc); 529 //Encrypt prepared blok 530 b = jceHandler.encryptData(b, mkcvc3); 531 //Format last two bytes to integer 532 int cvc3l = (b[6] & 0xff) << 8; 533 cvc3l |= b[7] & 0xff; 534 //Convert to string representation 535 return String.format("%05d",cvc3l); 536 } 537 538 @Override 539 protected boolean verifyCVC3Impl(SecureDESKey imkcvc3, String accountNo, String acctSeqNo, 540 byte[] atc, byte[] upn, byte[] data, MKDMethod mkdm, String cvc3) 541 throws SMException { 542 543 String calcCVC3 = calculateCVC3(imkcvc3, accountNo, acctSeqNo, 544 atc, upn, data, mkdm 545 ); 546 cvc3 = cvc3==null?"":cvc3; 547 //get some last digits 548 calcCVC3 = calcCVC3.substring(5-cvc3.length()); 549 return calcCVC3.equals(cvc3); 550 } 551 552 private byte[] calculateIVCVC3(Key mkcvc3, byte[] data) 553 throws JCEHandlerException { 554 byte[] paddedData = paddingISO9797Method2(data); 555 byte[] mac = calculateMACISO9797Alg3(mkcvc3, paddedData); 556 return Arrays.copyOfRange(mac,6,8); 557 } 558 559 /** 560 * ISO/IEC 9797-1 padding method 2 561 * @param d da to be padded 562 * @return padded data 563 */ 564 private byte[] paddingISO9797Method2(byte[] d) { 565 //Padding - first byte 0x80 rest 0x00 566 byte[] t = new byte[d.length - d.length%8 + 8]; 567 System.arraycopy(d, 0, t, 0, d.length); 568 for (int i=d.length;i<t.length;i++) 569 t[i] = (byte)(i==d.length?0x80:0x00); 570 d = t; 571 return d; 572 } 573 574 /** 575 * Calculate MAC according to ISO/IEC 9797-1 Alg 3 576 * @param key DES double length key 577 * @param d data to calculate MAC on it 578 * @return 8 byte of mac value 579 * @throws JCEHandlerException 580 */ 581 private byte[] calculateMACISO9797Alg3(Key key, byte[] d) throws JCEHandlerException { 582 Key kl = jceHandler.formDESKey(SMAdapter.LENGTH_DES 583 ,Arrays.copyOfRange(key.getEncoded(), 0, 8)); 584 Key kr = jceHandler.formDESKey(SMAdapter.LENGTH_DES 585 ,Arrays.copyOfRange(key.getEncoded(), 8, 16)); 586 if (d.length%8 != 0) { 587 //Padding with 0x00 bytes 588 byte[] t = new byte[d.length - d.length%8 + 8]; 589 System.arraycopy(d, 0, t, 0, d.length); 590 d = t; 591 } 592 //MAC_CBC alg 3 593 byte[] y_i = ISOUtil.hex2byte("0000000000000000"); 594 byte[] yi = new byte[8]; 595 for ( int i=0;i<d.length;i+=8){ 596 System.arraycopy(d, i, yi, 0, yi.length); 597 y_i = jceHandler.encryptData(ISOUtil.xor(yi, y_i), kl); 598 } 599 y_i = jceHandler.decryptData(y_i, kr); 600 y_i = jceHandler.encryptData(y_i, kl); 601 return y_i; 602 } 603 604 /** 605 * Prepare 8-bytes data from PAN and PAN Sequence Number. 606 * <p> 607 * Used at EMV operations: {@link #verifyARQCImpl ARQC verification}, 608 * {@link #generateARPCImpl ARPC generation}, 609 * {@link #generateSM_MACImpl secure message MAC generation} 610 * and {@link #translatePINGenerateSM_MACImpl secure message PIN translation} 611 */ 612 private static byte[] formatPANPSN(String pan, String psn, MKDMethod mkdm) 613 throws SMException { 614 switch (mkdm){ 615 case OPTION_A: 616 return formatPANPSNOptionA(pan, psn); 617 case OPTION_B: 618 if ( pan.length() <= 16) 619 //use OPTION_A 620 return formatPANPSNOptionA(pan, psn); 621 return formatPANPSNOptionB(pan, psn); 622 default: 623 throw new SMException("Unsupported ICC Master Key derivation method"); 624 } 625 } 626 627 /** 628 * Prepare 8-bytes data from PAN and PAN Sequence Number. 629 * <p> 630 * Used at dCVV verification {@link #verifydCVVImpl verifydCVV} 631 * and CVC3 {@link #verifyCVC3Impl verifyCVC3} 632 */ 633 private static byte[] formatPANPSN_dCVD(String pan, String psn, MKDMethod mkdm) 634 throws SMException { 635 switch (mkdm){ 636 case OPTION_A: 637 return formatPANPSNOptionA(pan, psn); 638 case OPTION_B: 639 return formatPANPSNOptionB(pan, psn); 640 default: 641 throw new SMException("Unsupported ICC Master Key derivation method"); 642 } 643 } 644 645 /** 646 * Format bytes representing Application PAN and 647 * PAN Sequence Number in BCD format. 648 * <p> 649 * Concatenate from left to right decimal digits of PAN and 650 * PAN Sequence Number digits. If {@code psn} is not present, it is 651 * replaced by a "00" digits. If the result is less than 16 digits long, 652 * pad it to the left with hexadecimal zeros in order to obtain an 653 * 16-digit number. If the Application PAN has an odd number of decimal 654 * digits then concatenate a "0" padding digit to the left thereby 655 * ensuring that the result is an even number of digits. 656 * 657 * @param pan application primary account number 658 * @param psn PAN Sequence Number 659 * @return up to 11 bytes representing Application PAN 660 */ 661 private static byte[] preparePANPSN(String pan, String psn){ 662 if (psn == null || psn.isEmpty()) 663 psn = "00"; 664 String ret = pan + psn; 665 //convert digits to bytes and padd with "0" 666 //to left for ensure even number of digits 667 return ISOUtil.hex2byte(ret); 668 } 669 670 /** 671 * Prepare 8-bytes data from PAN and PAN Sequence Number (Option A) 672 * <ul> 673 * <li> Prepare Application PAN and PAN Sequence Number by {@see #preparePANPSN} 674 * <li> Select first 16 digits 675 * </ul> 676 * @param pan application primary account number 677 * @param psn PAN Sequence Number 678 * @return 8-bytes representing rightmost 16 digits 679 */ 680 private static byte[] formatPANPSNOptionA(String pan, String psn){ 681 if ( pan.length() < 14 ) 682 try { 683 pan = ISOUtil.zeropad(pan, 14); 684 } catch( ISOException ex ) {} //NOPMD: ISOException condition is checked before. 685 byte[] b = preparePANPSN(pan, psn); 686 return Arrays.copyOfRange(b, b.length-8, b.length); 687 } 688 689 /** 690 * Prepare bytes data from PAN and PAN Sequence Number (Option B) 691 * 692 * <h5>Do the following:</h5> 693 * <ul> 694 * <li> Prepare Application PAN and PAN Sequence Number by {@see #preparePANPSN} 695 * <li> Hash the prepared result using the SHA-1 hashing algorithm 696 * to obtain the 20-byte hash result 697 * <li> Decimalize result of hasing by {@see #decimalizeVisa } 698 * <li> Select first 16 decimal digits 699 * </ul> 700 * @param pan application primary account number 701 * @param psn PAN Sequence Number 702 * @return 8-bytes representing first 16 decimalised digits 703 */ 704 private static byte[] formatPANPSNOptionB(String pan, String psn){ 705 byte[] b = preparePANPSN(pan, psn); 706 //20-bytes sha-1 digest 707 byte[] r = SHA1_MESSAGE_DIGEST.digest(b); 708 //decimalized HEX string of digest 709 String rs = decimalizeVisa(r); 710 //return 16-bytes decimalizd digest 711 return ISOUtil.hex2byte(rs.substring(0,16)); 712 } 713 714 /** 715 * Derive ICC Master Key from Issuer Master Key and preformated PAN/PANSeqNo 716 * 717 * Compute two 8-byte numbers: 718 * <li> left part is a result of Tripple-DES encription {@code panpsn} 719 * with {@code imk} as the key 720 * <li> right part is a result of Tripple-DES binary inverted 721 * {@code panpsn} with {@code imk} as the key 722 * <li> concatenate left and right parts 723 * <br> 724 * Described in EMV v4.2 Book 2, Annex A1.4.1 Master Key Derivation point 2 725 * 726 * @param imk 16-bytes Issuer Master Key 727 * @param panpsn preformated PAN and PAN Sequence Number 728 * @return derived 16-bytes ICC Master Key with adjusted DES parity 729 * @throws JCEHandlerException 730 */ 731 protected Key deriveICCMasterKey(Key imk, byte[] panpsn) 732 throws JCEHandlerException { 733 byte[] l = Arrays.copyOfRange(panpsn, 0, 8); 734 //left part of derived key 735 l = jceHandler.encryptData(l, imk); 736 byte[] r = Arrays.copyOfRange(panpsn, 0, 8); 737 //inverse clear right part of key 738 r = ISOUtil.xor(r, JCESecurityModule.fPaddingBlock); 739 //right part of derived key 740 r = jceHandler.encryptData(r,imk); 741 //derived key 742 byte[] mk = ISOUtil.concat(l,r); 743 //fix DES parity of key 744 Util.adjustDESParity(mk); 745 //form JCE Tripple-DES Key 746 return jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY, mk); 747 } 748 749 protected String calculatePVV(EncryptedPIN pinUnderLmk, Key key, int keyIdx 750 ,List<String> excludes) throws SMException { 751 String pin = decryptPINImpl(pinUnderLmk); 752 if (excludes!=null && excludes.contains(pin)) 753 throw new WeakPINException("Given PIN is on excludes list"); 754 String block = pinUnderLmk.getAccountNumber().substring(1); 755 block += Integer.toString(keyIdx%10); 756 block += pin.substring(0, 4); 757 byte[] b = ISOUtil.hex2byte(block); 758 b = jceHandler.encryptData(b, key); 759 760 return decimalizeVisa(b).substring(0,4); 761 } 762 763 @Override 764 protected String calculatePVVImpl(EncryptedPIN pinUnderLmk, SecureDESKey pvkA, 765 SecureDESKey pvkB, int pvkIdx, List<String> excludes) 766 throws SMException { 767 Key key = concatKeys(pvkA, pvkB); 768 return calculatePVV(pinUnderLmk, key, pvkIdx, excludes); 769 } 770 771 @Override 772 protected String calculatePVVImpl(EncryptedPIN pinUnderKd1, SecureDESKey kd1, 773 SecureDESKey pvkA, SecureDESKey pvkB, int pvkIdx, 774 List<String> excludes) throws SMException { 775 Key key = concatKeys(pvkA, pvkB); 776 EncryptedPIN pinUnderLmk = importPINImpl(pinUnderKd1, kd1); 777 return calculatePVV(pinUnderLmk, key, pvkIdx, excludes); 778 } 779 780 @Override 781 public boolean verifyPVVImpl(EncryptedPIN pinUnderKd1, SecureDESKey kd1, SecureDESKey pvkA, 782 SecureDESKey pvkB, int pvki, String pvv) throws SMException { 783 Key key = concatKeys(pvkA, pvkB); 784 EncryptedPIN pinUnderLmk = importPINImpl(pinUnderKd1, kd1); 785 String result = calculatePVV(pinUnderLmk, key, pvki, null); 786 return result.equals(pvv); 787 } 788 789 @Override 790 public EncryptedPIN translatePINImpl (EncryptedPIN pinUnderKd1, SecureDESKey kd1, 791 SecureDESKey kd2, byte destinationPINBlockFormat) throws SMException { 792 EncryptedPIN translatedPIN; 793 Key clearKd1 = decryptFromLMK(kd1); 794 Key clearKd2 = decryptFromLMK(kd2); 795 translatedPIN = translatePINExt(null, pinUnderKd1, clearKd1, clearKd2 796 ,destinationPINBlockFormat, null, PaddingMethod.MCHIP); 797 return translatedPIN; 798 } 799 800 private EncryptedPIN translatePINExt (EncryptedPIN oldPinUnderKd1, EncryptedPIN pinUnderKd1, Key kd1, 801 Key kd2, byte destinationPINBlockFormat, Key udk, PaddingMethod padm) throws SMException { 802 String accountNumber = pinUnderKd1.getAccountNumber(); 803 // get clear PIN 804 byte[] clearPINBlock = jceHandler.decryptData(pinUnderKd1.getPINBlock(), kd1); 805 String pin = calculatePIN(clearPINBlock, pinUnderKd1.getPINBlockFormat(), 806 accountNumber); 807 // Reformat PIN Block 808 byte[] translatedPINBlock; 809 if (isVSDCPinBlockFormat(destinationPINBlockFormat)) { 810 String udka = ISOUtil.hexString(Arrays.copyOfRange(udk.getEncoded(), 0, 8)); 811 if (destinationPINBlockFormat == SMAdapter.FORMAT42 ) { 812 byte[] oldClearPINBlock = jceHandler.decryptData(oldPinUnderKd1.getPINBlock(), kd1); 813 String oldPIN = calculatePIN(oldClearPINBlock, oldPinUnderKd1.getPINBlockFormat(), 814 accountNumber); 815 clearPINBlock = calculatePINBlock(pin+":"+oldPIN, destinationPINBlockFormat, udka); 816 } else 817 clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, udka); 818 819 accountNumber = udka.substring(4); 820 } else { 821 clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, accountNumber); 822 } 823 824 switch(padm){ 825 case VSDC: 826 //Add VSDC pin block padding 827 clearPINBlock = ISOUtil.concat(new byte[]{0x08}, clearPINBlock); 828 clearPINBlock = paddingISO9797Method2(clearPINBlock); 829 break; 830 case CCD: 831 //Add CCD pin block padding 832 clearPINBlock = paddingISO9797Method2(clearPINBlock); 833 break; 834 default: 835 } 836 837 // encrypt PIN 838 if (padm == PaddingMethod.CCD) 839 translatedPINBlock = jceHandler.encryptDataCBC(clearPINBlock, kd2 840 ,Arrays.copyOf(zeroBlock, zeroBlock.length)); 841 else 842 translatedPINBlock = jceHandler.encryptData(clearPINBlock, kd2); 843 return new EncryptedPIN(translatedPINBlock 844 ,destinationPINBlockFormat, accountNumber, false); 845 } 846 847 /** 848 * Derive the session key used for Application Cryptogram verification 849 * or for secure messaging, the diversification value is the ATC 850 * @param mkac unique ICC Master Key for Application Cryptogams or Secure Messaging 851 * @param atc ICC generated Application Transaction Counter as diversification value 852 * @return derived 16-bytes Session Key with adjusted DES parity 853 * @throws JCEHandlerException 854 */ 855 private Key deriveSK_VISA(Key mkac, byte[] atc) throws JCEHandlerException { 856 857 byte[] skl = new byte[8]; 858 System.arraycopy(atc, atc.length-2, skl, 6, 2); 859 skl = ISOUtil.xor(skl, Arrays.copyOfRange(mkac.getEncoded(),0 ,8)); 860 861 byte[] skr = new byte[8]; 862 System.arraycopy(atc, atc.length-2, skr, 6, 2); 863 skr = ISOUtil.xor(skr, ISOUtil.hex2byte("000000000000FFFF")); 864 skr = ISOUtil.xor(skr, Arrays.copyOfRange(mkac.getEncoded(),8 ,16)); 865 Util.adjustDESParity(skl); 866 Util.adjustDESParity(skr); 867 return jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY, ISOUtil.concat(skl,skr)); 868 } 869 870 /** 871 * Common Session Key Derivation Method for secure messaging. 872 * <p> 873 * The diversification value is the <em>RAND</em>, which is ARQC 874 * incremeted by 1 (with overflow) after each script command 875 * for that same ATC value. 876 * Described in EMV v4.2 Book 2, Annex A1.3.1 Common Session Key 877 * Derivation Option for secure messaging. 878 * 879 * @param mksm unique ICC Master Key for Secure Messaging 880 * @param rand Application Cryptogram as diversification value 881 * @return derived 16-bytes Session Key with adjusted DES parity 882 * @throws JCEHandlerException 883 */ 884 private Key deriveCommonSK_SM(Key mksm, byte[] rand) throws JCEHandlerException { 885 byte[] rl = Arrays.copyOf(rand,8); 886 rl[2] = (byte)0xf0; 887 byte[] skl = jceHandler.encryptData(rl, mksm); 888 byte[] rr = Arrays.copyOf(rand,8); 889 rr[2] = (byte)0x0f; 890 byte[] skr = jceHandler.encryptData(rr, mksm); 891 Util.adjustDESParity(skl); 892 Util.adjustDESParity(skr); 893 return jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY, ISOUtil.concat(skl,skr)); 894 } 895 896 /** 897 * Common Session Key Derivation Method for Application Cryptograms. 898 * <p> 899 * Described in EMV v4.2 Book 2, Annex A1.3.1 Common Session Key Derivation 900 * Option for Application Cryptograms. 901 * 902 * @param mkac unique ICC Master Key for Application Cryptogams. 903 * @param atc ICC generated Application Transaction Counter as diversification value. 904 * @return derived 16-bytes Session Key with adjusted DES parity. 905 * @throws JCEHandlerException 906 */ 907 private Key deriveCommonSK_AC(Key mkac, byte[] atc) throws JCEHandlerException { 908 909 byte[] r = new byte[8]; 910 System.arraycopy(atc, atc.length-2, r, 0, 2); 911 912 return deriveCommonSK_SM(mkac, r); 913 } 914 915 /** 916 * MasterCard Proprietary Session Key Derivation (SKD) method. 917 * <p> 918 * Described in M/Chip 4 version 1.1 Security & Key Management manual 919 * paragraph 7 ICC Session Key Derivation. 920 * 921 * @param mkac unique ICC Master Key for Application Cryptogams 922 * @param atc ICC generated Application Transaction Counter as diversification value 923 * @param upn terminal generated random as diversification value 924 * @return derived 16-bytes Session Key with adjusted DES parity 925 * @throws JCEHandlerException 926 */ 927 private Key deriveSK_MK(Key mkac, byte[] atc, byte[] upn) throws JCEHandlerException { 928 929 byte[] r = new byte[8]; 930 System.arraycopy(atc, atc.length-2, r, 0, 2); 931 System.arraycopy(upn, upn.length-4, r, 4, 4); 932 933 return deriveCommonSK_SM(mkac, r); 934 } 935 936 private void constraintMKDM(MKDMethod mkdm, SKDMethod skdm) throws SMException { 937 if (mkdm == MKDMethod.OPTION_B) 938 throw new SMException("Master Key Derivation Option B" 939 + " is not used in practice with scheme "+skdm); 940 } 941 942 private void constraintARPCM(SKDMethod skdm, ARPCMethod arpcMethod) throws SMException { 943 if (arpcMethod == ARPCMethod.METHOD_2 ) 944 throw new SMException("ARPC generation method 2" 945 + " is not used in practice with scheme "+skdm); 946 } 947 948 /** 949 * Calculate ARQC. 950 * <p> 951 * Entry point e.g. for simulator systems 952 */ 953 protected byte[] calculateARQC(MKDMethod mkdm, SKDMethod skdm 954 ,SecureDESKey imkac, String accountNo, String accntSeqNo, byte[] atc 955 ,byte[] upn, byte[] transData) throws SMException { 956 if (mkdm==null) 957 mkdm = MKDMethod.OPTION_A; 958 959 byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm); 960 Key mkac = deriveICCMasterKey(decryptFromLMK(imkac), panpsn); 961 Key skac = mkac; 962 switch(skdm){ 963 case VSDC: 964 constraintMKDM(mkdm, skdm); 965 break; 966 case MCHIP: 967 constraintMKDM(mkdm, skdm); 968 skac = deriveSK_MK(mkac,atc,upn); 969 break; 970 case EMV_CSKD: 971 skac = deriveCommonSK_AC(mkac, atc); 972 break; 973 default: 974 throw new SMException("Session Key Derivation "+skdm+" not supported"); 975 } 976 977 return calculateMACISO9797Alg3(skac, transData); 978 } 979 980 @Override 981 protected boolean verifyARQCImpl(MKDMethod mkdm, SKDMethod skdm, SecureDESKey imkac 982 ,String accountNo, String accntSeqNo, byte[] arqc, byte[] atc 983 ,byte[] upn, byte[] transData) throws SMException { 984 985 byte[] res = calculateARQC(mkdm, skdm, imkac, accountNo, accntSeqNo 986 ,atc, upn, transData); 987 return Arrays.equals(arqc, res); 988 } 989 990 @Override 991 public byte[] generateARPCImpl(MKDMethod mkdm, SKDMethod skdm, SecureDESKey imkac 992 ,String accountNo, String accntSeqNo, byte[] arqc, byte[] atc, byte[] upn 993 ,ARPCMethod arpcMethod, byte[] arc, byte[] propAuthData) 994 throws SMException { 995 if (mkdm==null) 996 mkdm = MKDMethod.OPTION_A; 997 998 byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm); 999 Key mkac = deriveICCMasterKey(decryptFromLMK(imkac), panpsn); 1000 Key skarpc = mkac; 1001 switch(skdm){ 1002 case VSDC: 1003 constraintMKDM(mkdm, skdm); 1004 constraintARPCM(skdm, arpcMethod); 1005 break; 1006 case MCHIP: 1007 constraintMKDM(mkdm, skdm); 1008 constraintARPCM(skdm, arpcMethod); 1009 break; 1010 case EMV_CSKD: 1011 skarpc = deriveCommonSK_AC(mkac, atc); 1012 break; 1013 default: 1014 throw new SMException("Session Key Derivation "+skdm+" not supported"); 1015 } 1016 1017 return calculateARPC(skarpc, arqc, arpcMethod, arc, propAuthData); 1018 } 1019 1020 @Override 1021 public byte[] verifyARQCGenerateARPCImpl(MKDMethod mkdm, SKDMethod skdm, SecureDESKey imkac 1022 ,String accountNo, String accntSeqNo, byte[] arqc, byte[] atc, byte[] upn 1023 ,byte[] transData, ARPCMethod arpcMethod, byte[] arc, byte[] propAuthData) 1024 throws SMException { 1025 if (mkdm==null) 1026 mkdm = MKDMethod.OPTION_A; 1027 1028 byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm); 1029 Key mkac = deriveICCMasterKey(decryptFromLMK(imkac), panpsn); 1030 Key skac = mkac; 1031 Key skarpc = mkac; 1032 switch(skdm){ 1033 case VSDC: 1034 constraintMKDM(mkdm, skdm); 1035 constraintARPCM(skdm, arpcMethod); 1036 break; 1037 case MCHIP: 1038 constraintMKDM(mkdm, skdm); 1039 constraintARPCM(skdm, arpcMethod); 1040 skac = deriveSK_MK(mkac,atc,upn); 1041 break; 1042 case EMV_CSKD: 1043 skac = deriveSK_MK(mkac, atc, new byte[4]); 1044 skarpc = skac; 1045 break; 1046 default: 1047 throw new SMException("Session Key Derivation "+skdm+" not supported"); 1048 } 1049 1050 if (!Arrays.equals(arqc, calculateMACISO9797Alg3(skac, transData))) 1051 return null; 1052 1053 return calculateARPC(skarpc, arqc, arpcMethod, arc, propAuthData); 1054 } 1055 1056 /** 1057 * Calculate ARPC. 1058 * <p> 1059 * Entry point e.g. for simulator systems 1060 */ 1061 protected byte[] calculateARPC(Key skarpc, byte[] arqc, ARPCMethod arpcMethod 1062 ,byte[] arc, byte[] propAuthData) throws SMException { 1063 if (arpcMethod == null) 1064 arpcMethod = ARPCMethod.METHOD_1; 1065 1066 byte[] b = new byte[8]; 1067 switch(arpcMethod) { 1068 case METHOD_1: 1069 System.arraycopy(arc, arc.length-2, b, 0, 2); 1070 b = ISOUtil.xor(arqc, b); 1071 return jceHandler.encryptData(b, skarpc); 1072 case METHOD_2: 1073 b = ISOUtil.concat(arqc, arc); 1074 if (propAuthData != null) 1075 b = ISOUtil.concat(b, propAuthData); 1076 b = paddingISO9797Method2(b); 1077 b = calculateMACISO9797Alg3(skarpc, b); 1078 return Arrays.copyOf(b, 4); 1079 default: 1080 throw new SMException("ARPC Method "+arpcMethod+" not supported"); 1081 } 1082 } 1083 1084 @Override 1085 protected byte[] generateSM_MACImpl(MKDMethod mkdm, SKDMethod skdm 1086 ,SecureDESKey imksmi, String accountNo, String accntSeqNo 1087 ,byte[] atc, byte[] arqc, byte[] data) throws SMException { 1088 if (mkdm==null) 1089 mkdm = MKDMethod.OPTION_A; 1090 byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm); 1091 Key mksmi = deriveICCMasterKey(decryptFromLMK(imksmi), panpsn); 1092 Key smi; 1093 switch(skdm){ 1094 case VSDC: 1095 smi = deriveSK_VISA(mksmi, atc); 1096 data = paddingISO9797Method2(data); 1097 break; 1098 case MCHIP: 1099 case EMV_CSKD: 1100 smi = deriveCommonSK_SM(mksmi,arqc); 1101 data = paddingISO9797Method2(data); 1102 break; 1103 default: 1104 throw new SMException("Session Key Derivation "+skdm+" not supported"); 1105 } 1106 return calculateMACISO9797Alg3(smi, data); 1107 } 1108 1109 @Override 1110 protected Pair<EncryptedPIN,byte[]> translatePINGenerateSM_MACImpl( 1111 MKDMethod mkdm, SKDMethod skdm, PaddingMethod padm, SecureDESKey imksmi 1112 ,String accountNo, String accntSeqNo, byte[] atc, byte[] arqc 1113 ,byte[] data, EncryptedPIN currentPIN, EncryptedPIN newPIN 1114 ,SecureDESKey kd1, SecureDESKey imksmc, SecureDESKey imkac 1115 ,byte destinationPINBlockFormat) throws SMException { 1116 if (mkdm==null) 1117 mkdm = MKDMethod.OPTION_A; 1118 byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm); 1119 Key mksmc = deriveICCMasterKey(decryptFromLMK(imksmc), panpsn); 1120 Key smc; 1121 PaddingMethod derivedPADM; 1122 switch(skdm){ 1123 case VSDC: 1124 smc = deriveSK_VISA(mksmc, atc); 1125 derivedPADM = PaddingMethod.VSDC; 1126 break; 1127 case MCHIP: 1128 smc = deriveCommonSK_SM(mksmc,arqc); 1129 derivedPADM = PaddingMethod.MCHIP; 1130 break; 1131 case EMV_CSKD: 1132 smc = deriveCommonSK_SM(mksmc,arqc); 1133 derivedPADM = PaddingMethod.CCD; 1134 break; 1135 default: 1136 throw new SMException("Session Key Derivation "+skdm+" not supported"); 1137 } 1138 1139 //Use derived Padding Method if not specified 1140 if ( padm == null ) 1141 padm = derivedPADM; 1142 1143 Key udk = null; 1144 if (isVSDCPinBlockFormat(destinationPINBlockFormat)) 1145 udk = deriveICCMasterKey(decryptFromLMK(imkac), panpsn); 1146 1147 EncryptedPIN pin = translatePINExt(currentPIN, newPIN, decryptFromLMK(kd1) 1148 ,smc, destinationPINBlockFormat, udk, padm); 1149 data = ISOUtil.concat(data, pin.getPINBlock()); 1150 byte[] mac = generateSM_MACImpl(mkdm, skdm, imksmi, accountNo 1151 ,accntSeqNo, atc, arqc, data); 1152 return new Pair(pin, mac); 1153 } 1154 1155 private boolean isVSDCPinBlockFormat(byte pinBlockFormat) { 1156 return pinBlockFormat==SMAdapter.FORMAT41 || pinBlockFormat==SMAdapter.FORMAT42; 1157 } 1158 1159 @Override 1160 public byte[] encryptDataImpl(CipherMode cipherMode, SecureDESKey kd 1161 ,byte[] data, byte[] iv) throws SMException { 1162 Key dek = decryptFromLMK(kd); 1163 return jceHandler.doCryptStuff(data, dek, Cipher.ENCRYPT_MODE, cipherMode, iv); 1164 } 1165 1166 @Override 1167 public byte[] decryptDataImpl(CipherMode cipherMode, SecureDESKey kd 1168 ,byte[] data, byte[] iv) throws SMException { 1169 Key dek = decryptFromLMK(kd); 1170 return jceHandler.doCryptStuff(data, dek, Cipher.DECRYPT_MODE, cipherMode, iv); 1171 } 1172 1173 /** 1174 * Generates CBC-MAC (Cipher Block Chaining Message Authentication Code) 1175 * for some data. 1176 * 1177 * @param data the data to be MACed 1178 * @param kd the key used for MACing 1179 * @return generated CBC-MAC bytes 1180 * @throws SMException 1181 */ 1182 @Override 1183 protected byte[] generateCBC_MACImpl (byte[] data, SecureDESKey kd) throws SMException { 1184 LogEvent evt = new LogEvent(this, "jce-provider-cbc-mac"); 1185 try { 1186 return generateMACImpl(data,kd,cfg.get("cbc-mac","ISO9797ALG3MACWITHISO7816-4PADDING"),evt); 1187 } catch (Exception e) { 1188 Logger.log(evt); 1189 throw e instanceof SMException ? (SMException)e : new SMException(e); 1190 } 1191 } 1192 1193 /** 1194 * Generates EDE-MAC (Encrypt Decrypt Encrypt Message Authentication Code) 1195 * for some data. 1196 * 1197 * @param data the data to be MACed 1198 * @param kd the key used for MACing 1199 * @return generated EDE-MAC bytes 1200 * @throws SMException 1201 */ 1202 @Override 1203 protected byte[] generateEDE_MACImpl (byte[] data, SecureDESKey kd) throws SMException { 1204 LogEvent evt = new LogEvent(this, "jce-provider-ede-mac"); 1205 try { 1206 return generateMACImpl(data,kd,cfg.get("ede-mac","DESEDEMAC"),evt); 1207 } catch (Exception e) { 1208 Logger.log(evt); 1209 throw e instanceof SMException ? (SMException)e : new SMException(e); 1210 } 1211 } 1212 1213 private byte[] generateMACImpl (byte[] data, SecureDESKey kd, 1214 String macAlgorithm, LogEvent evt) throws SMException { 1215 try { 1216 return jceHandler.generateMAC(data,decryptFromLMK(kd),macAlgorithm); 1217 } catch (JCEHandlerException e){ 1218 evt.addMessage(e); 1219 if (e.getCause() instanceof InvalidKeyException) 1220 throw new SMException(e); 1221 else 1222 throw new SMException("Unable to load MAC algorithm whose name is: " + macAlgorithm + 1223 ". Check that is used correct JCE provider and/or it is proper configured for this module.",e); 1224 } 1225 } 1226 1227 /** 1228 * Generates a random clear key component. 1229 * @param keyLength 1230 * @return clear key componenet 1231 * @throws SMException 1232 */ 1233 public String generateClearKeyComponent (short keyLength) throws SMException { 1234 String clearKeyComponenetHexString; 1235 SimpleMsg[] cmdParameters = { 1236 new SimpleMsg("parameter", "Key Length", keyLength) 1237 }; 1238 LogEvent evt = new LogEvent(this, "s-m-operation"); 1239 evt.addMessage(new SimpleMsg("command", "Generate Clear Key Component", cmdParameters)); 1240 1241 try { 1242 Key clearKey = jceHandler.generateDESKey(keyLength); 1243 byte[] clearKeyData = jceHandler.extractDESKeyMaterial(keyLength, clearKey); 1244 clearKeyComponenetHexString = ISOUtil.hexString(clearKeyData); 1245 evt.addMessage(new SimpleMsg("result", "Generated Clear Key Componenet", clearKeyComponenetHexString)); 1246 } catch (JCEHandlerException e) { 1247 evt.addMessage(e); 1248 throw e; 1249 } finally { 1250 Logger.log(evt); 1251 } 1252 return clearKeyComponenetHexString; 1253 } 1254 1255 /** 1256 * Generates key check value.<br> 1257 * @param secureDESKey SecureDESKey with untrusted or fake Key Check Value 1258 * @return generated Key Check Value 1259 * @throws SMException 1260 */ 1261 @Override 1262 protected byte[] generateKeyCheckValueImpl (SecureDESKey secureDESKey) throws SMException { 1263 return calculateKeyCheckValue(decryptFromLMK(secureDESKey)); 1264 } 1265 1266 @Override 1267 public SecureDESKey translateKeySchemeImpl (SecureDESKey key, KeyScheme keyScheme) 1268 throws SMException { 1269 1270 if (key == null) 1271 throw new SMException("Missing key to change"); 1272 1273 if (keyScheme == null) 1274 throw new SMException("Missing desired key schame"); 1275 1276 if (keyScheme == key.getScheme()) 1277 return key; 1278 1279 switch (key.getScheme()) { 1280 case Z: 1281 throw new SMException("Single length DES key using the variant method" 1282 + " can not be converted to any other key"); 1283 case U: 1284 case T: 1285 throw new SMException("DES key using the variant method can not " 1286 + "be converted to less secure key using X9.17 method"); 1287 case X: 1288 if (keyScheme != KeyScheme.U) 1289 throw new SMException("Double length key using X9.17 method may be " 1290 + "converted only to double length key using variant method"); 1291 break; 1292 case Y: 1293 if (keyScheme != KeyScheme.T) 1294 throw new SMException("Triple length key using X9.17 method may be " 1295 + "converted only to triple length key using variant method"); 1296 break; 1297 default: 1298 throw new SMException("Change key scheme allowed only for keys" 1299 + "using variant method"); 1300 } 1301 1302 // get key in clear 1303 Key clearKey = decryptFromLMK(key); 1304 // Encrypt key under LMK 1305 String keyType = getMajorType(key.getKeyType()) + ":" + key.getVariant() + keyScheme; 1306 return encryptToLMK(key.getKeyLength(), keyType, clearKey); 1307 } 1308 1309 /** 1310 * Forms a key from 3 clear components and returns it encrypted under its corresponding LMK 1311 * The corresponding LMK is determined from the keyType 1312 * @param keyLength e.g. LENGTH_DES, LENGTH_DES3_2, LENGTH_DES3_3, .. 1313 * @param keyType possible values are those defined in the SecurityModule inteface. e.g., ZMK, TMK,... 1314 * @param clearComponent1HexString HexString containing the first component 1315 * @param clearComponent2HexString HexString containing the second component 1316 * @param clearComponent3HexString HexString containing the second component 1317 * @return forms an SecureDESKey from two clear components 1318 * @throws SMException 1319 */ 1320 public SecureDESKey formKEYfromThreeClearComponents( 1321 short keyLength, 1322 String keyType, 1323 String clearComponent1HexString, 1324 String clearComponent2HexString, 1325 String clearComponent3HexString) throws SMException { 1326 SecureDESKey secureDESKey; 1327 LogEvent evt = new LogEvent(this, "s-m-operation"); 1328 1329 try { 1330 byte[] clearComponent1 = ISOUtil.hex2byte(clearComponent1HexString); 1331 byte[] clearComponent2 = clearComponent2HexString != null ? ISOUtil.hex2byte(clearComponent2HexString) : new byte[keyLength >> 3]; 1332 byte[] clearComponent3 = clearComponent3HexString != null ? ISOUtil.hex2byte(clearComponent3HexString) : new byte[keyLength >> 3]; 1333 byte[] clearKeyBytes = ISOUtil.xor(ISOUtil.xor(clearComponent1, clearComponent2), 1334 clearComponent3); 1335 Key clearKey = jceHandler.formDESKey(keyLength, clearKeyBytes); 1336 secureDESKey = encryptToLMK(keyLength, keyType, clearKey); 1337 SimpleMsg[] cmdParameters = { 1338 new SimpleMsg("parameter", "Key Length", keyLength), 1339 new SimpleMsg("parameter", "Key Type", keyType), 1340 new SimpleMsg("parameter", "Component 1 Check Value", calculateKeyCheckValue(jceHandler.formDESKey(keyLength, clearComponent1))), 1341 new SimpleMsg("parameter", "Component 2 Check Value", calculateKeyCheckValue(jceHandler.formDESKey(keyLength, clearComponent2))), 1342 new SimpleMsg("parameter", "Component 3 Check Value", calculateKeyCheckValue(jceHandler.formDESKey(keyLength, clearComponent3))) 1343 }; 1344 evt.addMessage(new SimpleMsg("command", "Form Key from Three Clear Components", cmdParameters)); 1345 evt.addMessage(new SimpleMsg("result", "Formed Key", secureDESKey)); 1346 } catch (JCEHandlerException e) { 1347 evt.addMessage(e); 1348 throw e; 1349 } finally { 1350 Logger.log(evt); 1351 } 1352 return secureDESKey; 1353 } 1354 1355 @Override 1356 public SecureDESKey formKEYfromClearComponents (short keyLength, String keyType, String... components) 1357 throws SMException 1358 { 1359 if (components.length < 1 || components.length > 3) 1360 throw new SMException("Invalid number of clear key components"); 1361 String[] s = new String[3]; 1362 int i=0; 1363 for (String component : components) 1364 s[i++] = component; 1365 1366 return formKEYfromThreeClearComponents(keyLength, keyType, s[0], s[1], s[2]); 1367 } 1368 1369 1370 /** 1371 * Calculates a key check value over a clear key 1372 * @param key 1373 * @return the key check value 1374 * @exception SMException 1375 */ 1376 protected byte[] calculateKeyCheckValue (Key key) throws SMException { 1377 byte[] encryptedZeroBlock = jceHandler.encryptData(zeroBlock, key); 1378 return ISOUtil.trim(encryptedZeroBlock, 3); 1379 } 1380 1381 /** 1382 * Encrypts a clear DES Key under LMK to form a SecureKey 1383 * @param keyLength 1384 * @param keyType 1385 * @param clearDESKey 1386 * @return secureDESKey 1387 * @throws SMException 1388 */ 1389 protected SecureDESKey encryptToLMK (short keyLength, String keyType, Key clearDESKey) throws SMException { 1390 Key novar, left, medium, right; 1391 byte[] clearKeyBytes = jceHandler.extractDESKeyMaterial(keyLength, clearDESKey); 1392 byte[] bl = new byte[SMAdapter.LENGTH_DES>>3]; 1393 byte[] bm = new byte[SMAdapter.LENGTH_DES>>3]; 1394 byte[] br = new byte[SMAdapter.LENGTH_DES>>3]; 1395 byte[] encrypted = null; 1396 // enforce correct (odd) parity before encrypting the key 1397 Util.adjustDESParity(clearKeyBytes); 1398 int lmkIndex = getKeyTypeIndex(keyLength, keyType); 1399 switch ( getScheme(keyLength, keyType) ){ 1400 case Z: 1401 case X: 1402 case Y: 1403 novar = getLMK( lmkIndex ); 1404 encrypted = jceHandler.encryptData(clearKeyBytes, novar); 1405 break; 1406 case U: 1407 left = getLMK( KEY_U_LEFT << 12 | lmkIndex & 0xfff ); 1408 right = getLMK( KEY_U_RIGHT << 12 | lmkIndex & 0xfff ); 1409 System.arraycopy(clearKeyBytes, 0, bl, 0, bl.length); 1410 System.arraycopy(clearKeyBytes, bl.length, br, 0, br.length); 1411 bl = jceHandler.encryptData(bl, left); 1412 br = jceHandler.encryptData(br, right); 1413 encrypted = ISOUtil.concat(bl, br); 1414 break; 1415 case T: 1416 left = getLMK( KEY_T_LEFT << 12 | lmkIndex & 0xfff ); 1417 medium= getLMK( KEY_T_MEDIUM << 12 | lmkIndex & 0xfff ); 1418 right = getLMK( KEY_T_RIGHT << 12 | lmkIndex & 0xfff ); 1419 System.arraycopy(clearKeyBytes, 0, bl, 0, bl.length); 1420 System.arraycopy(clearKeyBytes, bl.length, bm, 0, bm.length); 1421 System.arraycopy(clearKeyBytes, bl.length+bm.length, br, 0, br.length); 1422 bl = jceHandler.encryptData(bl, left); 1423 bm = jceHandler.encryptData(bm, medium); 1424 br = jceHandler.encryptData(br, right); 1425 encrypted = ISOUtil.concat(bl, bm); 1426 encrypted = ISOUtil.concat(encrypted, br); 1427 break; 1428 } 1429 SecureDESKey secureDESKey = new SecureDESKey(keyLength, keyType, encrypted, 1430 calculateKeyCheckValue(clearDESKey)); 1431 return secureDESKey; 1432 } 1433 1434 /** 1435 * Decrypts a secure DES key from encryption under LMK 1436 * @param secureDESKey (Key under LMK) 1437 * @return clear key 1438 * @throws SMException 1439 */ 1440 protected Key decryptFromLMK (SecureDESKey secureDESKey) throws SMException { 1441 Key left, medium, right; 1442 byte[] keyBytes = secureDESKey.getKeyBytes(); 1443 byte[] bl = new byte[SMAdapter.LENGTH_DES>>3]; 1444 byte[] bm = new byte[SMAdapter.LENGTH_DES>>3]; 1445 byte[] br = new byte[SMAdapter.LENGTH_DES>>3]; 1446 byte[] clearKeyBytes = null; 1447 Integer lmkIndex = getKeyTypeIndex(secureDESKey.getKeyLength(), secureDESKey.getKeyType()); 1448 if (lmkIndex==null) 1449 throw new SMException("Unsupported key type: " + secureDESKey.getKeyType()); 1450 lmkIndex |= secureDESKey.getVariant()<<8; 1451 switch ( secureDESKey.getScheme() ) { 1452 case Z: 1453 case X: 1454 case Y: 1455 clearKeyBytes = jceHandler.decryptData(keyBytes, getLMK( lmkIndex )); 1456 break; 1457 case U: 1458 left = getLMK( KEY_U_LEFT << 12 | lmkIndex & 0xfff ); 1459 right = getLMK( KEY_U_RIGHT << 12 | lmkIndex & 0xfff ); 1460 System.arraycopy(keyBytes, 0, bl, 0, bl.length); 1461 System.arraycopy(keyBytes, bl.length, br, 0, br.length); 1462 bl = jceHandler.decryptData(bl, left); 1463 br = jceHandler.decryptData(br, right); 1464 clearKeyBytes = ISOUtil.concat(bl, br); 1465 clearKeyBytes = ISOUtil.concat(clearKeyBytes, 0, clearKeyBytes.length, clearKeyBytes, 0, br.length ); 1466 break; 1467 case T: 1468 left = getLMK( KEY_T_LEFT << 12 | lmkIndex & 0xfff ); 1469 medium= getLMK( KEY_T_MEDIUM << 12 | lmkIndex & 0xfff ); 1470 right = getLMK( KEY_T_RIGHT << 12 | lmkIndex & 0xfff ); 1471 System.arraycopy(keyBytes, 0, bl, 0, bl.length); 1472 System.arraycopy(keyBytes, bl.length, bm, 0, bm.length); 1473 System.arraycopy(keyBytes, bl.length+bm.length, br, 0, br.length); 1474 bl = jceHandler.decryptData(bl, left); 1475 bm = jceHandler.decryptData(bm, medium); 1476 br = jceHandler.decryptData(br, right); 1477 clearKeyBytes = ISOUtil.concat(bl, bm); 1478 clearKeyBytes = ISOUtil.concat(clearKeyBytes, br); 1479 break; 1480 } 1481 if (!Util.isDESParityAdjusted(clearKeyBytes)) 1482 throw new JCEHandlerException("Parity not adjusted"); 1483 return jceHandler.formDESKey(secureDESKey.getKeyLength(), clearKeyBytes); 1484 } 1485 1486 private char[] formatPINBlock(String pin, int checkDigit){ 1487 char[] block = ISOUtil.hexString(fPaddingBlock).toCharArray(); 1488 char[] pinLenHex = String.format("%02X", pin.length()).toCharArray(); 1489 pinLenHex[0] = (char)('0' + checkDigit); 1490 1491 // pin length then pad with 'F' 1492 System.arraycopy(pinLenHex, 0, block, 0, pinLenHex.length); 1493 System.arraycopy(pin.toCharArray(), 0 1494 ,block, pinLenHex.length, pin.length()); 1495 return block; 1496 } 1497 1498 private String[] splitPins(String pins) { 1499 String[] pin = new String[2]; 1500 String[] p = SPLIT_PIN_PATTERN.split(pins); 1501 pin[0] = p[0]; 1502 if (p.length >= 2) 1503 pin[1] = p[1]; 1504 return pin; 1505 } 1506 1507 /** 1508 * Calculates the clear PIN Block 1509 * @param pin as entered by the card holder on the PIN entry device 1510 * @param pinBlockFormat 1511 * @param accountNumber (the 12 right-most digits of the account number excluding the check digit) 1512 * @return The clear PIN Block 1513 * @throws SMException 1514 * 1515 */ 1516 protected byte[] calculatePINBlock (String pin, byte pinBlockFormat, String accountNumber) throws SMException { 1517 byte[] pinBlock = null; 1518 String oldPin = null; 1519 if (pinBlockFormat==SMAdapter.FORMAT42){ 1520 String[] p = splitPins(pin); 1521 pin = p[0]; 1522 oldPin = p[1]; 1523 if (oldPin.length() < MIN_PIN_LENGTH || oldPin.length() > MAX_PIN_LENGTH) 1524 throw new SMException("Invalid OLD PIN length: " + oldPin.length()); 1525 if (!ISOUtil.isNumeric(oldPin, 10)) 1526 throw new SMException("Invalid OLD PIN decimal digits: " + oldPin); 1527 } 1528 if (pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) 1529 throw new SMException("Invalid PIN length: " + pin.length()); 1530 if (!ISOUtil.isNumeric(pin, 10)) 1531 throw new SMException("Invalid PIN decimal digits: " + pin); 1532 if (isVSDCPinBlockFormat(pinBlockFormat)) { 1533 if (accountNumber.length() != 16 ) 1534 throw new SMException("Invalid UDK-A: " + accountNumber 1535 + ". The length of the UDK-A must be 16 hexadecimal digits"); 1536 } else if (accountNumber.length() != 12) 1537 throw new SMException("Invalid Account Number: " + accountNumber + ". The length of the account number must be 12 (the 12 right-most digits of the account number excluding the check digit)"); 1538 switch (pinBlockFormat) { 1539 case FORMAT00: // same as FORMAT01 1540 case FORMAT01: 1541 { 1542 // Block 1 1543 byte[] block1 = ISOUtil.hex2byte(new String(formatPINBlock(pin,0x0))); 1544 1545 // Block 2 1546 byte[] block2 = ISOUtil.hex2byte("0000" + accountNumber); 1547 // pinBlock 1548 pinBlock = ISOUtil.xor(block1, block2); 1549 } 1550 break; 1551 case FORMAT03: 1552 { 1553 char[] block = ISOUtil.hexString(fPaddingBlock).toCharArray(); 1554 System.arraycopy(pin.toCharArray(), 0 1555 ,block, 0, pin.length()); 1556 pinBlock = ISOUtil.hex2byte (new String(block)); 1557 } 1558 break; 1559 case FORMAT05: 1560 { 1561 // Block 1 1562 char[] block1 = formatPINBlock(pin, 0x1); 1563 1564 // Block rnd 1565 byte[] rnd = new byte[8]; 1566 Random rd = new SecureRandom(); 1567 rd.nextBytes(rnd); 1568 1569 // Block 2 1570 char[] block2 = ISOUtil.hexString(rnd).toCharArray(); 1571 1572 // merge blocks 1573 System.arraycopy(block1, 0 1574 ,block2, 0, pin.length() + 2 1575 ); 1576 // pinBlock 1577 pinBlock = ISOUtil.hex2byte(new String(block2)); 1578 } 1579 break; 1580 case FORMAT34: 1581 { 1582 pinBlock = ISOUtil.hex2byte (new String(formatPINBlock(pin,0x2))); 1583 } 1584 break; 1585 case FORMAT35: 1586 { 1587 // Block 1 1588 byte[] block1 = ISOUtil.hex2byte(new String(formatPINBlock(pin,0x2))); 1589 1590 // Block 2 1591 byte[] block2 = ISOUtil.hex2byte("0000" + accountNumber); 1592 // pinBlock 1593 pinBlock = ISOUtil.xor(block1, block2); 1594 } 1595 break; 1596 case FORMAT41: 1597 { 1598 // Block 1 1599 byte[] block1 = ISOUtil.hex2byte(new String(formatPINBlock(pin,0x0))); 1600 1601 // Block 2 - account number should contain Unique DEA Key A (UDK-A) 1602 byte[] block2 = ISOUtil.hex2byte("00000000" 1603 + accountNumber.substring(accountNumber.length()-8) ); 1604 // pinBlock 1605 pinBlock = ISOUtil.xor(block1, block2); 1606 } 1607 break; 1608 case FORMAT42: 1609 { 1610 // Block 1 1611 byte[] block1 = ISOUtil.hex2byte(new String(formatPINBlock(pin,0x0))); 1612 1613 // Block 2 - account number should contain Unique DEA Key A (UDK-A) 1614 byte[] block2 = ISOUtil.hex2byte("00000000" 1615 + accountNumber.substring(accountNumber.length()-8) ); 1616 // Block 3 - old pin 1617 byte[] block3 = ISOUtil.hex2byte(ISOUtil.zeropadRight(oldPin, 16)); 1618 // pinBlock 1619 pinBlock = ISOUtil.xor(block1, block2); 1620 pinBlock = ISOUtil.xor(pinBlock, block3); 1621 } 1622 break; 1623 default: 1624 throw new SMException("Unsupported PIN format: " + pinBlockFormat); 1625 } 1626 return pinBlock; 1627 } 1628 1629 private void validatePinBlock(char[] pblock, int checkDigit, int padidx, int offset) 1630 throws SMException { 1631 validatePinBlock(pblock, checkDigit, padidx, offset, 'F'); 1632 } 1633 1634 private void validatePinBlock(char[] pblock, int checkDigit 1635 ,int padidx, int offset, char padDigit) 1636 throws SMException { 1637 // test pin block check digit 1638 if (checkDigit >= 0 && pblock[0] - '0' != checkDigit) 1639 throw new SMException("PIN Block Error - invalid check digit"); 1640 // test pin block pdding 1641 int i = pblock.length - 1; 1642 while (i >= padidx) 1643 if (pblock[i--] != padDigit && padDigit > 0) 1644 throw new SMException("PIN Block Error - invalid padding"); 1645 // test pin block digits 1646 while (i >= offset) 1647 if (pblock[i--] >= 'A') 1648 throw new SMException("PIN Block Error - illegal pin digit"); 1649 // test pin length 1650 int pinLength = padidx - offset; 1651 if (pinLength < MIN_PIN_LENGTH || pinLength > MAX_PIN_LENGTH) 1652 throw new SMException("PIN Block Error - invalid pin length: " + pinLength); 1653 } 1654 1655 /** 1656 * Calculates the clear pin (as entered by card holder on the pin entry device) 1657 * givin the clear PIN block 1658 * @param pinBlock clear PIN Block 1659 * @param pinBlockFormat 1660 * @param accountNumber 1661 * @return the pin 1662 * @throws SMException 1663 */ 1664 protected String calculatePIN (byte[] pinBlock, byte pinBlockFormat, String accountNumber) throws SMException { 1665 String pin = null; 1666 if (isVSDCPinBlockFormat(pinBlockFormat)) { 1667 if (accountNumber.length() != 16 ) 1668 throw new SMException("Invalid UDK-A: " + accountNumber 1669 + ". The length of the UDK-A must be 16 hexadecimal digits"); 1670 } else if (accountNumber.length() != 12) 1671 throw new SMException("Invalid Account Number: " + accountNumber + ". The length of the account number must be 12 (the 12 right-most digits of the account number excluding the check digit)"); 1672 switch (pinBlockFormat) { 1673 case FORMAT00: // same as format 01 1674 case FORMAT01: 1675 { 1676 // Block 2 1677 byte[] bl2 = ISOUtil.hex2byte("0000" + accountNumber); 1678 // get Block1 1679 byte[] bl1 = ISOUtil.xor(pinBlock, bl2); 1680 int pinLength = bl1[0] & 0x0f; 1681 char[] block1 = ISOUtil.hexString(bl1).toCharArray(); 1682 int offset = 2; 1683 int checkDigit = 0x0; 1684 int padidx = pinLength + offset; 1685 // test pin block 1686 validatePinBlock(block1,checkDigit,padidx,offset); 1687 // get pin 1688 pin = new String(Arrays.copyOfRange(block1, offset, padidx)); 1689 } 1690 break; 1691 case FORMAT03: 1692 { 1693 String bl1 = ISOUtil.hexString(pinBlock); 1694 int padidx = bl1.indexOf('F'); 1695 if ( padidx < 0) padidx = 12; 1696 char[] block1 = bl1.toCharArray(); 1697 int checkDigit = -0x1; 1698 int offset = 0; 1699 1700 // test pin block 1701 validatePinBlock(block1,checkDigit,padidx,offset); 1702 // get pin 1703 pin = new String(Arrays.copyOfRange(block1, offset, padidx)); 1704 } 1705 break; 1706 case FORMAT05: 1707 { 1708 // get Block1 1709 byte[] bl1 = pinBlock; 1710 int pinLength = bl1[0] & 0x0f; 1711 char[] block1 = ISOUtil.hexString(bl1).toCharArray(); 1712 int offset = 2; 1713 int checkDigit = 0x01; 1714 int padidx = pinLength + offset; 1715 // test pin block 1716 validatePinBlock(block1, checkDigit, padidx, offset, (char) 0); 1717 // get pin 1718 pin = new String(Arrays.copyOfRange(block1, offset, padidx)); 1719 } 1720 break; 1721 case FORMAT34: 1722 { 1723 int pinLength = pinBlock[0] & 0x0f; 1724 char[] block1 = ISOUtil.hexString(pinBlock).toCharArray(); 1725 int offset = 2; 1726 int checkDigit = 0x2; 1727 int padidx = pinLength + offset; 1728 // test pin block 1729 validatePinBlock(block1,checkDigit,padidx,offset); 1730 // get pin 1731 pin = new String(Arrays.copyOfRange(block1, offset, padidx)); 1732 } 1733 break; 1734 case FORMAT35: 1735 { 1736 // Block 2 1737 byte[] bl2 = ISOUtil.hex2byte("0000" + accountNumber); 1738 // get Block1 1739 byte[] bl1 = ISOUtil.xor(pinBlock, bl2); 1740 int pinLength = bl1[0] & 0x0f; 1741 char[] block1 = ISOUtil.hexString(bl1).toCharArray(); 1742 int offset = 2; 1743 int checkDigit = 0x2; 1744 int padidx = pinLength + offset; 1745 // test pin block 1746 validatePinBlock(block1,checkDigit,padidx,offset); 1747 // get pin 1748 pin = new String(Arrays.copyOfRange(block1, offset, padidx)); 1749 } 1750 break; 1751 case FORMAT41: 1752 { 1753 // Block 2 - account number should contain Unique DEA Key A (UDK-A) 1754 byte[] bl2 = ISOUtil.hex2byte("00000000" 1755 + accountNumber.substring(accountNumber.length()-8) ); 1756 // get Block1 1757 byte[] bl1 = ISOUtil.xor(pinBlock, bl2); 1758 int pinLength = bl1[0] & 0x0f; 1759 char[] block1 = ISOUtil.hexString(bl1).toCharArray(); 1760 int offset = 2; 1761 int checkDigit = 0x0; 1762 int padidx = pinLength + offset; 1763 // test pin block 1764 validatePinBlock(block1,checkDigit,padidx,offset); 1765 // get pin 1766 pin = new String(Arrays.copyOfRange(block1, offset, padidx)); 1767 } 1768 break; 1769 case FORMAT42: 1770 { 1771 // Block 2 - account number should contain Unique DEA Key A (UDK-A) 1772 byte[] bl2 = ISOUtil.hex2byte("00000000" 1773 + accountNumber.substring(accountNumber.length()-8) ); 1774 // get Block1 1775 byte[] bl1 = ISOUtil.xor(pinBlock, bl2); 1776 int pinLength = bl1[0] & 0x0f; 1777 char[] block1 = ISOUtil.hexString(bl1).toCharArray(); 1778 int offset = 2; 1779 int checkDigit = 0x0; 1780 int padidx = pinLength + offset; 1781 // test pin block 1782 validatePinBlock(block1,checkDigit,padidx,offset,'0'); 1783 // get pin 1784 pin = new String(Arrays.copyOfRange(block1, offset, padidx)); 1785 } 1786 break; 1787 default: 1788 throw new SMException("Unsupported PIN Block format: " + pinBlockFormat); 1789 } 1790 return pin; 1791 } 1792 1793 /** 1794 * Initializes the JCE Security Module 1795 * @param jceProviderClassName 1796 * @param lmkFile Local Master Keys File used by JCE Security Module to store the LMKs 1797 * @param lmkRebuild if set to true, the lmkFile gets overwritten with newly generated keys (WARNING: this would render all your previously stored SecureKeys unusable) 1798 * @throws SMException 1799 */ 1800 private void init (String jceProviderClassName, String lmkFile, boolean lmkRebuild) throws SMException { 1801 File lmk = lmkFile != null ? new File(lmkFile) : null; 1802 if (lmk == null && !lmkRebuild) 1803 throw new SMException ("null lmkFile - needs rebuild"); 1804 try { 1805 keyTypeToLMKIndex = new TreeMap<>(); 1806 keyTypeToLMKIndex.put(SMAdapter.TYPE_ZMK, 0x000); 1807 keyTypeToLMKIndex.put(SMAdapter.TYPE_ZPK, 0x001); 1808 keyTypeToLMKIndex.put(SMAdapter.TYPE_PVK, 0x002); 1809 keyTypeToLMKIndex.put(SMAdapter.TYPE_TPK, 0x002); 1810 keyTypeToLMKIndex.put(SMAdapter.TYPE_TMK, 0x002); 1811 keyTypeToLMKIndex.put(SMAdapter.TYPE_TAK, 0x003); 1812// keyTypeToLMKIndex.put(PINLMKIndex, 0x004); 1813 keyTypeToLMKIndex.put(SMAdapter.TYPE_CVK, 0x402); 1814 keyTypeToLMKIndex.put(SMAdapter.TYPE_ZAK, 0x008); 1815 keyTypeToLMKIndex.put(SMAdapter.TYPE_BDK, 0x009); 1816 keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_AC, 0x109); 1817 keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_SMI, 0x209); 1818 keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_SMC, 0x309); 1819 keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_DAC, 0x409); 1820 keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_DN, 0x509); 1821 keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_CVC3, 0x709); 1822 keyTypeToLMKIndex.put(SMAdapter.TYPE_ZEK, 0x00A); 1823 keyTypeToLMKIndex.put(SMAdapter.TYPE_DEK, 0x00B); 1824 keyTypeToLMKIndex.put(SMAdapter.TYPE_RSA_SK, 0x00C); 1825 keyTypeToLMKIndex.put(SMAdapter.TYPE_HMAC, 0x10C); 1826 keyTypeToLMKIndex.put(SMAdapter.TYPE_RSA_PK, 0x00D); 1827 Provider provider = null; 1828 LogEvent evt = new LogEvent(this, "jce-provider"); 1829 try { 1830 if (jceProviderClassName != null && !jceProviderClassName.isEmpty()) { 1831 provider = (Provider) Class.forName(jceProviderClassName).getDeclaredConstructor().newInstance(); 1832 Security.addProvider(provider); 1833 evt.addMessage("name", provider.getName()); 1834 } 1835 } catch (Exception e) { 1836 evt.addMessage(e); 1837 throw new SMException("Unable to load jce provider whose class name is: " 1838 + jceProviderClassName); 1839 } finally { 1840 Logger.log(evt); 1841 } 1842 jceHandler = new JCEHandler(); 1843 if (lmkRebuild) { 1844 // Creat new LMK file 1845 evt = new LogEvent(this, "local-master-keys"); 1846 if (lmk != null) 1847 evt.addMessage("Rebuilding new Local Master Keys in file: \"" + lmk.getCanonicalPath() + "\"."); 1848 Logger.log(evt); 1849 // Generate New random Local Master Keys 1850 generateLMK(); 1851 // Write the new Local Master Keys to file 1852 evt = new LogEvent(this, "local-master-keys"); 1853 if (lmk != null) { 1854 writeLMK(lmk); 1855 evt.addMessage("Local Master Keys built successfully in file: \"" 1856 + lmk.getCanonicalPath() + "\"."); 1857 } else { 1858 evt.addMessage("Local Master Keys built successfully"); 1859 } 1860 Logger.log(evt); 1861 } 1862 if (lmk != null) { 1863 if (!lmk.exists()) { 1864 // LMK File does not exist 1865 throw new SMException("Error loading Local Master Keys, file: \"" 1866 + lmk.getCanonicalPath() + "\" does not exist." 1867 + " Please specify a valid LMK file, or rebuild a new one."); 1868 } 1869 else { 1870 // Read LMK from file 1871 readLMK(lmk); 1872 evt = new LogEvent(this, "local-master-keys"); 1873 evt.addMessage("Loaded successfully from file: \"" + lmk.getCanonicalPath() 1874 + "\""); 1875 Logger.log(evt); 1876 } 1877 } 1878 } catch (Exception e) { 1879 if (e instanceof SMException) { 1880 throw (SMException)e; 1881 } 1882 else { 1883 throw new SMException(e); 1884 } 1885 } 1886 } 1887 1888 private byte[] applySchemeVariant(byte[] lmkdata, int variant){ 1889 byte[] vardata = new byte[lmkdata.length]; 1890 System.arraycopy(lmkdata, 0, vardata, 0, lmkdata.length); 1891 //XOR first byte of second key with selected variant byte 1892 vardata[8] ^= variant; 1893 return vardata; 1894 } 1895 1896 private byte[] applyVariant(byte[] lmkdata, int variant){ 1897 byte[] vardata = new byte[lmkdata.length]; 1898 System.arraycopy(lmkdata, 0, vardata, 0, lmkdata.length); 1899 //XOR first byte of first key with selected variant byte 1900 vardata[0] ^= variant; 1901 return vardata; 1902 } 1903 1904 private void spreadLMKVariants(byte[] lmkData, int idx) throws SMException { 1905 int i = 0; 1906 for (int v :variants){ 1907 int k = 0; 1908 byte[] variantData = applyVariant(lmkData,v); 1909 for (int sv :schemeVariants){ 1910 byte[] svData = applySchemeVariant(variantData,sv); 1911// System.out.println(String.format("LMK0x%1$d:%2$d:%3$02x=%4$s", k, i, idx 1912// ,ISOUtil.hexString(svData))); 1913 // make it 3 components to work with sun JCE 1914 svData = ISOUtil.concat( 1915 svData, 0, jceHandler.getBytesLength(SMAdapter.LENGTH_DES3_2KEY), 1916 svData, 0, jceHandler.getBytesLength(SMAdapter.LENGTH_DES) 1917 ); 1918 int key = idx; 1919 key += 0x100*i; 1920 key += 0x1000*k++; 1921 lmks.put(key, (SecretKey)jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY, svData)); 1922 } 1923 i++; 1924 } 1925 } 1926 1927 /** 1928 * Generates new LMK keys 1929 * @exception SMException 1930 */ 1931 private void generateLMK () throws SMException { 1932 lmks.clear(); 1933 try { 1934 for (int i = 0; i <= LMK_PAIRS_NO; i++){ 1935 SecretKey lmkKey = (SecretKey)jceHandler.generateDESKey(LMK_KEY_LENGTH); 1936 spreadLMKVariants(lmkKey.getEncoded(), i); 1937 } 1938 } catch (JCEHandlerException e) { 1939 throw new SMException("Can't generate Local Master Keys", e); 1940 } 1941 } 1942 1943 /** 1944 * reads (loads) LMK's from lmkFile 1945 * @param lmkFile 1946 * @exception SMException 1947 */ 1948 private void readLMK (File lmkFile) throws SMException { 1949 lmks.clear(); 1950 try { 1951 Properties lmkProps = new Properties(); 1952 InputStream in = new BufferedInputStream(new FileInputStream(lmkFile)); 1953 try { 1954 lmkProps.load(in); 1955 } finally { 1956 in.close(); 1957 } 1958 Enumeration<?> e = lmkProps.propertyNames(); 1959 while (e.hasMoreElements()) { 1960 String propName = (String) e.nextElement(); 1961 lmkProps.put(propName, Environment.get(lmkProps.getProperty(propName))); 1962 } 1963 byte[] lmkData; 1964 for (int i = 0; i <= LMK_PAIRS_NO; i++) { 1965 lmkData = ISOUtil.hex2byte(lmkProps.getProperty( 1966 String.format("LMK0x%1$02x", i)).substring(0, SMAdapter.LENGTH_DES3_2KEY/4)); 1967 spreadLMKVariants(lmkData, i); 1968 } 1969 } catch (Exception e) { 1970 throw new SMException("Can't read Local Master Keys from file: " + 1971 lmkFile, e); 1972 } 1973 } 1974 1975 /** 1976 * Writes a newly generated LMK's to lmkFile 1977 * @param lmkFile 1978 * @exception SMException 1979 */ 1980 private void writeLMK (File lmkFile) throws SMException { 1981 Properties lmkProps = new Properties(); 1982 try { 1983 for (int i = 0; i <= LMK_PAIRS_NO; i++) { 1984 lmkProps.setProperty(String.format("LMK0x%1$02x", i), 1985 ISOUtil.hexString(lmks.get(i).getEncoded())); 1986 } 1987 OutputStream out = new BufferedOutputStream(new FileOutputStream(lmkFile)); 1988 try { 1989 lmkProps.store(out, "Local Master Keys"); 1990 } finally { 1991 out.close(); 1992 } 1993 } catch (Exception e) { 1994 throw new SMException("Can't write Local Master Keys to file: " + lmkFile, 1995 e); 1996 } 1997 } 1998 1999 /** 2000 * gets the suitable LMK variant for the key index 2001 * @param lmkIndex 2002 * @return the lmks secret key for the givin key index 2003 * @throws SMException 2004 */ 2005 private SecretKey getLMK (Integer lmkIndex) throws SMException { 2006 SecretKey lmk = lmks.get(lmkIndex); 2007 if (lmk==null) 2008 throw new SMException(String.format("Invalid key code: LMK0x%1$04x", lmkIndex)); 2009 return lmk; 2010 } 2011 /** 2012 * maps a key type to an LMK Index 2013 */ 2014 private Map<String,Integer> keyTypeToLMKIndex; 2015 /** 2016 * The clear Local Master Keys 2017 */ 2018 private final Map<Integer,SecretKey> lmks = new TreeMap<>(); 2019 /** 2020 * A index for the LMK used to encrypt the PINs 2021 */ 2022 private static final Integer PINLMKIndex = 0x004; 2023 /** 2024 * The key length (in bits) of the Local Master Keys. 2025 * JCESecurityModule uses Triple DES Local Master Keys 2026 */ 2027 private static final short LMK_KEY_LENGTH = LENGTH_DES3_2KEY; 2028 /** 2029 * The minimum length of the PIN 2030 */ 2031 private static final short MIN_PIN_LENGTH = 4; 2032 /** 2033 * The maximum length of the PIN 2034 */ 2035 private static final short MAX_PIN_LENGTH = 12; 2036 2037 /** 2038 * a 64-bit block of ones used when calculating pin blocks 2039 */ 2040 private static final byte[] fPaddingBlock = ISOUtil.hex2byte("FFFFFFFFFFFFFFFF"); 2041 2042 /** 2043 * a dummy 64-bit block of zeros used when calculating the check value 2044 */ 2045 private static final byte[] zeroBlock = ISOUtil.hex2byte("0000000000000000"); 2046 2047 protected JCEHandler jceHandler; 2048 2049 //-------------------------------------------------------------------------------------------------- 2050 // DUKPT 2051 //-------------------------------------------------------------------------------------------------- 2052 2053 private byte[] encrypt64(byte[] data, byte[] key) 2054 throws JCEHandlerException 2055 { 2056 return jceHandler.encryptData( 2057 data, 2058 jceHandler.formDESKey((short) (key.length << 3), key) 2059 ); 2060 } 2061 2062 private byte[] decrypt64(byte[] data, byte[] key) 2063 throws JCEHandlerException 2064 { 2065 return jceHandler.decryptData( 2066 data, 2067 jceHandler.formDESKey((short) (key.length << 3), key) 2068 ); 2069 } 2070 2071 protected byte[] specialEncrypt(byte[] data, byte[] key) 2072 throws JCEHandlerException 2073 { 2074 if (key.length == 8) 2075 { 2076 data = ISOUtil.xor(data, key); 2077 data = encrypt64(data, key); 2078 return ISOUtil.xor(data, key); 2079 } 2080 byte[] keyL = new byte[8]; 2081 byte[] keyR = new byte[8]; 2082 System.arraycopy(key, 0, keyL, 0, 8); 2083 System.arraycopy(key, 8, keyR, 0, 8); 2084 data = encrypt64(data, keyL); 2085 data = decrypt64(data, keyR); 2086 data = encrypt64(data, keyL); 2087 return data; 2088 } 2089 2090 protected byte[] specialDecrypt(byte[] data, byte[] key) 2091 throws JCEHandlerException 2092 { 2093 if (key.length == 8) 2094 { 2095 data = ISOUtil.xor(data, key); 2096 data = decrypt64(data, key); 2097 return ISOUtil.xor(data, key); 2098 } 2099 byte[] keyL = new byte[8]; 2100 byte[] keyR = new byte[8]; 2101 System.arraycopy(key, 0, keyL, 0, 8); 2102 System.arraycopy(key, 8, keyR, 0, 8); 2103 data = decrypt64(data, keyL); 2104 data = encrypt64(data, keyR); 2105 data = decrypt64(data, keyL); 2106 return data; 2107 } 2108 2109 private void shr(byte[] b) 2110 { 2111 boolean carry = false; 2112 for (int i = 0; i < b.length; i++) 2113 { 2114 byte c = b[i]; 2115 b[i] = (byte) (c >>> 1 & 0x7F); 2116 if (carry) 2117 { 2118 b[i] |= 0x80; 2119 } 2120 carry = (c & 0x01) == 1; 2121 } 2122 } 2123 2124 private void or(byte[] b, byte[] mask, int offset) 2125 { 2126 int len = Math.min(b.length - offset, mask.length); 2127 byte[] d = new byte[len]; 2128 2129 for (int i = 0; i < len; i++) 2130 { 2131 b[offset++] |= mask[i]; 2132 } 2133 } 2134 2135 private byte[] and(byte[] b, byte[] mask, int offset) 2136 { 2137 int len = Math.min(b.length - offset, mask.length); 2138 byte[] d = new byte[b.length]; 2139 System.arraycopy(b, 0, d, 0, b.length); 2140 2141 for (int i = 0; i < len; i++, offset++) 2142 { 2143 d[offset] = (byte) (b[offset] & mask[i]); 2144 } 2145 return d; 2146 } 2147 2148 private byte[] and(byte[] b, byte[] mask) 2149 { 2150 return and(b, mask, 0); 2151 } 2152 2153 private boolean notZero(byte[] b) 2154 { 2155 int l = b.length; 2156 for (int i = 0; i < l; i++) 2157 { 2158 if (b[i] != 0) 2159 { 2160 return true; 2161 } 2162 } 2163 return false; 2164 } 2165 2166 private byte[] calculateInitialKey(KeySerialNumber sn, SecureDESKey bdk, boolean tdes) 2167 throws SMException 2168 { 2169 byte[] kl = new byte[8]; 2170 byte[] kr = new byte[8]; 2171 byte[] kk = decryptFromLMK(bdk).getEncoded(); 2172 byte[] ksn = new byte[8]; 2173 2174 System.arraycopy(kk, 0, kl, 0, 8); 2175 System.arraycopy(kk, 8, kr, 0, 8); 2176 System.arraycopy (sn.getBytes(), 0, ksn, 0, ksn.length); 2177 ksn[7] &= 0xE0; 2178 2179 byte[] data = encrypt64(ksn, kl); 2180 data = decrypt64(data, kr); 2181 data = encrypt64(data, kl); 2182 2183 // ANS X9.24:2009 3DES keys (@DFLC/@apr 2011) A.6 steps 5 & 6 (p69) 2184 if (tdes) 2185 { 2186 byte[] kl2 = ISOUtil.xor(kl, _VARIANT_RIGHT_HALF); 2187 byte[] kr2 = ISOUtil.xor(kr, _VARIANT_RIGHT_HALF); 2188 byte[] data2 = encrypt64(ksn, kl2); 2189 data2 = decrypt64(data2, kr2); 2190 data2 = encrypt64(data2, kl2); 2191 2192 byte[] d = new byte[16]; 2193 System.arraycopy(data, 0, d, 0, 8); 2194 System.arraycopy(data2, 0, d, 8, 8); 2195 data = d; 2196 } 2197 return data; 2198 } 2199 2200 2201 @Override 2202 public byte[] dataEncrypt (SecureDESKey bdk, byte[] clearText) throws SMException { 2203 try { 2204 byte[] ksnB = jceHandler.generateDESKey ((short) 128).getEncoded(); 2205 KeySerialNumber ksn = getKSN (ksnB); 2206 byte[] derivedKey = calculateDerivedKey (ksn, bdk, true, true); 2207 Key dk = jceHandler.formDESKey ((short) 128, derivedKey); 2208 byte[] cypherText = jceHandler.encryptData (lpack(clearText), dk); 2209 2210 ByteBuffer bb = ByteBuffer.allocate (cypherText.length + 32); 2211 bb.put (ksnB); 2212 bb.put (cypherText); 2213 bb.put (hash8 (new byte[][]{ ksnB, cypherText, derivedKey })); 2214 return bb.array(); 2215 } catch (JCEHandlerException e) { 2216 throw new SMException (e); 2217 } 2218 } 2219 2220 @Override 2221 public byte[] dataDecrypt (SecureDESKey bdk, byte[] cypherText) throws SMException { 2222 try { 2223 if (cypherText.length < 32) { 2224 throw new SMException ( 2225 "Invalid key block '" + ISOUtil.hexString (cypherText) + "'" 2226 ); 2227 } 2228 byte[] ksnB = new byte[24]; 2229 byte[] encryptedData = new byte[cypherText.length - 32]; 2230 byte[] mac = new byte[8]; 2231 2232 System.arraycopy (cypherText, 0, ksnB, 0, 24); 2233 System.arraycopy (cypherText, 24, encryptedData, 0, encryptedData.length); 2234 System.arraycopy (cypherText, cypherText.length-8, mac, 0, 8); 2235 2236 KeySerialNumber ksn = getKSN (ksnB); 2237 2238 byte[] derivedKey = calculateDerivedKey (ksn, bdk, true, true); 2239 Key dk = jceHandler.formDESKey ((short) 128, derivedKey); 2240 byte[] clearText = jceHandler.decryptData (encryptedData, dk); 2241 byte[] generatedMac = hash8 ( 2242 new byte[][] { ksnB, encryptedData, derivedKey } 2243 ); 2244 if (!Arrays.equals (mac, generatedMac)) 2245 throw new SMException ("Invalid cyphertext."); 2246 return lunpack (clearText); 2247 } catch (JCEHandlerException e) { 2248 throw new SMException (e); 2249 } 2250 } 2251 2252 protected byte[] calculateDerivedKey(KeySerialNumber ksn, SecureDESKey bdk, boolean tdes, boolean dataEncryption) 2253 throws SMException 2254 { 2255 return tdes?calculateDerivedKeyTDES(ksn,bdk, dataEncryption):calculateDerivedKeySDES(ksn,bdk); 2256 } 2257 2258 private byte[] calculateDerivedKeySDES(KeySerialNumber ksn, SecureDESKey bdk) 2259 throws SMException 2260 { 2261 final byte[] _1FFFFF = 2262 new byte[]{(byte) 0x1F, (byte) 0xFF, (byte) 0xFF}; 2263 final byte[] _100000 = 2264 new byte[]{(byte) 0x10, (byte) 0x00, (byte) 0x00}; 2265 final byte[] _E00000 = 2266 new byte[]{(byte) 0xE0, (byte) 0x00, (byte) 0x00}; 2267 2268 byte[] curkey = calculateInitialKey(ksn, bdk, false); 2269 byte[] smidr = new byte[8]; 2270 System.arraycopy (ksn.getBytes(), 2, smidr, 0, smidr.length); 2271 2272 byte[] reg3 = ksn.getTransactionCounterBytes(); 2273 reg3 = and(reg3, _1FFFFF); 2274 byte[] shiftr = _100000; 2275 byte[] temp; 2276 byte[] tksnr; 2277 smidr = and(smidr, _E00000, 5); 2278 do 2279 { 2280 temp = and(shiftr, reg3); 2281 if (notZero(temp)) 2282 { 2283 or(smidr, shiftr, 5); 2284 tksnr = ISOUtil.xor(smidr, curkey); 2285 tksnr = encrypt64(tksnr, curkey); 2286 curkey = ISOUtil.xor(tksnr, curkey); 2287 } 2288 shr(shiftr); 2289 } 2290 while (notZero(shiftr)); 2291 curkey[7] ^= 0xFF; 2292 return curkey; 2293 } 2294 private byte[] calculateDerivedKeyTDES(KeySerialNumber ksn, SecureDESKey bdk, boolean dataEncryption) 2295 throws SMException 2296 { 2297 final byte[] _1FFFFF = 2298 new byte[]{(byte) 0x1F, (byte) 0xFF, (byte) 0xFF}; 2299 final byte[] _100000 = 2300 new byte[]{(byte) 0x10, (byte) 0x00, (byte) 0x00}; 2301 final byte[] _E00000 = 2302 new byte[]{(byte) 0xE0, (byte) 0x00, (byte) 0x00}; 2303 2304 byte[] curkey = calculateInitialKey(ksn, bdk, true); 2305 2306 byte[] smidr = new byte[8]; 2307 System.arraycopy (ksn.getBytes(), 2, smidr, 0, smidr.length); 2308 2309 byte[] ksnImage = ksn.getBytes(); 2310 byte[] reg3; 2311 if (dataEncryption && ksnImage[0] != (byte) 0xFF && ksnImage[1] != (byte) 0xFF) { 2312 // jPOS 2.x compatibility mode - 2313 reg3 = new byte[5]; 2314 System.arraycopy (ksnImage, 5, reg3, 0, reg3.length); 2315 } else { 2316 reg3 = ksn.getTransactionCounterBytes(); 2317 } 2318 reg3 = and(reg3, _1FFFFF); 2319 byte[] shiftr = _100000; 2320 byte[] temp; 2321 byte[] tksnr; 2322 byte[] r8a; 2323 byte[] r8b; 2324 byte[] curkeyL = new byte[8]; 2325 byte[] curkeyR = new byte[8]; 2326 smidr = and(smidr, _E00000, 5); 2327 2328 do 2329 { 2330 temp = and(shiftr, reg3); 2331 if (notZero(temp)) 2332 { 2333 System.arraycopy(curkey, 0, curkeyL, 0, 8); 2334 System.arraycopy(curkey, 8, curkeyR, 0, 8); 2335 2336 or(smidr, shiftr, 5); 2337 // smidr == R8 2338 2339 tksnr = ISOUtil.xor(smidr, curkeyR); 2340 tksnr = encrypt64(tksnr, curkeyL); 2341 tksnr = ISOUtil.xor(tksnr, curkeyR); 2342 // tksnr == R8A 2343 curkeyL = ISOUtil.xor(curkeyL, _VARIANT_RIGHT_HALF); 2344 curkeyR = ISOUtil.xor(curkeyR, _VARIANT_RIGHT_HALF); 2345 2346 r8b = ISOUtil.xor(smidr, curkeyR); 2347 r8b = encrypt64(r8b, curkeyL); 2348 r8b = ISOUtil.xor(r8b, curkeyR); 2349 2350 System.arraycopy(r8b, 0, curkey, 0, 8); 2351 System.arraycopy(tksnr, 0, curkey, 8, 8); 2352 } 2353 shr(shiftr); 2354 } 2355 while (notZero(shiftr)); 2356 2357 if (dataEncryption) { 2358 curkey[5] ^= 0xFF; 2359 curkey[13] ^= 0xFF; 2360 System.arraycopy(curkey, 0, curkeyL, 0, 8); 2361 System.arraycopy(curkey, 8, curkeyR, 0, 8); 2362 byte[] L = encrypt64(curkeyL, curkeyL); 2363 L = decrypt64(L, curkeyR); 2364 L = encrypt64(L, curkeyL); 2365 2366 byte[] R = encrypt64(curkeyR, curkeyL); // this is the right implementation 2367 R = decrypt64(R, curkeyR); 2368 R = encrypt64(R, curkeyL); 2369 System.arraycopy (L, 0, curkey, 0, 8); 2370 System.arraycopy (R, 0, curkey, 8, 8); 2371 } else { 2372 curkey[7] ^= 0xFF; 2373 curkey[15] ^= 0xFF; 2374 } 2375 return curkey; 2376 } 2377 2378 public SecureDESKey importBDK(String clearComponent1HexString, 2379 String clearComponent2HexString, 2380 String clearComponent3HexString) throws SMException 2381 { 2382 return formKEYfromThreeClearComponents((short) 128, "BDK", 2383 clearComponent1HexString, 2384 clearComponent2HexString, 2385 clearComponent3HexString); 2386 } 2387 2388 private KeySerialNumber getKSN(byte[] b) { 2389 ByteBuffer buf = ByteBuffer.allocate(10); 2390 buf.put(b, 0, 10); 2391 return new KeySerialNumber (buf.array()); 2392 } 2393 2394 protected EncryptedPIN translatePINImpl 2395 (EncryptedPIN pinUnderDuk, KeySerialNumber ksn, 2396 SecureDESKey bdk, SecureDESKey kd2, byte destinationPINBlockFormat,boolean tdes) 2397 throws SMException 2398 { 2399 byte[] derivedKey = calculateDerivedKey(ksn, bdk, tdes, false); 2400 byte[] clearPinblk = specialDecrypt( 2401 pinUnderDuk.getPINBlock(), derivedKey 2402 ); 2403 2404 String pan = pinUnderDuk.getAccountNumber(); 2405 String pin = calculatePIN( 2406 clearPinblk, pinUnderDuk.getPINBlockFormat(), pan 2407 ); 2408 byte[] translatedPinblk = jceHandler.encryptData( 2409 calculatePINBlock(pin, destinationPINBlockFormat, pan), 2410 decryptFromLMK(kd2) 2411 ); 2412 return new EncryptedPIN(translatedPinblk, destinationPINBlockFormat, pan,false); 2413 } 2414 protected EncryptedPIN importPINImpl 2415 (EncryptedPIN pinUnderDuk, KeySerialNumber ksn, SecureDESKey bdk,boolean tdes) 2416 throws SMException 2417 { 2418 byte[] derivedKey = calculateDerivedKey(ksn, bdk,tdes, false); 2419 byte[] clearPinblk = specialDecrypt( 2420 pinUnderDuk.getPINBlock(), derivedKey 2421 ); 2422 String pan = pinUnderDuk.getAccountNumber(); 2423 String pin = calculatePIN( 2424 clearPinblk, pinUnderDuk.getPINBlockFormat(), pan 2425 ); 2426 2427 byte[] pinUnderLmk = jceHandler.encryptData( 2428 calculatePINBlock(pin, SMAdapter.FORMAT00, pan), 2429 getLMK(PINLMKIndex) 2430 ); 2431 return new EncryptedPIN(pinUnderLmk, SMAdapter.FORMAT00, pan,false); 2432 } 2433 2434 /** 2435 * Exports PIN to DUKPT Encryption. 2436 * 2437 * @param pinUnderLmk 2438 * @param ksn 2439 * @param bdk 2440 * @param tdes 2441 * @param destinationPINBlockFormat 2442 * @return The encrypted pin 2443 * @throws SMException 2444 */ 2445 public EncryptedPIN exportPIN 2446 (EncryptedPIN pinUnderLmk, KeySerialNumber ksn, SecureDESKey bdk, boolean tdes, 2447 byte destinationPINBlockFormat) 2448 throws SMException 2449 { 2450 String accountNumber = pinUnderLmk.getAccountNumber(); 2451 // process 2452 // get clear PIN 2453 byte[] clearPINBlock = jceHandler.decryptData(pinUnderLmk.getPINBlock(), 2454 getLMK(PINLMKIndex)); 2455 // extract clear pin 2456 String pin = calculatePIN(clearPINBlock, pinUnderLmk.getPINBlockFormat(), 2457 accountNumber); 2458 2459 clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, accountNumber); 2460 2461 // encrypt PIN 2462 byte[] derivedKey = calculateDerivedKey(ksn, bdk, tdes, false); 2463 byte[] translatedPINBlock = specialEncrypt(clearPINBlock, derivedKey); 2464 2465 return new EncryptedPIN(translatedPINBlock, destinationPINBlockFormat, 2466 accountNumber, false); 2467 } 2468 2469 /** 2470 * places a length indicator in a byte array. 2471 * @param b the byte array 2472 * @return a byte array with a two-byte length indicator 2473 */ 2474 private byte[] lpack (byte[] b) { 2475 int l = b.length; 2476 int adjustedLen = ((l+9) >> 3) << 3; 2477 byte[] d = new byte[adjustedLen]; 2478 System.arraycopy (b, 0, d, 2, l); 2479 d[0] = (byte) ((l >> 8) & 0xFF); 2480 d[1] = (byte) (l & 0xFF); 2481 return d; 2482 } 2483 /** 2484 * Unpacks a byte array packed by lPack 2485 * into the former byte[] 2486 * @param b packed byte array 2487 * @return original (unpacked) byte array 2488 */ 2489 private byte[] lunpack (byte[] b) { 2490 int l = ((((int)b[0])&0xFF) << 8) | (((int)b[1])&0xFF); 2491 byte[] d = new byte[l]; 2492 System.arraycopy (b,2,d,0,l); 2493 return d; 2494 } 2495 2496 private byte[] hash8 (byte[][] bb) throws SMException { 2497 try { 2498 MessageDigest md = MessageDigest.getInstance("SHA"); 2499 for (byte[] b : bb) { 2500 md.update(b); 2501 } 2502 return Arrays.copyOf(md.digest(), 8); 2503 } catch (NoSuchAlgorithmException e) { 2504 throw new SMException (e); 2505 } 2506 } 2507}