Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/run_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ jobs:
uses: testhub-io/testhub-io.action@v0.62
with:
test_result_pattern: "target/surefire-reports/*.xml"
test_coverage_pattern: "target/site/jacoco/jacoco.xml"
test_coverage_pattern: "target/site/jacoco/jacoco.xml"
38 changes: 36 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,45 @@
### Example:
```
-e -k 1 -f "/path/to/file.txt" - Encrypt file with key 1
-d -k 5 -f "/path/to/file [ENCRYPTED].txt" - Decrypt file with key 5
-bf -f "/path/to/file [ENCRYPTED].txt" - Brute force decrypt file
-d -k 5 -f "/path/to/file[ENCRYPTED].txt" - Decrypt file with key 5
-bf -f "/path/to/file[ENCRYPTED].txt" - Brute force decrypt file
```

### Argument could be in any order
```
-e -f "/path/to/file.txt" -k 1
```

## Summary
### What was done ?
```
All basic requirements were implemented:
- Encryption
- Decryption
- Brute force algorithm
```

Additional features that were implemented:
```
- Ukrainian language added
- Auto detecting language method
```
### What hasn't been done from the basic requirements?
```
All basic features have been implemented
```
### Features of the project
```
The features of the project are the use of large English and Ukrainian dictionaries
to form a dataset for brute force encoding of the file. This solution provides better acurracy
for the brute force operation.
Also language auto-detection was implemented.This makes it possible to use many languages in the
program and to add new languages without changing the client code.
```

### What the mentor should pay attention to during the inspection?
```
- BruteForce.java
- Alphabet.java
- CryptoOperationHandler.java
```
31 changes: 12 additions & 19 deletions src/main/java/ua/com/javarush/gnew/Main.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,25 @@
package ua.com.javarush.gnew;

import ua.com.javarush.gnew.crypto.Cypher;
import ua.com.javarush.gnew.file.FileManager;
import ua.com.javarush.gnew.runner.ArgumentsParser;
import ua.com.javarush.gnew.runner.Command;
import ua.com.javarush.gnew.runner.RunOptions;
import ua.com.javarush.gnew.processing.ArgumentParser;
import ua.com.javarush.gnew.processing.CryptoOperationHandler;

import java.io.IOException;
import java.nio.file.Path;

public class Main {
public static void main(String[] args) {
Cypher cypher = new Cypher();
FileManager fileManager = new FileManager();
ArgumentsParser argumentsParser = new ArgumentsParser();
RunOptions runOptions = argumentsParser.parse(args);
if (args.length == 0) {
System.out.println("No arguments provided. Use -e for encrypt, -d for decrypt, -k for key, -bf for brute-force, -p for file path.");
return;
}

try {
if (runOptions.getCommand() == Command.ENCRYPT) {
String content = fileManager.read(runOptions.getFilePath());
String encryptedContent = cypher.encrypt(content, runOptions.getKey());
String fileName = runOptions.getFilePath().getFileName().toString();
String newFileName = fileName.substring(0, fileName.length() - 4) + " [ENCRYPTED].txt";

Path newFilePath = runOptions.getFilePath().resolveSibling(newFileName);
fileManager.write(newFilePath, encryptedContent);
}
ArgumentParser parser = new ArgumentParser(args);
CryptoOperationHandler handler = new CryptoOperationHandler();
handler.handleOperation(parser.getMode(), parser.getFilePath(), parser.getKey(), parser.isBruteForce());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
}

33 changes: 0 additions & 33 deletions src/main/java/ua/com/javarush/gnew/crypto/Cypher.java

This file was deleted.

87 changes: 87 additions & 0 deletions src/main/java/ua/com/javarush/gnew/cypher/BruteForce.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package ua.com.javarush.gnew.cypher;

import ua.com.javarush.gnew.file.FileOperations;
import ua.com.javarush.gnew.language.Alphabet;
import ua.com.javarush.gnew.language.Language;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

public class BruteForce {
private final Set<String> dataSet;
private final Language language;
private final FileOperations fileOperations;
private final CryptoProcessor cryptoProcessor;

public BruteForce(Language language) {
this.fileOperations = new FileOperations();
this.cryptoProcessor = new CryptoProcessor();
this.language = language;
String text;

// Loading training data depending on selected language
switch (language) {
case UA -> text = readFromResource("bruteForceUA.txt");
case ENG -> text = readFromResource("bruteForceENG.txt");
default -> throw new IllegalStateException("Unexpected value: " + language);
}
// Form the vocabulary
this.dataSet = new HashSet<>(Arrays.asList(text.split("[,\\.\\s]+")));
}

// Method to read from resources
private String readFromResource(String resourcePath) {
StringBuilder result = new StringBuilder();
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourcePath);
BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(inputStream)))) {
String line;
while ((line = reader.readLine()) != null) {
result.append(line).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return result.toString();
}


public int getKey(String path){
if(path == null || path.isEmpty()){
throw new IllegalArgumentException("Path cannot be empty");
}
HashMap<Integer,Integer> accuracyMap = new HashMap<>();
String encryptText = fileOperations.readFromFile(path);
Alphabet alphabet = new Alphabet(language);
cryptoProcessor.setAlphabet(alphabet.getAlphabet());
String decryptText;
for (int i = 1; i <= 26 ; i++) {
decryptText = cryptoProcessor.decrypt(encryptText,i);
String[] decryptArray = decryptText.split("[,\\.\\s]+");
int matches = 0;
for (String dec: decryptArray){
if(dataSet.contains(dec)){
matches++;
accuracyMap.put(i,matches);

}
}
}
return getMax(accuracyMap);
}

private int getMax(HashMap<Integer,Integer> accuracyMap){
int maxValue = Integer.MIN_VALUE;
int key = 1;
for (int score: accuracyMap.keySet()) {
if(accuracyMap.get(score)>maxValue){
maxValue = accuracyMap.get(score);
key = score;
}

} return key;
}

}
69 changes: 69 additions & 0 deletions src/main/java/ua/com/javarush/gnew/cypher/CryptoProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package ua.com.javarush.gnew.cypher;

