/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package userGeneratedContent.testbedPlugIns.layerPlugIns.layer2recodingScheme.RSA_AES_Channel_v0_001; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import staticContent.framework.AnonNode; import staticContent.framework.config.Settings; import staticContent.framework.message.MixMessage; import staticContent.framework.message.Reply; import staticContent.framework.message.Request; import staticContent.framework.routing.RoutingMode; import staticContent.framework.util.Util; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer2recodingScheme.RSA_AES_Channel_v0_001.MixPlugIn.ChannelData; public class RSA_AES_Channel { private AnonNode owner; private RSA_AES_Channel_Config config; private Settings settings; private Cipher asymmetricCipher; private Cipher asymmetricCiphers[]; private KeyGenerator symKeyGenerator; private KeyGenerator macKeyGenerator; private SecretKey[] macKeys; private SecretKey[] sessionKeysForRequestChannel; private SecretKey[] sessionKeysForReplyChannel; private IvParameterSpec[] sessionIVsForRequestChannel; private IvParameterSpec[] sessionIVsForReplyChannel; private Cipher[] symmetricEncryptCiphers; private Cipher[] symmetricDecryptCiphers; private SecureRandom secureRandom; private boolean channelEstablished = false; public RSA_AES_Channel(AnonNode owner, RSA_AES_Channel_Config config) { this.owner = owner; this.config = config; this.settings = owner.getSettings(); } public void initAsClient() { this.macKeys = new SecretKey[config.routeLength]; this.sessionKeysForRequestChannel = new SecretKey[config.routeLength]; this.sessionKeysForReplyChannel = new SecretKey[config.routeLength]; this.sessionIVsForRequestChannel = new IvParameterSpec[config.routeLength]; this.sessionIVsForReplyChannel = new IvParameterSpec[config.routeLength]; this.symmetricEncryptCiphers = new Cipher[config.routeLength]; this.symmetricDecryptCiphers = new Cipher[config.routeLength]; this.asymmetricCiphers = new Cipher[config.routeLength]; // create key generators and ciphers try { this.secureRandom = SecureRandom.getInstance(config.PRNG_ALGORITHM); this.symKeyGenerator = KeyGenerator.getInstance(config.NAME_OF_SYM_KEY_GENERATOR); this.symKeyGenerator.init(config.SYM_KEY_LENGTH * 8); this.macKeyGenerator = KeyGenerator.getInstance(config.MAC_ALGORITHM); this.macKeyGenerator.init(config.MAC_KEY_LENGTH * 8); } catch (Exception e) { e.printStackTrace(); } } public void initAsRecoder() { try { this.secureRandom = SecureRandom.getInstance(config.PRNG_ALGORITHM); this.asymmetricCipher = Cipher.getInstance(config.ASYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); this.asymmetricCipher.init(Cipher.DECRYPT_MODE, config.keyPair.getPrivate()); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("could not init asym cipher at mix"); } } // returns the message's payload public synchronized Request recodeMessage(Request message, ChannelData channelData) { if (channelData.macKey == null) return recodeChannelEstablishMessage(message, channelData); else return recodeChannelMessage(message, channelData); } private Request recodeChannelEstablishMessage(Request message, ChannelData channelData) { String cipherTextHash = null; try { if (config.DEBUG_ON) { cipherTextHash = Util.md5(message.getByteMessage()); } // decrypt asymmetrically encrypted part byte[] asymCipherText = Arrays.copyOfRange(message.getByteMessage(), 0, asymmetricCipher.getBlockSize()); byte[] asymPlaintext = asymmetricCipher.doFinal(asymCipherText); // extract data from derypted header (= first part of the "asymPlaintext") byte[] mac; SecretKey symKey; IvParameterSpec initVector; int payloadLength; int pointer = 0; mac = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.MAC_LENGTH); if (owner.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { // determine next hop if source routing is enabled byte[] nextHopAddress = Arrays.copyOfRange(asymPlaintext, pointer, pointer += 4); int address = Util.byteArrayToInt(nextHopAddress); if (address == owner.PUBLIC_PSEUDONYM) { // this mix is the last hop if (owner.DISPLAY_ROUTE_INFO) System.out.println(""+owner +" setting nextHopAddress to \"LAST HOP\""); message.nextHopAddress = MixMessage.NONE; channelData.nextHopAddress = MixMessage.NONE; } else { // this mix is not the last hop if (owner.DISPLAY_ROUTE_INFO) System.out.println(""+owner +" setting nextHopAddress to " +address); message.nextHopAddress = address; channelData.nextHopAddress = address; } } byte[] macKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.MAC_KEY_LENGTH); channelData.macKey = new SecretKeySpec(macKeyAsByteArray, config.MAC_ALGORITHM); byte[] symKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.SYM_KEY_LENGTH); symKey = new SecretKeySpec(symKeyAsByteArray, config.SYM_CRYPTOGRAPHY_ALGORITHM); byte[] ivAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.IV_LENGTH); initVector = new IvParameterSpec(ivAsByteArray); channelData.decryptCipher = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); channelData.decryptCipher.init(Cipher.DECRYPT_MODE, symKey, initVector); if (owner.IS_DUPLEX) { byte[] symRepKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.SYM_KEY_LENGTH); SecretKey symRepKey = new SecretKeySpec(symRepKeyAsByteArray, config.SYM_CRYPTOGRAPHY_ALGORITHM); ivAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.IV_LENGTH); initVector = new IvParameterSpec(ivAsByteArray); channelData.encryptCipher = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); channelData.encryptCipher.init(Cipher.ENCRYPT_MODE, symRepKey, initVector); } byte[] payloadLengthAsArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.LENGTH_HEADER_LENGTH); payloadLength = Util.byteArrayToInt(payloadLengthAsArray); // decrypt symmetrically encrypted part byte[] symCipherText = Arrays.copyOfRange(message.getByteMessage(), asymmetricCipher.getBlockSize(), message.getByteMessage().length); byte[] symPlaintext = channelData.decryptCipher.update(symCipherText); assert symCipherText.length == symPlaintext.length: owner.toString() +": symCipherText.length != symPlaintext.length (" +symCipherText.length +"!=" +symPlaintext.length +")"; byte[] plaintext = Util.concatArrays(asymPlaintext, symPlaintext); //System.out.println("-[-[[-mix: asymPlaintext (" +Util.md5(asymPlaintext) +") + symPlaintext ("+Util.md5(symPlaintext) +") -> (" +Util.md5(asymCipherText) +"), (" +Util.md5(symCipherText) +") using " +Util.md5(symKeyAsByteArray) +", " +Util.md5(channelData.decryptCipher.getIV())); // validate mac Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(channelData.macKey); byte[] signedData = Arrays.copyOfRange(plaintext, config.MAC_LENGTH, plaintext.length); if (config.DEBUG_ON) System.out.println(owner +" " +cipherTextHash +" -> " +Util.md5(signedData)); byte[] locallyGeneratedMac = macGenerator.doFinal(signedData); if (!Arrays.equals(locallyGeneratedMac, mac)) { System.out.println("wrong MAC!"); //System.out.println("%&/%&/ wrong MAC: " +Util.md5(mac) + ", signedData: " +Util.md5(signedData)); // TODO return null; } if (config.PERFORM_REPLY_DETECTION) if (config.replayDetection.isReplay(macKeyAsByteArray)) return null; // extract payload (= receive data without headers and padding) if (payloadLength == 0) {// dummy message.setByteMessage(new byte[0]); } else { message.setByteMessage(Arrays.copyOfRange(plaintext, pointer, (pointer + payloadLength))); } return message; } catch (Exception e) { e.printStackTrace(); System.err.println(owner +"Exception-message (ciphertext): " +Util.md5(message.getByteMessage())); return null; } } private Request recodeChannelMessage(Request message, ChannelData channelData) { try { String ct = Util.md5(message.getByteMessage()); int ctlength = message.getByteMessage().length; byte[] plaintext = channelData.decryptCipher.update(message.getByteMessage()); assert ctlength == message.getByteMessage().length; // validate mac int pointer = 0; byte[] mac = Arrays.copyOfRange(plaintext, pointer, pointer += config.MAC_LENGTH); Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(channelData.macKey); byte[] signedData = Arrays.copyOfRange(plaintext, config.MAC_LENGTH, plaintext.length); byte[] locallyGeneratedMac = macGenerator.doFinal(signedData); if (!Arrays.equals(locallyGeneratedMac, mac)) { System.err.println(owner.toString() +"wrong MAC cm! " +message +": " +ct +" mac of " +Util.md5(signedData) +" is " +Util.md5(mac)); // TODO return null; } // extract payload (= receive data without headers and padding) byte[] payloadLengthAsArray = Arrays.copyOfRange(plaintext, pointer, pointer += config.LENGTH_HEADER_LENGTH); int payloadLength = Util.byteArrayToInt(payloadLengthAsArray); if (payloadLength == 0) { // dummy message.setByteMessage(new byte[0]); } else { message.setByteMessage(Arrays.copyOfRange(plaintext, pointer, pointer += payloadLength)); } //System.out.println("0=0=0=0=0: cm@" +mix.toString() +": symPlaintext:"+Util.md5(plaintext) +" <- " +ct +", received mac:" +Util.md5(mac) +", locallyGeneratedMac: " +Util.md5(locallyGeneratedMac) +", signedData: " +Util.md5(signedData) +"; key: " +Util.md5(channelData.reqKey.getEncoded()) +"; iv: "+Util.md5(channelData.decryptCipher.getIV()) ); if (owner.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { // set next hop address if source routing is enabled if (channelData.nextHopAddress == owner.PUBLIC_PSEUDONYM) { // this mix is the last hop if (owner.DISPLAY_ROUTE_INFO) System.out.println(""+owner +" setting nextHopAddress to \"LAST HOP\""); message.nextHopAddress = MixMessage.NONE; } else { // this mix is not the last hop if (owner.DISPLAY_ROUTE_INFO) System.out.println(""+owner +" setting nextHopAddress to " +channelData.nextHopAddress); message.nextHopAddress = channelData.nextHopAddress; } } return message; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } catch (InvalidKeyException e) { e.printStackTrace(); return null; } } public synchronized Reply recodeReply(Reply message, ChannelData channelData) { boolean isLastMix = false; if (owner.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING && owner.IS_LAST_MIX) isLastMix = true; else if (channelData.nextHopAddress == MixMessage.NONE) isLastMix = true; if (isLastMix) { if (message.getByteMessage() == null) // dummy message.setByteMessage(new byte[0]); if (message.getByteMessage().length > config.MAX_PAYLOAD) throw new RuntimeException("can't send more than " +config.MAX_PAYLOAD +" bytes in one message"); // add length header byte[] lengthHeader = Util.intToByteArray(message.getByteMessage().length); message.setByteMessage(Util.concatArrays(lengthHeader, message.getByteMessage())); // add padding int desiredLength = config.MAX_PAYLOAD + config.LENGTH_HEADER_LENGTH; int diffToBlockSize = desiredLength % channelData.encryptCipher.getBlockSize(); if (diffToBlockSize != 0) desiredLength += channelData.encryptCipher.getBlockSize() - diffToBlockSize; byte[] padding = new byte[desiredLength - message.getByteMessage().length]; secureRandom.nextBytes(padding); message.setByteMessage(Util.concatArrays(message.getByteMessage(), padding)); assert (message.getByteMessage().length % channelData.encryptCipher.getBlockSize()) == 0; } byte[] result = channelData.encryptCipher.update(message.getByteMessage()); // System.out.println(" oOoOo " +owner.toString() +" sende " +Util.md5(message.getByteMessage()) + " -> " +Util.md5(result) +"( for " +message.getOwner() +", " +channelData +")"); assert result.length == message.getByteMessage().length: "" +result.length +" != " +message.getByteMessage().length; message.setByteMessage(result); return message; } public synchronized Request applyLayeredEncryption(Request request) { if (!channelEstablished) { channelEstablished = true; return createChannelEstablishMessage(request); } else { return createChannelMessage(request); } } private Request createChannelEstablishMessage(Request request) { byte[] payload = request.getByteMessage(); if (payload == null) // dummy payload = new byte[0]; if (payload.length > config.MAX_PAYLOAD) throw new RuntimeException("can't send more than " +config.MAX_PAYLOAD +" bytes in one message"); // generate keys, init ciphers etc. for (int i=config.routeLength-1; i>=0; i--) { try { this.asymmetricCiphers[i] = Cipher.getInstance(settings.getProperty("ASYM_CRYPTOGRAPHY_ALGORITHM"), owner.CRYPTO_PROVIDER); if (owner.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING) this.asymmetricCiphers[i].init(Cipher.ENCRYPT_MODE, config.publicKeysOfMixes[i]); else { this.asymmetricCiphers[i].init(Cipher.ENCRYPT_MODE, config.publicKeysOfMixes[request.route[i]]); // TODO: possible side-effect -> won't work anymore if mix-ids are chosen differently System.out.println("" +owner +" using public key " +Util.md5(config.publicKeysOfMixes[request.route[i]].getEncoded()) +"for mix " +request.route[i]); } this.sessionKeysForRequestChannel[i] = symKeyGenerator.generateKey(); this.macKeys[i] = macKeyGenerator.generateKey(); byte[] ivReq = new byte[sessionKeysForRequestChannel[i].getEncoded().length]; secureRandom.nextBytes(ivReq); this.sessionIVsForRequestChannel[i] = new IvParameterSpec(ivReq); this.symmetricEncryptCiphers[i] = Cipher.getInstance(settings.getProperty("SYM_CRYPTOGRAPHY_ALGORITHM"), owner.CRYPTO_PROVIDER); this.symmetricEncryptCiphers[i].init(Cipher.ENCRYPT_MODE, sessionKeysForRequestChannel[i], sessionIVsForRequestChannel[i]); if (owner.IS_DUPLEX) { this.sessionKeysForReplyChannel[i] = symKeyGenerator.generateKey(); byte[] ivRep = new byte[sessionKeysForReplyChannel[i].getEncoded().length]; secureRandom.nextBytes(ivRep); this.sessionIVsForReplyChannel[i] = new IvParameterSpec(ivRep); this.symmetricDecryptCiphers[i] = Cipher.getInstance(settings.getProperty("SYM_CRYPTOGRAPHY_ALGORITHM"), owner.CRYPTO_PROVIDER); this.symmetricDecryptCiphers[i].init(Cipher.DECRYPT_MODE, sessionKeysForReplyChannel[i], sessionIVsForReplyChannel[i]); } } catch (Exception e) { e.printStackTrace(); } } String s = ""; // add header and encryption layer for each mix for (int i=config.routeLength-1; i>=0; i--) { try { // generate header (without mac; must be added later) byte[] mac; byte[] plaintext; byte[] payloadLength = Util.intToByteArray(payload.length); // concat header and payload if (owner.IS_DUPLEX) { plaintext = Util.concatArrays(new byte[][] { macKeys[i].getEncoded(), sessionKeysForRequestChannel[i].getEncoded(), sessionIVsForRequestChannel[i].getIV(), sessionKeysForReplyChannel[i].getEncoded(), sessionIVsForReplyChannel[i].getIV(), payloadLength, payload }); } else { plaintext = Util.concatArrays(new byte[][] { macKeys[i].getEncoded(), sessionKeysForRequestChannel[i].getEncoded(), sessionIVsForRequestChannel[i].getIV(), payloadLength, payload }); } if (owner.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { // add route-info if (i == config.routeLength-1) { // last mix plaintext = Util.concatArrays(Util.intToByteArray(request.route[request.route.length-1]), plaintext); } else { plaintext = Util.concatArrays(Util.intToByteArray(request.route[i+1]), plaintext); } } // add padding for last mix' payload if (i == config.routeLength-1 && payload.length != config.MAX_PAYLOAD) { byte[] padding = new byte[config.MAX_PAYLOAD - payload.length]; secureRandom.nextBytes(padding); plaintext = Util.concatArrays(plaintext, padding); } // make sure message lengths are a multiple of the sym crypto algorithm's block size int symPlaintextLength = (plaintext.length + config.MAC_LENGTH) - asymmetricCiphers[i].getBlockSize(); // note that "getBlockSize()" returns the max length of a plaintext encryptable with the asym. cipher (= aysm. keylength - overhead) int diffToBlockSize = symPlaintextLength % symmetricEncryptCiphers[i].getBlockSize(); if (diffToBlockSize != 0) { byte[] padding = new byte[symmetricEncryptCiphers[i].getBlockSize() - diffToBlockSize]; secureRandom.nextBytes(padding); plaintext = Util.concatArrays(plaintext, padding); symPlaintextLength += padding.length; } // add mac to header Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(macKeys[i]); mac = macGenerator.doFinal(plaintext); plaintext = Util.concatArrays(mac, plaintext); assert macKeys[i].getEncoded().length == config.MAC_KEY_LENGTH; assert sessionKeysForRequestChannel[i].getEncoded().length == config.SYM_KEY_LENGTH; assert sessionIVsForRequestChannel[i].getIV().length == config.IV_LENGTH; assert mac.length == config.MAC_LENGTH; if (owner.IS_DUPLEX) { assert sessionKeysForReplyChannel[i].getEncoded().length == config.SYM_KEY_LENGTH; assert sessionIVsForReplyChannel[i].getIV().length == config.IV_LENGTH; } // encrypt message; asymmetric part byte[] cipherText; byte[] asymPt = Arrays.copyOfRange(plaintext, 0, asymmetricCiphers[i].getBlockSize()); byte[] asymCipherText = asymmetricCiphers[i].doFinal(asymPt); // encrypt message; symmetric part byte[] symPt = Arrays.copyOfRange(plaintext, asymmetricCiphers[i].getBlockSize(), plaintext.length); assert symPt.length == symPlaintextLength; assert symPt.length % symmetricEncryptCiphers[i].getBlockSize() == 0; byte[] symCipherText = symmetricEncryptCiphers[i].update(symPt); assert symCipherText.length == symPt.length; cipherText = Util.concatArrays(asymCipherText, symCipherText); assert cipherText.length % symmetricEncryptCiphers[i].getBlockSize() == 0; if (config.DEBUG_ON) { //System.out.println("-[-[[: asymPlaintext (" +Util.md5(asymPt) +") + symPlaintext ("+Util.md5(symPt) +") -> (" +Util.md5(asymCipherText) +"), (" +Util.md5(symCipherText) +") using " +Util.md5(sessionKeysForRequestChannel[i].getEncoded()) +", " +Util.md5(symmetricEncryptCiphers[i].getIV())); s += "[msg mix "+i +":" //+"\n\tlength of mac: " +mac.length //+"\n\tlength of macKey: " +macKey.getEncoded().length //+"\n\tlength of symKey: " +symKey.getEncoded().length //+"\n\tlength of iv: " +initVector.getIV().length //+"\n\tlength of payload: " +payload.length //+"\n\tlength of asym ciphertext: " +asymCipherText.length //+"\n\tlength of sym ciphertext: " +symCipherText.length //+"\n\ttotal length of ciphertext: " +cipherText.length +"(pt " +Util.md5(Arrays.copyOfRange(plaintext, config.MAC_LENGTH, plaintext.length)) +")" +", (ct: " +Util.md5(cipherText) +")" //+"\n\thash of public key: " +Util.md5(publicKeysOfMixes[i].getEncoded()) +"] " ; //System.out.println(Util.display(Arrays.copyOfRange(plaintext, MAC_LENGTH, plaintext.length))); //System.out.println(Util.display(cipherText)); } payload = cipherText; } catch (Exception e) { e.printStackTrace(); } } System.out.println(s); s = owner.toString() +": "; request.setByteMessage(payload); return request; } private Request createChannelMessage(Request request) { byte[] payload = request.getByteMessage(); if (payload == null) // dummy payload = new byte[0]; if (payload.length > config.MAX_PAYLOAD) throw new RuntimeException("can't send more than " +config.MAX_PAYLOAD +" bytes in one message"); for (int i=config.routeLength-1; i>=0; i--) { // add length-header try { byte[] payloadLength = Util.intToByteArray(payload.length); payload = Util.concatArrays(payloadLength, payload); // add padding for last mix' payload if (i == config.routeLength-1 && payload.length != config.MAX_PAYLOAD) { byte[] padding = new byte[(config.MAX_PAYLOAD + config.MAC_LENGTH) - payload.length]; secureRandom.nextBytes(padding); payload = Util.concatArrays(payload, padding); } // make sure message lengths are a multiple of the sym crypto algorithm's block size int symPlaintextLength = payload.length + config.MAC_LENGTH; int diffToBlockSize = symPlaintextLength % symmetricEncryptCiphers[i].getBlockSize(); if (diffToBlockSize != 0) { byte[] padding = new byte[symmetricEncryptCiphers[i].getBlockSize() - diffToBlockSize]; secureRandom.nextBytes(padding); payload = Util.concatArrays(payload, padding); symPlaintextLength += padding.length; } // add mac to header Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(macKeys[i]); byte[] mac = macGenerator.doFinal(payload); payload = Util.concatArrays(mac, payload); byte[] ciphertext = symmetricEncryptCiphers[i].update(payload); assert ciphertext.length == payload.length; payload = ciphertext; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); throw new RuntimeException(""); } catch (InvalidKeyException e) { e.printStackTrace(); throw new RuntimeException(""); } } request.setByteMessage(payload); return request; } public int getMaxPayloadForNextMessage() { return config.MAX_PAYLOAD; } public int getMaxPayloadForNextReply() { return config.MAX_PAYLOAD; } public Reply extractPayload(Reply reply) { byte[] result = reply.getByteMessage(); //byte[] forLater = reply.getByteMessage().clone(); for (int i=0; i<config.routeLength; i++) { Cipher cipher = symmetricDecryptCiphers[i]; byte[] plaintext = cipher.update(result); assert plaintext.length == result.length; //System.out.println(" oOoOo " +owner.toString() +" haber empfangen: " +Util.md5(plaintext) + " <- " +Util.md5(result)); result = plaintext; } int lengthOfPayload = Util.byteArrayToInt(Arrays.copyOf(result, 4)); try { result = Arrays.copyOfRange(result, config.LENGTH_HEADER_LENGTH, config.LENGTH_HEADER_LENGTH + lengthOfPayload); } catch (Exception e) { //System.err.println("oOoOo " +owner.toString() +" haber empfangen: " +Util.md5(result) + " <- " +Util.md5(forLater)); e.printStackTrace(); } reply.setByteMessage(result); return reply; } }