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