/** * Generate a key exchange key for use in encrypting the mwk * * @param privateKey The private key for the merchant * @return byte array containing the kek * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws InvalidKeyException */ public byte[] generateKek(PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { // get the ValueLink public key PublicKey vlPublic = this.getValueLinkPublicKey(); // generate shared secret key KeyAgreement ka = KeyAgreement.getInstance("DH"); ka.init(privateKey); ka.doPhase(vlPublic, true); byte[] secretKey = ka.generateSecret(); if (debug) { Debug.logInfo( "Secret Key : " + StringUtil.toHexString(secretKey) + " / " + secretKey.length, module); } // generate 3DES from secret key using VL algorithm (KEK) MessageDigest md = MessageDigest.getInstance("SHA1"); byte[] digest = md.digest(secretKey); byte[] des2 = getByteRange(digest, 0, 16); byte[] first8 = getByteRange(des2, 0, 8); byte[] kek = copyBytes(des2, first8, 0); if (debug) { Debug.logInfo("Generated KEK : " + StringUtil.toHexString(kek) + " / " + kek.length, module); } return kek; }
protected SecretKey getKekKey() { if (kek == null) { kek = this.getDesEdeKey(getKek()); } if (debug) { Debug.logInfo("Raw KEK : " + StringUtil.toHexString(getKek()), module); Debug.logInfo("KEK : " + StringUtil.toHexString(kek.getEncoded()), module); } return kek; }
protected SecretKey getMwkKey() { if (mwk == null) { mwk = this.getDesEdeKey(getByteRange(getMwk(), 8, 24)); } if (debug) { Debug.logInfo("Raw MWK : " + StringUtil.toHexString(getMwk()), module); Debug.logInfo("MWK : " + StringUtil.toHexString(mwk.getEncoded()), module); } return mwk; }
/** * Generate a new MWK * * @param desBytes byte array of the DES key (24 bytes) * @return Hex String of the new encrypted MWK ready for transmission to ValueLink */ public byte[] generateMwk(byte[] desBytes) { if (debug) { Debug.logInfo( "DES Key : " + StringUtil.toHexString(desBytes) + " / " + desBytes.length, module); } SecretKeyFactory skf1 = null; SecretKey mwk = null; try { skf1 = SecretKeyFactory.getInstance("DESede"); } catch (NoSuchAlgorithmException e) { Debug.logError(e, module); } DESedeKeySpec desedeSpec2 = null; try { desedeSpec2 = new DESedeKeySpec(desBytes); } catch (InvalidKeyException e) { Debug.logError(e, module); } if (skf1 != null && desedeSpec2 != null) { try { mwk = skf1.generateSecret(desedeSpec2); } catch (InvalidKeySpecException e) { Debug.logError(e, module); } } if (mwk != null) { return generateMwk(mwk); } else { return null; } }
/** * Generate a new MWK * * @param mwkdes3 pre-generated DES3 SecretKey * @return Hex String of the new encrypted MWK ready for transmission to ValueLink */ public byte[] generateMwk(SecretKey mwkdes3) { // zeros for checksum byte[] zeros = {0, 0, 0, 0, 0, 0, 0, 0}; // 8 bytes random data byte[] random = new byte[8]; Random ran = new Random(); ran.nextBytes(random); // open a cipher using the new mwk Cipher cipher = this.getCipher(mwkdes3, Cipher.ENCRYPT_MODE); // make the checksum - encrypted 8 bytes of 0's byte[] encryptedZeros = new byte[0]; try { encryptedZeros = cipher.doFinal(zeros); } catch (IllegalStateException e) { Debug.logError(e, module); } catch (IllegalBlockSizeException e) { Debug.logError(e, module); } catch (BadPaddingException e) { Debug.logError(e, module); } // make the 40 byte MWK - random 8 bytes + key + checksum byte[] newMwk = copyBytes(mwkdes3.getEncoded(), encryptedZeros, 0); newMwk = copyBytes(random, newMwk, 0); if (debug) { Debug.logInfo("Random 8 byte : " + StringUtil.toHexString(random), module); Debug.logInfo("Encrypted 0's : " + StringUtil.toHexString(encryptedZeros), module); Debug.logInfo( "Decrypted MWK : " + StringUtil.toHexString(mwkdes3.getEncoded()) + " / " + mwkdes3.getEncoded().length, module); Debug.logInfo( "Encrypted MWK : " + StringUtil.toHexString(newMwk) + " / " + newMwk.length, module); } return newMwk; }
/** * Encrypt the defined pin using the configured keys * * @param pin Plain text String of the pin * @return Hex String of the encrypted pin (EAN) for transmission to ValueLink */ public String encryptPin(String pin) { // get the Cipher Cipher mwkCipher = this.getCipher(this.getMwkKey(), Cipher.ENCRYPT_MODE); // pin to bytes byte[] pinBytes = pin.getBytes(); // 7 bytes of random data byte[] random = this.getRandomBytes(7); // pin checksum byte[] checkSum = this.getPinCheckSum(pinBytes); // put all together byte[] eanBlock = new byte[16]; int i; for (i = 0; i < random.length; i++) { eanBlock[i] = random[i]; } eanBlock[7] = checkSum[0]; for (i = 0; i < pinBytes.length; i++) { eanBlock[i + 8] = pinBytes[i]; } // encrypy the ean String encryptedEanHex = null; try { byte[] encryptedEan = mwkCipher.doFinal(eanBlock); encryptedEanHex = StringUtil.toHexString(encryptedEan); } catch (IllegalStateException e) { Debug.logError(e, module); } catch (IllegalBlockSizeException e) { Debug.logError(e, module); } catch (BadPaddingException e) { Debug.logError(e, module); } if (debug) { Debug.logInfo("encryptPin : " + pin + " / " + encryptedEanHex, module); } return encryptedEanHex; }
private StringBuffer outputKeyCreation(int loop, boolean kekOnly, String kekTest) { StringBuffer buf = new StringBuffer(); loop++; if (loop > 100) { // only loop 100 times; then throw an exception throw new IllegalStateException("Unable to create 128 byte keys in 100 tries"); } // place holder for the keys DHPrivateKey privateKey = null; DHPublicKey publicKey = null; if (!kekOnly) { KeyPair keyPair = null; try { keyPair = this.createKeys(); } catch (NoSuchAlgorithmException e) { Debug.logError(e, module); } catch (InvalidAlgorithmParameterException e) { Debug.logError(e, module); } catch (InvalidKeySpecException e) { Debug.logError(e, module); } if (keyPair != null) { publicKey = (DHPublicKey) keyPair.getPublic(); privateKey = (DHPrivateKey) keyPair.getPrivate(); if (publicKey == null || publicKey.getY().toByteArray().length != 128) { // run again until we get a 128 byte public key for VL return this.outputKeyCreation(loop, kekOnly, kekTest); } } else { Debug.logInfo("Returned a null KeyPair", module); return this.outputKeyCreation(loop, kekOnly, kekTest); } } else { // use our existing private key to generate a KEK try { privateKey = (DHPrivateKey) this.getPrivateKey(); } catch (Exception e) { Debug.logError(e, module); } } // the KEK byte[] kekBytes = null; try { kekBytes = this.generateKek(privateKey); } catch (NoSuchAlgorithmException e) { Debug.logError(e, module); } catch (InvalidKeySpecException e) { Debug.logError(e, module); } catch (InvalidKeyException e) { Debug.logError(e, module); } // the 3DES KEK value SecretKey loadedKek = this.getDesEdeKey(kekBytes); byte[] loadKekBytes = loadedKek.getEncoded(); // test the KEK Cipher cipher = this.getCipher(this.getKekKey(), Cipher.ENCRYPT_MODE); byte[] kekTestB = {0, 0, 0, 0, 0, 0, 0, 0}; byte[] kekTestC = new byte[0]; if (kekTest != null) { kekTestB = StringUtil.fromHexString(kekTest); } // encrypt the test bytes try { kekTestC = cipher.doFinal(kekTestB); } catch (Exception e) { Debug.logError(e, module); } if (!kekOnly) { // public key (just Y) BigInteger y = publicKey.getY(); byte[] yBytes = y.toByteArray(); String yHex = StringUtil.toHexString(yBytes); buf.append("======== Begin Public Key (Y @ ") .append(yBytes.length) .append(" / ") .append(yHex.length()) .append(") ========\n"); buf.append(yHex).append("\n"); buf.append("======== End Public Key ========\n\n"); // private key (just X) BigInteger x = privateKey.getX(); byte[] xBytes = x.toByteArray(); String xHex = StringUtil.toHexString(xBytes); buf.append("======== Begin Private Key (X @ ") .append(xBytes.length) .append(" / ") .append(xHex.length()) .append(") ========\n"); buf.append(xHex).append("\n"); buf.append("======== End Private Key ========\n\n"); // private key (full) byte[] privateBytes = privateKey.getEncoded(); String privateHex = StringUtil.toHexString(privateBytes); buf.append("======== Begin Private Key (Full @ ") .append(privateBytes.length) .append(" / ") .append(privateHex.length()) .append(") ========\n"); buf.append(privateHex).append("\n"); buf.append("======== End Private Key ========\n\n"); } if (kekBytes != null) { buf.append("======== Begin KEK (").append(kekBytes.length).append(") ========\n"); buf.append(StringUtil.toHexString(kekBytes)).append("\n"); buf.append("======== End KEK ========\n\n"); buf.append("======== Begin KEK (DES) (").append(loadKekBytes.length).append(") ========\n"); buf.append(StringUtil.toHexString(loadKekBytes)).append("\n"); buf.append("======== End KEK (DES) ========\n\n"); buf.append("======== Begin KEK Test (").append(kekTestC.length).append(") ========\n"); buf.append(StringUtil.toHexString(kekTestC)).append("\n"); buf.append("======== End KEK Test ========\n\n"); } else { Debug.logError("KEK came back empty", module); } return buf; }