import java.util.ArrayList;
import java.util.Collections;

public class CryptoProcessor {
private ArrayList<Character> alphabet;

public void setAlphabet(ArrayList<Character> alphabet) {
this.alphabet = alphabet;
}

public String encrypt(String text, int key){

ArrayList<Character> alphabetToRotate = new ArrayList<>(alphabet);
Collections.rotate(alphabetToRotate,-key);
if(text.isEmpty()){
throw new IllegalArgumentException("Text to encrypt must contains chars");
}
StringBuilder result = new StringBuilder();

for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if (Character.isLetter(ch)) {
boolean isUpperCase = Character.isUpperCase(ch);
int originalPosition = getPositionInAlphabet(ch);
char newChar = alphabetToRotate.get(originalPosition);
result.append(isUpperCase ? newChar : Character.toLowerCase(newChar));
} else {
result.append(ch);
}
}

return result.toString();

}

public String decrypt(String text, int key){
ArrayList<Character> alphabetToRotate = new ArrayList<>(alphabet);
Collections.rotate(alphabetToRotate,-key);
if(text.isEmpty()){
throw new IllegalArgumentException("Text to encrypt must contains chars");
}
StringBuilder result = new StringBuilder();

for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if (Character.isLetter(ch)) {
boolean isUpperCase = Character.isUpperCase(ch);
int originalPosition = alphabetToRotate.indexOf(Character.toUpperCase(ch));
char newChar = alphabet.get(originalPosition);
result.append(isUpperCase ? newChar : Character.toLowerCase(newChar));
} else {
result.append(ch);
}
}

return result.toString();

}



private int getPositionInAlphabet(char ch) {
return alphabet.indexOf(Character.toUpperCase(ch));
}


}
7 changes: 7 additions & 0 deletions src/main/java/ua/com/javarush/gnew/cypher/Operation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ua.com.javarush.gnew.cypher;

public enum Operation {
ENCRYPT,
DECRYPT,
BRUTE_FORCE
}
15 changes: 0 additions & 15 deletions src/main/java/ua/com/javarush/gnew/file/FileManager.java

This file was deleted.

39 changes: 39 additions & 0 deletions src/main/java/ua/com/javarush/gnew/file/FileOperations.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ua.com.javarush.gnew.file;

import ua.com.javarush.gnew.cypher.Operation;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class FileOperations {

public String readFromFile(String path) {
try {
return new String(Files.readAllBytes(Paths.get(path)));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}


public void writeToFile(String path, String content, Operation type) {
try {
switch (type){
case ENCRYPT -> Files.write(Paths.get(modifyPath(path, "[ENCRYPTED]")), content.getBytes());
case DECRYPT -> Files.write(Paths.get(modifyPath(path, "[DECRYPTED]")), content.getBytes());
case BRUTE_FORCE -> Files.write(Paths.get(modifyPath(path, "[BRUTE_FORCE]")), content.getBytes());
}

}catch (IOException e){
e.printStackTrace();
}
}

private String modifyPath(String path, String modificationType){
int index = path.lastIndexOf(".");
String modifiedPath = path.substring(0,index) + modificationType + path.substring(index);
return modifiedPath;
}
}
Loading