public byte[] calculateDtlsMac( ProtocolVersion protocolVersion, ProtocolMessageType contentType, byte[] data, long dtlsSequenceNumber, int epochNumber) { byte[] SQN = ArrayConverter.concatenate( ArrayConverter.intToBytes(epochNumber, 2), ArrayConverter.longToUint48Bytes(dtlsSequenceNumber)); byte[] HDR = ArrayConverter.concatenate( contentType.getArrayValue(), protocolVersion.getValue(), ArrayConverter.intToBytes(data.length, 2)); writeMac.update(SQN); writeMac.update(HDR); writeMac.update(data); if (LOGGER.isDebugEnabled()) { LOGGER.debug( "The MAC will be caluculated over the following data: {}", ArrayConverter.bytesToHexString( ArrayConverter.concatenate( ArrayConverter.intToBytes(epochNumber, 2), ArrayConverter.longToUint48Bytes(sequenceNumber), HDR, data))); } byte[] result = writeMac.doFinal(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("MAC result: {}", ArrayConverter.bytesToHexString(result)); } return result; }
/** * From the Lucky13 paper: An individual record R (viewed as a byte sequence of length at least * zero) is processed as follows. The sender maintains an 8-byte sequence number SQN which is * incremented for each record sent, and forms a 5-byte field HDR consisting of a 1-byte type * field, a 2-byte version field, and a 2-byte length field. It then calculates a MAC over the * bytes SQN || HDR || R. * * @param protocolVersion * @param contentType * @param data * @return */ public byte[] calculateMac( ProtocolVersion protocolVersion, ProtocolMessageType contentType, byte[] data) { byte[] SQN = ArrayConverter.longToUint64Bytes(sequenceNumber); byte[] HDR = ArrayConverter.concatenate( contentType.getArrayValue(), protocolVersion.getValue(), ArrayConverter.intToBytes(data.length, 2)); writeMac.update(SQN); writeMac.update(HDR); writeMac.update(data); LOGGER.debug( "The MAC was caluculated over the following data: {}", ArrayConverter.bytesToHexString(ArrayConverter.concatenate(SQN, HDR, data))); byte[] result = writeMac.doFinal(); LOGGER.debug("MAC result: {}", ArrayConverter.bytesToHexString(result)); // we increment sequence number for the sent records sequenceNumber++; return result; }
/** * Takes correctly padded data and encrypts it * * @param data correctly padded data * @return * @throws CryptoException */ public byte[] encrypt(byte[] data) throws CryptoException { try { byte[] ciphertext; if (useExplicitIv) { ciphertext = ArrayConverter.concatenate(encryptIv.getIV(), encryptCipher.doFinal(data)); } else { encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey, encryptIv); ciphertext = encryptCipher.doFinal(data); encryptIv = new IvParameterSpec( Arrays.copyOfRange( ciphertext, ciphertext.length - decryptCipher.getBlockSize(), ciphertext.length)); } return ciphertext; } catch (BadPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException | InvalidKeyException ex) { throw new CryptoException(ex); } }
public TlsRecordBlockCipher(TlsContext tlsContext) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { this.tlsContext = tlsContext; ProtocolVersion protocolVersion = tlsContext.getProtocolVersion(); CipherSuite cipherSuite = tlsContext.getSelectedCipherSuite(); if (protocolVersion == ProtocolVersion.TLS11 || protocolVersion == ProtocolVersion.TLS12 || protocolVersion == ProtocolVersion.DTLS10 || protocolVersion == ProtocolVersion.DTLS12) { useExplicitIv = true; } bulkCipherAlg = BulkCipherAlgorithm.getBulkCipherAlgorithm(cipherSuite); CipherAlgorithm cipherAlg = AlgorithmResolver.getCipher(cipherSuite); int keySize = cipherAlg.getKeySize(); encryptCipher = Cipher.getInstance(cipherAlg.getJavaName()); decryptCipher = Cipher.getInstance(cipherAlg.getJavaName()); MacAlgorithm macAlg = AlgorithmResolver.getMacAlgorithm(cipherSuite); readMac = Mac.getInstance(macAlg.getJavaName()); writeMac = Mac.getInstance(macAlg.getJavaName()); int secretSetSize = (2 * keySize) + readMac.getMacLength() + writeMac.getMacLength(); if (!useExplicitIv) { secretSetSize += encryptCipher.getBlockSize() + decryptCipher.getBlockSize(); } byte[] masterSecret = tlsContext.getMasterSecret(); byte[] seed = tlsContext.getServerClientRandom(); PRFAlgorithm prfAlgorithm = AlgorithmResolver.getPRFAlgorithm( tlsContext.getProtocolVersion(), tlsContext.getSelectedCipherSuite()); byte[] keyBlock = PseudoRandomFunction.compute( prfAlgorithm, masterSecret, PseudoRandomFunction.KEY_EXPANSION_LABEL, seed, secretSetSize); LOGGER.debug("A new key block was generated: {}", ArrayConverter.bytesToHexString(keyBlock)); int offset = 0; byte[] clientMacWriteSecret = Arrays.copyOfRange(keyBlock, offset, offset + readMac.getMacLength()); offset += readMac.getMacLength(); LOGGER.debug( "Client MAC write Secret: {}", ArrayConverter.bytesToHexString(clientMacWriteSecret)); byte[] serverMacWriteSecret = Arrays.copyOfRange(keyBlock, offset, offset + writeMac.getMacLength()); offset += writeMac.getMacLength(); LOGGER.debug( "Server MAC write Secret: {}", ArrayConverter.bytesToHexString(serverMacWriteSecret)); clientWriteKey = Arrays.copyOfRange(keyBlock, offset, offset + keySize); offset += keySize; LOGGER.debug("Client write key: {}", ArrayConverter.bytesToHexString(clientWriteKey)); serverWriteKey = Arrays.copyOfRange(keyBlock, offset, offset + keySize); offset += keySize; LOGGER.debug("Server write key: {}", ArrayConverter.bytesToHexString(serverWriteKey)); byte[] clientWriteIv, serverWriteIv; if (useExplicitIv) { clientWriteIv = new byte[encryptCipher.getBlockSize()]; RandomHelper.getRandom().nextBytes(clientWriteIv); serverWriteIv = new byte[decryptCipher.getBlockSize()]; RandomHelper.getRandom().nextBytes(serverWriteIv); } else { clientWriteIv = Arrays.copyOfRange(keyBlock, offset, offset + encryptCipher.getBlockSize()); offset += encryptCipher.getBlockSize(); LOGGER.debug("Client write IV: {}", ArrayConverter.bytesToHexString(clientWriteIv)); serverWriteIv = Arrays.copyOfRange(keyBlock, offset, offset + decryptCipher.getBlockSize()); offset += decryptCipher.getBlockSize(); LOGGER.debug("Server write IV: {}", ArrayConverter.bytesToHexString(serverWriteIv)); } if (tlsContext.getMyConnectionEnd() == ConnectionEnd.CLIENT) { encryptIv = new IvParameterSpec(clientWriteIv); decryptIv = new IvParameterSpec(serverWriteIv); encryptKey = new SecretKeySpec(clientWriteKey, bulkCipherAlg.getJavaName()); decryptKey = new SecretKeySpec(serverWriteKey, bulkCipherAlg.getJavaName()); encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey, encryptIv); decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey, decryptIv); readMac.init(new SecretKeySpec(serverMacWriteSecret, macAlg.getJavaName())); writeMac.init(new SecretKeySpec(clientMacWriteSecret, macAlg.getJavaName())); } else { decryptIv = new IvParameterSpec(clientWriteIv); encryptIv = new IvParameterSpec(serverWriteIv); // todo check if this correct??? encryptCipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec(serverWriteKey, bulkCipherAlg.getJavaName()), encryptIv); decryptCipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec(clientWriteKey, bulkCipherAlg.getJavaName()), decryptIv); readMac.init(new SecretKeySpec(clientMacWriteSecret, macAlg.getJavaName())); writeMac.init(new SecretKeySpec(serverMacWriteSecret, macAlg.getJavaName())); } if (offset != keyBlock.length) { throw new CryptoException("Offset exceeded the generated key block length"); } // mac has to be put into one or more blocks, depending on the MAC/block // length // additionally, there is a need for one explicit IV block minimalEncryptedRecordLength = ((readMac.getMacLength() / decryptCipher.getBlockSize()) + 2) * decryptCipher.getBlockSize(); }
/** * @param message * @param pointer * @return */ @Override public int parseMessageAction(byte[] message, int pointer) { if (message[pointer] != HandshakeMessageType.SERVER_KEY_EXCHANGE.getValue()) { throw new InvalidMessageTypeException(HandshakeMessageType.SERVER_KEY_EXCHANGE); } protocolMessage.setType(message[pointer]); int currentPointer = pointer + HandshakeByteLength.MESSAGE_TYPE; int nextPointer = currentPointer + HandshakeByteLength.MESSAGE_TYPE_LENGTH; int length = ArrayConverter.bytesToInt(Arrays.copyOfRange(message, currentPointer, nextPointer)); protocolMessage.setLength(length); currentPointer = nextPointer; nextPointer = currentPointer + HandshakeByteLength.DH_PARAM_LENGTH; int pLength = ArrayConverter.bytesToInt(Arrays.copyOfRange(message, currentPointer, nextPointer)); protocolMessage.setpLength(pLength); currentPointer = nextPointer; nextPointer = currentPointer + protocolMessage.getpLength().getValue(); BigInteger p = new BigInteger(1, Arrays.copyOfRange(message, currentPointer, nextPointer)); protocolMessage.setP(p); currentPointer = nextPointer; nextPointer = currentPointer + HandshakeByteLength.DH_PARAM_LENGTH; int gLength = ArrayConverter.bytesToInt(Arrays.copyOfRange(message, currentPointer, nextPointer)); protocolMessage.setgLength(gLength); currentPointer = nextPointer; nextPointer = currentPointer + protocolMessage.getgLength().getValue(); BigInteger g = new BigInteger(1, Arrays.copyOfRange(message, currentPointer, nextPointer)); protocolMessage.setG(g); currentPointer = nextPointer; nextPointer = currentPointer + HandshakeByteLength.DH_PARAM_LENGTH; int publicKeyLength = ArrayConverter.bytesToInt(Arrays.copyOfRange(message, currentPointer, nextPointer)); protocolMessage.setPublicKeyLength(publicKeyLength); currentPointer = nextPointer; nextPointer = currentPointer + protocolMessage.getPublicKeyLength().getValue(); BigInteger publicKey = new BigInteger(1, Arrays.copyOfRange(message, currentPointer, nextPointer)); protocolMessage.setPublicKey(publicKey); byte[] dhParams = ArrayConverter.concatenate( ArrayConverter.intToBytes( protocolMessage.getpLength().getValue(), HandshakeByteLength.DH_PARAM_LENGTH), BigIntegers.asUnsignedByteArray(protocolMessage.getP().getValue()), ArrayConverter.intToBytes( protocolMessage.getgLength().getValue(), HandshakeByteLength.DH_PARAM_LENGTH), BigIntegers.asUnsignedByteArray(protocolMessage.getG().getValue()), ArrayConverter.intToBytes( protocolMessage.getPublicKeyLength().getValue(), HandshakeByteLength.DH_PARAM_LENGTH), BigIntegers.asUnsignedByteArray(protocolMessage.getPublicKey().getValue())); InputStream is = new ByteArrayInputStream(dhParams); try { ServerDHParams publicKeyParameters = ServerDHParams.parse(is); tlsContext.setServerDHParameters(publicKeyParameters); if (tlsContext.getProtocolVersion() == ProtocolVersion.DTLS12 || tlsContext.getProtocolVersion() == ProtocolVersion.TLS12) { currentPointer = nextPointer; nextPointer++; HashAlgorithm ha = HashAlgorithm.getHashAlgorithm(message[currentPointer]); protocolMessage.setHashAlgorithm(ha.getValue()); currentPointer = nextPointer; nextPointer++; SignatureAlgorithm sa = SignatureAlgorithm.getSignatureAlgorithm(message[currentPointer]); protocolMessage.setSignatureAlgorithm(sa.getValue()); } currentPointer = nextPointer; nextPointer = currentPointer + HandshakeByteLength.SIGNATURE_LENGTH; int signatureLength = ArrayConverter.bytesToInt(Arrays.copyOfRange(message, currentPointer, nextPointer)); protocolMessage.setSignatureLength(signatureLength); currentPointer = nextPointer; nextPointer = currentPointer + signatureLength; protocolMessage.setSignature(Arrays.copyOfRange(message, currentPointer, nextPointer)); protocolMessage.setCompleteResultingMessage( Arrays.copyOfRange(message, pointer, nextPointer)); return nextPointer; } catch (IOException ex) { throw new WorkflowExecutionException("DH public key parsing failed", ex); } }
@Override public byte[] prepareMessageAction() { // To use true DH ephemeral we need to precompute the prime number P(DH modulus) /** * int defaultPrimeProbability = 30; * * <p>DHParametersGenerator generator = new DHParametersGenerator(); //Genration of a higher bit * prime number takes too long (512 bits takes 2 seconds) generator.init(512, * defaultPrimeProbability, new SecureRandom()); DHParameters params = * generator.generateParameters(); */ DHPublicKeyParameters dhPublic; // fixed DH modulus P and DH generator G byte[] pArray = ArrayConverter.hexStringToByteArray( "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc" + "74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d" + "51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24" + "117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83" + "655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca1821" + "7c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf695" + "5817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff"); byte[] gArray = {0x02}; BigInteger p = new BigInteger(1, pArray); BigInteger g = new BigInteger(1, gArray); DHParameters params = new DHParameters(p, g); KeyGenerationParameters kgp = new DHKeyGenerationParameters(new SecureRandom(), params); DHKeyPairGenerator keyGen = new DHKeyPairGenerator(); keyGen.init(kgp); AsymmetricCipherKeyPair serverKeyPair = keyGen.generateKeyPair(); dhPublic = (DHPublicKeyParameters) serverKeyPair.getPublic(); DHPrivateKeyParameters dhPrivate = (DHPrivateKeyParameters) serverKeyPair.getPrivate(); protocolMessage.setG(dhPublic.getParameters().getG()); protocolMessage.setP(dhPublic.getParameters().getP()); protocolMessage.setPublicKey(dhPublic.getY()); protocolMessage.setPrivateKey(dhPrivate.getX()); tlsContext.setServerDHPrivateKeyParameters(dhPrivate); byte[] serializedP = BigIntegers.asUnsignedByteArray(protocolMessage.getP().getValue()); protocolMessage.setSerializedP(serializedP); protocolMessage.setSerializedPLength(serializedP.length); byte[] serializedG = BigIntegers.asUnsignedByteArray(protocolMessage.getG().getValue()); protocolMessage.setSerializedG(serializedG); protocolMessage.setSerializedGLength(serializedG.length); byte[] serializedPublicKey = BigIntegers.asUnsignedByteArray(protocolMessage.getPublicKey().getValue()); protocolMessage.setSerializedPublicKey(serializedPublicKey); protocolMessage.setSerializedPublicKeyLength(serializedPublicKey.length); byte[] dhParams = ArrayConverter.concatenate( ArrayConverter.intToBytes( protocolMessage.getSerializedPLength().getValue(), HandshakeByteLength.DH_PARAM_LENGTH), protocolMessage.getSerializedP().getValue(), ArrayConverter.intToBytes( protocolMessage.getSerializedGLength().getValue(), HandshakeByteLength.DH_PARAM_LENGTH), protocolMessage.getSerializedG().getValue(), ArrayConverter.intToBytes( protocolMessage.getSerializedPublicKeyLength().getValue(), HandshakeByteLength.DH_PARAM_LENGTH), protocolMessage.getSerializedPublicKey().getValue()); InputStream is = new ByteArrayInputStream(dhParams); try { ServerDHParams publicKeyParameters = ServerDHParams.parse(is); tlsContext.setServerDHParameters(publicKeyParameters); KeyStore ks = tlsContext.getKeyStore(); // could be extended to choose the algorithms depending on the certificate SignatureAndHashAlgorithm selectedSignatureHashAlgo = new SignatureAndHashAlgorithm(SignatureAlgorithm.RSA, HashAlgorithm.SHA1); protocolMessage.setSignatureAlgorithm( selectedSignatureHashAlgo.getSignatureAlgorithm().getValue()); protocolMessage.setHashAlgorithm(selectedSignatureHashAlgo.getHashAlgorithm().getValue()); Key key = ks.getKey(tlsContext.getAlias(), tlsContext.getPassword().toCharArray()); RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key; Signature instance = Signature.getInstance(selectedSignatureHashAlgo.getJavaName()); instance.initSign(rsaKey); LOGGER.debug( "SignatureAndHashAlgorithm for ServerKeyExchange message: {}", selectedSignatureHashAlgo.getJavaName()); byte[] toBeSignedBytes = ArrayConverter.concatenate( tlsContext.getClientRandom(), tlsContext.getServerRandom(), dhParams); instance.update(toBeSignedBytes); byte[] signature = instance.sign(); protocolMessage.setSignature(signature); protocolMessage.setSignatureLength(signature.length); byte[] result = ArrayConverter.concatenate( dhParams, new byte[] { protocolMessage.getHashAlgorithm().getValue(), protocolMessage.getSignatureAlgorithm().getValue() }, ArrayConverter.intToBytes( protocolMessage.getSignatureLength().getValue(), HandshakeByteLength.SIGNATURE_LENGTH), protocolMessage.getSignature().getValue()); protocolMessage.setLength(result.length); long header = (HandshakeMessageType.SERVER_KEY_EXCHANGE.getValue() << 24) + protocolMessage.getLength().getValue(); protocolMessage.setCompleteResultingMessage( ArrayConverter.concatenate(ArrayConverter.longToUint32Bytes(header), result)); } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | InvalidKeyException | SignatureException | IOException ex) { throw new ConfigurationException(ex.getLocalizedMessage(), ex); } return protocolMessage.getCompleteResultingMessage().getValue(); }
@Override public byte[] getFrame() { return ArrayConverter.concatenate(eapframe.getFrame(), frame); }