Refactor serialization and added message validation
This commit is contained in:
@@ -1,18 +1,20 @@
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import model.BlockChain;
|
import model.BlockChain;
|
||||||
import model.ChatClient;
|
import util.ChatClient;
|
||||||
import model.Miner;
|
import model.Miner;
|
||||||
|
import util.FileManagement;
|
||||||
import util.Security;
|
import util.Security;
|
||||||
|
|
||||||
public final class Main {
|
public final class Main {
|
||||||
|
|
||||||
public static void main(final String[] args) throws InterruptedException {
|
public static void main(final String[] args) throws InterruptedException, IOException {
|
||||||
|
|
||||||
final var nThreads = Runtime.getRuntime().availableProcessors();
|
final var nThreads = Runtime.getRuntime().availableProcessors();
|
||||||
final var blockChain = new BlockChain();
|
final var blockChain = BlockChain.getInstance();
|
||||||
blockChain.load();
|
|
||||||
|
|
||||||
// Cryptographic keys management
|
// Cryptographic keys management
|
||||||
final File publicKey = new File(Security.PUBLIC_KEY);
|
final File publicKey = new File(Security.PUBLIC_KEY);
|
||||||
@@ -24,15 +26,13 @@ public final class Main {
|
|||||||
|
|
||||||
final var chatExecutor = Executors.newScheduledThreadPool(nThreads);
|
final var chatExecutor = Executors.newScheduledThreadPool(nThreads);
|
||||||
|
|
||||||
// Mocks 3 chat clients who send messages to the blockchain
|
// Mocks a chat client who send messages to the blockchain
|
||||||
IntStream.range(0, 3)
|
chatExecutor.scheduleAtFixedRate(new ChatClient(blockChain), 0, 200, TimeUnit.MILLISECONDS);
|
||||||
.mapToObj(chatClientId -> new ChatClient(chatClientId, blockChain))
|
|
||||||
.forEach(e -> chatExecutor.scheduleAtFixedRate(e, 0, 100, TimeUnit.MILLISECONDS));
|
|
||||||
|
|
||||||
final var minerExecutor = Executors.newFixedThreadPool(nThreads);
|
final var minerExecutor = Executors.newFixedThreadPool(nThreads);
|
||||||
|
|
||||||
// Creation of 10 miners
|
// Creation of 5 miners
|
||||||
IntStream.range(0, 10)
|
IntStream.range(0, 5)
|
||||||
.mapToObj(minerId -> new Miner(minerId, blockChain))
|
.mapToObj(minerId -> new Miner(minerId, blockChain))
|
||||||
.forEach(minerExecutor::submit);
|
.forEach(minerExecutor::submit);
|
||||||
|
|
||||||
@@ -48,8 +48,8 @@ public final class Main {
|
|||||||
chatExecutor.shutdownNow();
|
chatExecutor.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
blockChain.save();
|
FileManagement.saveBlockChain(blockChain);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import java.util.stream.Collectors;
|
|||||||
public class Block implements Serializable {
|
public class Block implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final String previousBlockHash;
|
private final String previousBlockHash;
|
||||||
private final String blockHash;
|
private final String blockHash;
|
||||||
private final int minerId;
|
private final int minerId;
|
||||||
@@ -24,7 +25,7 @@ public class Block implements Serializable {
|
|||||||
final int id,
|
final int id,
|
||||||
final int magicNumber,
|
final int magicNumber,
|
||||||
final float generationSecs,
|
final float generationSecs,
|
||||||
final List<Message> chatMessages
|
final List<Message> blockMessages
|
||||||
) {
|
) {
|
||||||
this.previousBlockHash = previousBlockHash;
|
this.previousBlockHash = previousBlockHash;
|
||||||
this.blockHash = blockHash;
|
this.blockHash = blockHash;
|
||||||
@@ -32,7 +33,7 @@ public class Block implements Serializable {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
this.magicNumber = magicNumber;
|
this.magicNumber = magicNumber;
|
||||||
this.generationSecs = generationSecs;
|
this.generationSecs = generationSecs;
|
||||||
this.chatMessages = chatMessages;
|
this.chatMessages = blockMessages;
|
||||||
this.timeStamp = new Date().getTime();
|
this.timeStamp = new Date().getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,8 +59,8 @@ public class Block implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String messagesToString() {
|
public String messagesToString() {
|
||||||
return chatMessages.stream()
|
return chatMessages.isEmpty() ? "Empty block\n" : chatMessages.stream()
|
||||||
.map(m -> m.getText().concat("\n"))
|
.map(m -> String.valueOf(m.getId()).concat(" - ").concat(m.getText()).concat("\n"))
|
||||||
.collect(Collectors.joining());
|
.collect(Collectors.joining());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
package model;
|
package model;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import util.FileManagement;
|
import util.FileManagement;
|
||||||
import util.HashFunction;
|
import util.HashFunction;
|
||||||
import util.Security;
|
import util.Security;
|
||||||
@@ -9,20 +16,42 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import static java.lang.String.valueOf;
|
import static java.lang.String.valueOf;
|
||||||
|
|
||||||
public class BlockChain {
|
public class BlockChain implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final float LOWER_LIMIT_SECS = 0.1F;
|
||||||
|
private static final float UPPER_LIMIT_SECS = 0.5F;
|
||||||
|
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
private final List<Block> blockList = new LinkedList<>();
|
private final List<Block> blockList = new LinkedList<>();
|
||||||
private final List<Message> chatMessages = new ArrayList<>();
|
private final List<Message> incomingChatMessages = new ArrayList<>();
|
||||||
|
private final AtomicInteger lastMessageId = new AtomicInteger(1);
|
||||||
|
|
||||||
private int hashZeroes;
|
private int hashZeroes;
|
||||||
private int magicNumber;
|
private int magicNumber;
|
||||||
private float generationSecs = 0;
|
private float generationSecs = 0;
|
||||||
|
|
||||||
|
public static BlockChain getInstance() {
|
||||||
|
try {
|
||||||
|
final BlockChain blockChain = (BlockChain) FileManagement.loadBlockChain();
|
||||||
|
if (!blockChain.validateBlockchain()) {
|
||||||
|
System.out.println("Blockchain not valid! Creating new one");
|
||||||
|
return new BlockChain();
|
||||||
|
} else {
|
||||||
|
return blockChain;
|
||||||
|
}
|
||||||
|
} catch (final ClassNotFoundException | IOException e) {
|
||||||
|
return new BlockChain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void addBlock(final int minerId) {
|
public synchronized void addBlock(final int minerId) {
|
||||||
final int nextId = blockList.size();
|
final int nextId = blockList.size();
|
||||||
final String previousBlockHash = (nextId > 0) ? blockList.get(nextId - 1).getBlockHash() : "0";
|
final String previousBlockHash = (nextId > 0) ? blockList.get(nextId - 1).getBlockHash() : "0";
|
||||||
final String blockHash = calculateBlockHash(minerId);
|
final String blockHash = calculateBlockHash(minerId);
|
||||||
|
|
||||||
|
incomingChatMessages.sort(Comparator.comparingInt(Message::getId));
|
||||||
|
|
||||||
final var block = new Block(
|
final var block = new Block(
|
||||||
previousBlockHash,
|
previousBlockHash,
|
||||||
blockHash,
|
blockHash,
|
||||||
@@ -30,16 +59,16 @@ public class BlockChain {
|
|||||||
nextId,
|
nextId,
|
||||||
magicNumber,
|
magicNumber,
|
||||||
generationSecs,
|
generationSecs,
|
||||||
chatMessages
|
incomingChatMessages
|
||||||
);
|
);
|
||||||
blockList.add(block);
|
blockList.add(block);
|
||||||
System.out.println(block);
|
System.out.println(block);
|
||||||
this.chatMessages.clear();
|
incomingChatMessages.clear();
|
||||||
|
|
||||||
if (generationSecs < 1) {
|
if (generationSecs < LOWER_LIMIT_SECS) {
|
||||||
hashZeroes += 1;
|
hashZeroes += 1;
|
||||||
System.out.println("N was increased to " + hashZeroes +"\n");
|
System.out.println("N was increased to " + hashZeroes +"\n");
|
||||||
} else if (generationSecs > 10) {
|
} else if (generationSecs > UPPER_LIMIT_SECS) {
|
||||||
hashZeroes -= 1;
|
hashZeroes -= 1;
|
||||||
System.out.println("N was decreased by 1\n");
|
System.out.println("N was decreased by 1\n");
|
||||||
} else {
|
} else {
|
||||||
@@ -48,42 +77,52 @@ public class BlockChain {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acceptMessage(Message message) {
|
public int getNextMessageId() {
|
||||||
if (!blockList.isEmpty() || message.getId() > getLastMessageId()) {
|
return lastMessageId.getAndIncrement();
|
||||||
chatMessages.add(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLastMessageId() {
|
public void acceptText(final int nextMessageId, final String name, final String text)
|
||||||
return blockList.stream()
|
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, IOException, InvalidKeySpecException {
|
||||||
.map(Block::getMessages)
|
incomingChatMessages.add(new Message(
|
||||||
.flatMap(Collection::stream)
|
nextMessageId,
|
||||||
.mapToInt(Message::getId)
|
name,
|
||||||
.max().orElse(0);
|
text,
|
||||||
|
Security.getPrivate(),
|
||||||
|
Security.getPublic()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void load() {
|
public boolean validateBlockchain() {
|
||||||
FileManagement.loadBlockchain(blockList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() {
|
|
||||||
FileManagement.saveBlockchain(blockList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
if (!validateBlockchain()) {
|
|
||||||
System.out.println("Blockchain invalid!");
|
|
||||||
}
|
|
||||||
return blockList.stream().map(Block::toString).collect(Collectors.joining("\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean validateBlockchain() {
|
|
||||||
if (blockList.isEmpty()) {
|
if (blockList.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO implement message validation
|
// Check message security
|
||||||
|
if (!blockList.stream()
|
||||||
|
.map(Block::getMessages)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.allMatch(Security::messageIsValid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check messages Id ordering
|
||||||
|
if (!blockList.stream()
|
||||||
|
.map(Block::getMessages)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.map(Message::getId)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
.equals(blockList.stream()
|
||||||
|
.map(Block::getMessages)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.map(Message::getId)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check block hashes ordering
|
||||||
final List<String> previousHashes = blockList.stream()
|
final List<String> previousHashes = blockList.stream()
|
||||||
.map(Block::getPreviousBlockHash)
|
.map(Block::getPreviousBlockHash)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -100,11 +139,6 @@ public class BlockChain {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean messageIsValid(Message msg) {
|
|
||||||
//TODO correct signature issues
|
|
||||||
return Security.verifySignature(msg.getId() + msg.getText(), msg.getSignature(), msg.getPublicKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String calculateBlockHash(final int minerId) {
|
private String calculateBlockHash(final int minerId) {
|
||||||
final long start = System.currentTimeMillis();
|
final long start = System.currentTimeMillis();
|
||||||
var hash = "";
|
var hash = "";
|
||||||
@@ -113,7 +147,7 @@ public class BlockChain {
|
|||||||
+ valueOf(new Date().getTime())
|
+ valueOf(new Date().getTime())
|
||||||
+ calculateMagicNumber()
|
+ calculateMagicNumber()
|
||||||
);
|
);
|
||||||
} while (!hash.matches("(?s)0{" + hashZeroes + "}([^0].*)?"));
|
} while (!hash.startsWith("0".repeat(hashZeroes)));
|
||||||
final long end = System.currentTimeMillis();
|
final long end = System.currentTimeMillis();
|
||||||
generationSecs = (end - start) / 1000F;
|
generationSecs = (end - start) / 1000F;
|
||||||
return hash;
|
return hash;
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
package model;
|
|
||||||
|
|
||||||
import util.Security;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class ChatClient implements Runnable {
|
|
||||||
|
|
||||||
private final int clientId;
|
|
||||||
private final BlockChain blockChain;
|
|
||||||
private static final Random random = new Random();
|
|
||||||
|
|
||||||
public ChatClient(final int chatClientId, final BlockChain blockChain) {
|
|
||||||
this.clientId = chatClientId;
|
|
||||||
this.blockChain = blockChain;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
blockChain.acceptMessage(createMessage());
|
|
||||||
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IOException | InvalidKeySpecException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Message createMessage()
|
|
||||||
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, IOException, InvalidKeySpecException {
|
|
||||||
return new Message(
|
|
||||||
blockChain.getLastMessageId() + 1,
|
|
||||||
clientId,
|
|
||||||
generateRandomAlphabeticString(),
|
|
||||||
Security.getPrivate(),
|
|
||||||
Security.getPublic()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String generateRandomAlphabeticString() {
|
|
||||||
final int leftLimit = 97; // letter 'a'
|
|
||||||
final int rightLimit = 122; // letter 'z'
|
|
||||||
final int targetStringLength = 10;
|
|
||||||
|
|
||||||
return random.ints(leftLimit, rightLimit + 1)
|
|
||||||
.limit(targetStringLength)
|
|
||||||
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -8,15 +8,16 @@ import java.security.*;
|
|||||||
public class Message implements Serializable {
|
public class Message implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final int id;
|
private final int id;
|
||||||
private final String text;
|
private final String text;
|
||||||
private final byte[] signature;
|
private final byte[] signature;
|
||||||
private final PublicKey publicKey;
|
private final PublicKey publicKey;
|
||||||
|
|
||||||
public Message(final int id, final int clientId, final String text, final PrivateKey privateKey, final PublicKey publicKey)
|
public Message(final int id, final String name, final String text, final PrivateKey privateKey, final PublicKey publicKey)
|
||||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.text = "Client " + clientId + " says: " + text;
|
this.text = "Chatter " + name + " says: " + text;
|
||||||
this.signature = Security.sign(text, privateKey);
|
this.signature = Security.sign(text, privateKey);
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/util/ChatClient.java
Normal file
53
src/util/ChatClient.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package util;
|
||||||
|
|
||||||
|
import model.BlockChain;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class ChatClient implements Runnable {
|
||||||
|
|
||||||
|
private static final Random random = new Random();
|
||||||
|
|
||||||
|
private final BlockChain blockChain;
|
||||||
|
|
||||||
|
public ChatClient(final BlockChain blockChain) {
|
||||||
|
this.blockChain = blockChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
blockChain.acceptText(blockChain.getNextMessageId(), generateRandomName(), generateRandomText());
|
||||||
|
} catch (final NoSuchAlgorithmException | SignatureException | InvalidKeyException | IOException | InvalidKeySpecException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateRandomName() {
|
||||||
|
final int leftLimit = 97; // letter 'a'
|
||||||
|
final int rightLimit = 122; // letter 'z'
|
||||||
|
final int targetStringLength = 5;
|
||||||
|
|
||||||
|
return random.ints(leftLimit, rightLimit + 1)
|
||||||
|
.limit(targetStringLength)
|
||||||
|
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateRandomText() {
|
||||||
|
final int leftLimit = 97; // letter 'a'
|
||||||
|
final int rightLimit = 122; // letter 'z'
|
||||||
|
final int targetStringLength = 20;
|
||||||
|
|
||||||
|
return random.ints(leftLimit, rightLimit + 1)
|
||||||
|
.limit(targetStringLength)
|
||||||
|
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,43 +1,37 @@
|
|||||||
package util;
|
package util;
|
||||||
|
|
||||||
import model.Block;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public final class FileManagement {
|
public final class FileManagement {
|
||||||
|
|
||||||
private static final String BLOCKCHAIN = "blockChain.txt";
|
private static final String BLOCKCHAIN = "blockChain.txt";
|
||||||
|
|
||||||
public static void saveBlockchain(final List<Block> blockList) {
|
private FileManagement() {
|
||||||
final var blockChainFile = new File(BLOCKCHAIN);
|
throw new IllegalStateException("FileManagement class");
|
||||||
try (final var fileOut = new FileOutputStream(blockChainFile);
|
|
||||||
final var objectOut = new ObjectOutputStream(fileOut)) {
|
|
||||||
for (final var block : blockList) {
|
|
||||||
objectOut.writeObject(block);
|
|
||||||
}
|
|
||||||
} catch (final IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadBlockchain(final List<Block> blockList) {
|
public static void saveBlockChain(final Object obj) throws IOException {
|
||||||
final var blockChainFile = new File(BLOCKCHAIN);
|
final FileOutputStream fos = new FileOutputStream(BLOCKCHAIN);
|
||||||
if (blockChainFile.exists()) {
|
final BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||||
try (final var fileIn = new FileInputStream(blockChainFile);
|
final ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||||
final var objectIn = new ObjectInputStream(fileIn)) {
|
oos.writeObject(obj);
|
||||||
while (fileIn.available() > 0) {
|
oos.close();
|
||||||
final var object = objectIn.readObject();
|
|
||||||
blockList.add((Block) object);
|
|
||||||
}
|
|
||||||
} catch (final IOException | ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Object loadBlockChain() throws IOException, ClassNotFoundException {
|
||||||
|
final FileInputStream fis = new FileInputStream(BLOCKCHAIN);
|
||||||
|
final BufferedInputStream bis = new BufferedInputStream(fis);
|
||||||
|
final ObjectInputStream ois = new ObjectInputStream(bis);
|
||||||
|
final Object obj = ois.readObject();
|
||||||
|
ois.close();
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveKey(final String path, final byte[] key) throws IOException {
|
public static void saveKey(final String path, final byte[] key) throws IOException {
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
public final class HashFunction {
|
public final class HashFunction {
|
||||||
|
|
||||||
|
private HashFunction() {
|
||||||
|
throw new IllegalStateException("HashFunction class");
|
||||||
|
}
|
||||||
|
|
||||||
/* Applies Sha256 to a string and returns a hash. */
|
/* Applies Sha256 to a string and returns a hash. */
|
||||||
public static String applySha256(final String input) throws RuntimeException {
|
public static String applySha256(final String input) throws RuntimeException {
|
||||||
try {
|
try {
|
||||||
@@ -22,4 +27,5 @@ public final class HashFunction {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,17 @@ import java.security.*;
|
|||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import model.Message;
|
||||||
|
|
||||||
public final class Security {
|
public final class Security {
|
||||||
|
|
||||||
public static final String PRIVATE_KEY = "KeyPair/privateKey";
|
public static final String PRIVATE_KEY = "KeyPair/privateKey";
|
||||||
public static final String PUBLIC_KEY = "KeyPair/publicKey";
|
public static final String PUBLIC_KEY = "KeyPair/publicKey";
|
||||||
|
|
||||||
|
private Security() {
|
||||||
|
throw new IllegalStateException("Security class");
|
||||||
|
}
|
||||||
|
|
||||||
public static PublicKey getPublic()
|
public static PublicKey getPublic()
|
||||||
throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
|
throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
|
||||||
final byte[] keyBytes = Files.readAllBytes(new File(PUBLIC_KEY).toPath());
|
final byte[] keyBytes = Files.readAllBytes(new File(PUBLIC_KEY).toPath());
|
||||||
@@ -37,6 +42,10 @@ public final class Security {
|
|||||||
return rsa.sign();
|
return rsa.sign();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean messageIsValid(final Message msg) {
|
||||||
|
return Security.verifySignature(msg.getId() + msg.getText(), msg.getSignature(), msg.getPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean verifySignature(final String data, final byte[] signature, final PublicKey publicKey) {
|
public static boolean verifySignature(final String data, final byte[] signature, final PublicKey publicKey) {
|
||||||
try {
|
try {
|
||||||
final Signature verifier = Signature.getInstance("SHA1withRSA");
|
final Signature verifier = Signature.getInstance("SHA1withRSA");
|
||||||
|
|||||||
Reference in New Issue
Block a user