/******************************************************************************* * 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.Sphinx_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.Sphinx_Channel_v0_001.MixPlugIn.ChannelData; public class Sphinx_Channel { private AnonNode owner; private Sphinx_Channel_Config config; private Sphinx_Config configSphinx; 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 Sphinx_Channel(AnonNode owner, Sphinx_Channel_Config config, Sphinx_Config configSphinx) { this.owner = owner; this.config = config; this.configSphinx = configSphinx; 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) { try { EncryptedMessage msg = new EncryptedMessage(message, configSphinx); // System.out.println("processing at " + new String(Hex.encode(_name))); if (msg.alpBetaGam[0].length != 32) { System.out.println("alpha is not an element of ECC group curve25519"); return null; } byte[] sharedSecret = Sphinx.genSharedSecret(msg.alpBetaGam[0], configSphinx.privateKey); byte[] tag = Sphinx.hashTau(sharedSecret); if (config.PERFORM_REPLY_DETECTION) { if (config.replayDetection.isReplay(tag)) { System.out.println("the shared secret was already seen"); return null; } } if (!Arrays.equals(msg.alpBetaGam[2], Sphinx.mu(Sphinx.hashMu(sharedSecret, configSphinx.SECURITY_PARAMETER_SIZE), msg.alpBetaGam[1], configSphinx.SECURITY_PARAMETER_SIZE))) { System.err.println("MAC mismatch " +message +", " +Util.md5(message.getByteMessage()) +", " +message +", " +owner); // TODO: remove return null; } SecureRandom prng = SecureRandom.getInstance(config.PRNG_ALGORITHM); prng.setSeed(sharedSecret); byte[] symKeyReqAsByte= new byte[config.SYM_KEY_LENGTH]; prng.nextBytes(symKeyReqAsByte); SecretKey symKey = new SecretKeySpec(symKeyReqAsByte, config.SYM_CRYPTOGRAPHY_ALGORITHM); byte[] ivReq = new byte[config.IV_LENGTH]; prng.nextBytes(ivReq); IvParameterSpec initVector = new IvParameterSpec(ivReq); byte[] macKeyAsByteArray = new byte[config.MAC_KEY_LENGTH]; prng.nextBytes(macKeyAsByteArray); channelData.decryptCipher = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); channelData.decryptCipher.init(Cipher.DECRYPT_MODE, symKey, initVector); channelData.macKey = new SecretKeySpec(macKeyAsByteArray, config.MAC_ALGORITHM); if(owner.IS_DUPLEX) { byte[] symKeyRepAsByte = new byte[config.SYM_KEY_LENGTH]; prng.nextBytes(symKeyRepAsByte); SecretKey symKeyRep = new SecretKeySpec(symKeyRepAsByte, config.SYM_CRYPTOGRAPHY_ALGORITHM); byte[] ivRep = new byte[config.IV_LENGTH]; prng.nextBytes(ivRep); IvParameterSpec initVectorRep = new IvParameterSpec(ivRep); channelData.encryptCipher = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); channelData.encryptCipher.init(Cipher.ENCRYPT_MODE, symKeyRep, initVectorRep); } /* * if (anonNode.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING) { outputStrategyLayerMix.addRequest(request); } else if (anonNode.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { byte[][] splitted = Util.split(getRouteHeaderSize(anonNode), request.getByteMessage()); UnpackedIdArray routeInfo = MixList.unpackIdArrayWithPos(splitted[0]); if (routeInfo.pos >= routeInfo.route.length) { if (anonNode.DISPLAY_ROUTE_INFO) System.out.println(""+anonNode +" setting nextHopAddress to \"LAST HOP\" (pos: " +routeInfo.pos +")"); request.nextHopAddress = MixMessage.NONE; request.setByteMessage(splitted[1]); } else { if (anonNode.DISPLAY_ROUTE_INFO) System.out.println(""+anonNode +" setting nextHopAddress to " +routeInfo.route[routeInfo.pos] +", pos: " +routeInfo.pos); request.nextHopAddress = routeInfo.route[routeInfo.pos]; routeInfo.pos++; System.arraycopy(MixList.packIdArray(routeInfo), 0, request.getByteMessage(), 0, getRouteHeaderSize(anonNode)); } outputStrategyLayerMix.addRequest(request); } else if (anonNode.ROUTING_MODE == RoutingMode.DYNAMIC_ROUTING) { request.nextHopAddress = anonNode.mixList.getRandomMixId(); outputStrategyLayerMix.addRequest(request); } else { throw new RuntimeException("not supported routing mode: " +anonNode.ROUTING_MODE); } */ byte[] B = Sphinx.xor(Util.concatArrays(msg.alpBetaGam[1], Sphinx.ZERO32), Sphinx.rho(Sphinx.hashRho(sharedSecret, configSphinx.SECURITY_PARAMETER_SIZE), configSphinx.ROUTE_LENGTH, configSphinx.SECURITY_PARAMETER_SIZE)); if (B[0] == MixHeader.MIX_PREFIX) { // this mix is not the final hop -> forward message to next hop byte[] nextMixId = Arrays.copyOf(B, configSphinx.SECURITY_PARAMETER_SIZE); //byte[] remaining = Arrays.copyOfRange(B, Config._k, B.length); byte[] blindingFactor = Sphinx.hashB(msg.alpBetaGam[0], sharedSecret); msg.alpBetaGam[0] = Sphinx.genSharedSecret(msg.alpBetaGam[0], blindingFactor); msg.alpBetaGam[2] = Arrays.copyOfRange(B, configSphinx.SECURITY_PARAMETER_SIZE, 2 * configSphinx.SECURITY_PARAMETER_SIZE); msg.alpBetaGam[1] = Arrays.copyOfRange(B, configSphinx.SECURITY_PARAMETER_SIZE * 2, B.length); msg.delta = Sphinx.pii(Sphinx.hashPi(sharedSecret, configSphinx.SECURITY_PARAMETER_SIZE), msg.delta, configSphinx.SECURITY_PARAMETER_SIZE); //System.out.println("this mix is not the final hop -> forward message to next hop"); if (owner.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { message.nextHopAddress = configSphinx.getGlobalMixIdFor(nextMixId); channelData.nextHopAddress = configSphinx.getGlobalMixIdFor(nextMixId); assert message.nextHopAddress != -1; if (owner.DISPLAY_ROUTE_INFO) System.out.println(""+owner +" setting nextHopAddress to " +message.nextHopAddress); } message.setByteMessage(msg.toByteArray(configSphinx)); return message; } else if (B[0] == MixHeader.SPECIAL_DEST_PREFIX) { // this mix is the final hop; message is a request //System.out.println("this mix is the final hop; message is a request"); msg.delta = Sphinx.pii(Sphinx.hashPi(sharedSecret, configSphinx.SECURITY_PARAMETER_SIZE), msg.delta, configSphinx.SECURITY_PARAMETER_SIZE); msg.delta = Sphinx.unpadBody(msg.delta, configSphinx); byte[] payload = Arrays.copyOfRange(msg.delta, configSphinx.SECURITY_PARAMETER_SIZE, msg.delta.length); //System.out.println("payload received by last mix: " +Util.md5(payload)); message.setByteMessage(payload); if (owner.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { message.nextHopAddress = MixMessage.NONE; channelData.nextHopAddress = MixMessage.NONE; assert message.nextHopAddress != -1; if (owner.DISPLAY_ROUTE_INFO) System.out.println(""+owner +" setting nextHopAddress to \"LAST HOP\""); } return message; } else if (B[0] == MixHeader.CLIENT_PREFIX) { // this mix is the final hop; message is a reply //System.out.println("this mix is the final hop; message is a reply"); byte[] addressData = Arrays.copyOf(B, configSphinx.SECURITY_PARAMETER_SIZE); byte[] clientId = Sphinx.extractClientId(addressData, configSphinx); byte[] replyId = Sphinx.extractReplyId(addressData, configSphinx); msg.delta = Sphinx.pii(Sphinx.hashPi(sharedSecret, configSphinx.SECURITY_PARAMETER_SIZE), msg.delta, configSphinx.SECURITY_PARAMETER_SIZE); message.nextHopAddress = Util.byteArrayToInt(owner.getInfoService().getValue(new String(clientId, "UTF-8"))); // TODO -> mechanism to forward replies... message.setByteMessage(Util.concatArrays(replyId, msg.delta)); return message; } else { System.err.println("received unknown id"); return null; } } catch (Exception e) { e.printStackTrace(); 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"); //System.out.println("START CHANNEL ESTABLISH MESSAGE!!!"); EncryptedMessage em = new EncryptedMessage(); try { int[] route; if (owner.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING) route = owner.mixList.mixIDs; else route = request.route; MixHeader header = new MixHeader(configSphinx, secureRandom, new Route(route, configSphinx)); byte[] addressForLastMix = new byte[configSphinx.SECURITY_PARAMETER_SIZE]; secureRandom.nextBytes(addressForLastMix); addressForLastMix[0] = MixHeader.SPECIAL_DEST_PREFIX; em.alpBetaGam = header.createHeader(addressForLastMix); byte[] body = Util.concatArrays(Sphinx.ZERO16, payload); body = Sphinx.padBody(body, configSphinx); assert body.length == configSphinx.DELTA_SIZE; byte[][] deltas = new byte[configSphinx.ROUTE_LENGTH][]; // delta v-1 byte[] delta = Sphinx.pi(Sphinx.hashPi(header.getSecret(configSphinx.ROUTE_LENGTH-1), configSphinx.SECURITY_PARAMETER_SIZE), body, configSphinx.SECURITY_PARAMETER_SIZE); deltas[configSphinx.ROUTE_LENGTH-1] = delta; // deltas for 0<=i<v-1 for (int i=configSphinx.ROUTE_LENGTH-2; i>=0; i--) { delta = Sphinx.pi(Sphinx.hashPi(header.getSecret(i), configSphinx.SECURITY_PARAMETER_SIZE), delta, configSphinx.SECURITY_PARAMETER_SIZE); deltas[i] = delta; } em.delta = deltas[0]; // generate keys, init ciphers etc. for (int i=config.routeLength-1; i>=0; i--) { 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]); } SecureRandom prng = SecureRandom.getInstance(config.PRNG_ALGORITHM); prng.setSeed(header.getSecret(i)); byte[] sessionKeyRequest= new byte[config.SYM_KEY_LENGTH]; prng.nextBytes(sessionKeyRequest); this.sessionKeysForRequestChannel[i] = new SecretKeySpec(sessionKeyRequest, config.SYM_CRYPTOGRAPHY_ALGORITHM); byte[] ivReq = new byte[sessionKeysForRequestChannel[i].getEncoded().length]; prng.nextBytes(ivReq); this.sessionIVsForRequestChannel[i] = new IvParameterSpec(ivReq); byte[] macKey = new byte[config.MAC_KEY_LENGTH]; prng.nextBytes(macKey); this.macKeys[i] = new SecretKeySpec(macKey, config.MAC_ALGORITHM); 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) { byte[] sessionKeyReply= new byte[config.SYM_KEY_LENGTH]; prng.nextBytes(sessionKeyReply); this.sessionKeysForReplyChannel[i] = new SecretKeySpec(sessionKeyReply, config.SYM_CRYPTOGRAPHY_ALGORITHM); byte[] ivRep = new byte[sessionKeysForReplyChannel[i].getEncoded().length]; prng.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(); } request.setByteMessage(em.toByteArray(configSphinx)); 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; } }