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
3 changes: 3 additions & 0 deletions META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: ua.com.javarush.gnew.Main

Binary file added out/artifacts/GNEW_M1_FP_jar/GNEW-M1-FP.jar
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.jar file shouldn't be committed. It should be added as a release executable.

Binary file not shown.
29 changes: 27 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,32 @@
-bf -f "/path/to/file [ENCRYPTED].txt" - Brute force decrypt file
```

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

## About the project

### Implemented tasks
All mandatory tasks are completed.
- Program compiled in jar format and released on GitHub.
- Program can be run from the console by passing arguments.
- Program implements three options: [ENCRYPT, DECRYPT, BRUTE_FORCE].
- Program implements only one option at a time.
- If you use Encrypt or Decrypt command, key value is mandatory.
- Program writes the result into a new file, which name contains
a label according to command used: [ENCRYPTED], [DECRYPTED],
[BRUTE_FORCE]. In case of using Bruteforce, the calculated key value
is appended to the file name as well.
- Program can be used English language only.
- Only letters of latin alphabet are encrypted.
- After decryption, the text has exactly the same formatting as the original file
- (spaces, indents, line separators, symbols, upper and lower case letters).

### Not implemented tasks
- Program does not work for Ukrainian or any other languages.
- Program does not encode any special or blank symbols (latin letters only).
- Program does not work with CLI.
- Program does not have UI.

44 changes: 34 additions & 10 deletions src/main/java/ua/com/javarush/gnew/Main.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package ua.com.javarush.gnew;

import ua.com.javarush.gnew.crypto.Cypher;
import ua.com.javarush.gnew.file.FileManager;
import ua.com.javarush.gnew.cypher.Cypher;
import ua.com.javarush.gnew.fileManager.FileManager;
import ua.com.javarush.gnew.runner.ArgumentsParser;
import ua.com.javarush.gnew.runner.Command;
import ua.com.javarush.gnew.runner.RunOptions;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

public class Main {
public static void main(String[] args) {
Expand All @@ -15,16 +16,39 @@ public static void main(String[] args) {
ArgumentsParser argumentsParser = new ArgumentsParser();
RunOptions runOptions = argumentsParser.parse(args);

List<String> linesRead;
List<String> linesWritten = new ArrayList<>();
Path resultFile = null;

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";
linesRead = fileManager.read(runOptions.getFilePath());
switch (runOptions.getCommand()) {
case ENCRYPT:
for (String lineRead : linesRead) {
linesWritten.add(cypher.encrypt(lineRead, runOptions.getKey()));
}
resultFile = fileManager.getNewPath(runOptions.getFilePath(), runOptions.getCommand());
break;
case DECRYPT:
for (String lineRead : linesRead) {
linesWritten.add(cypher.decrypt(lineRead, runOptions.getKey()));
}
resultFile = fileManager.getNewPath(runOptions.getFilePath(), runOptions.getCommand());
break;
case BRUTEFORCE:
int key = cypher.bruteForceKey(linesRead);
for (String lineRead : linesRead) {
linesWritten.add(cypher.decrypt(lineRead, key));
}
resultFile = fileManager.getNewPath(runOptions.getFilePath(), key);
break;
}
Comment on lines +24 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to keep the logic out of the main method.


Path newFilePath = runOptions.getFilePath().resolveSibling(newFileName);
fileManager.write(newFilePath, encryptedContent);
if (Files.notExists(resultFile)) {
Files.createFile(resultFile);
}
fileManager.write(resultFile, linesWritten);

} catch (Exception e) {
System.out.println(e.getMessage());
}
Expand Down
33 changes: 0 additions & 33 deletions src/main/java/ua/com/javarush/gnew/crypto/Cypher.java

This file was deleted.

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

import ua.com.javarush.gnew.language.Alphabet;

import java.util.ArrayList;
import java.util.List;

public class Cypher {

public String encrypt(String text, int key) {

ArrayList<Character> rotatedUppercase = Alphabet.rotateAlphabet(Alphabet.ENGLISH_UPPERCASE, key);
ArrayList<Character> rotatedLowercase = Alphabet.rotateAlphabet(Alphabet.ENGLISH_LOWERCASE, key);
StringBuilder result = new StringBuilder();

for (int i = 0; i < text.length(); i++) {
char oneChar = text.charAt(i); // беремо символ з тексту
int index = Alphabet.ENGLISH_LOWERCASE.indexOf(oneChar); // шукаємо його в алфавіті lowercase
if (index != -1) { // якщо знайшли
result.append(rotatedLowercase.get(index)); // шифруємо і додаємо до результату
} else { // якщо немає
index = Alphabet.ENGLISH_UPPERCASE.indexOf(oneChar); // шукаємо його в алфавіті uppercase
if (index != -1) { // якщо знайшли
result.append(rotatedUppercase.get(index)); // шифруємо і додаємо до результату
} else { // якщо не знайшли в жодному з двох алфавітів, це спеціальний символ
result.append(text.charAt(i)); // НЕ шифруємо і просто додаємо
}
}
}
Comment on lines +16 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be simplified.

return result.toString();
}

public String decrypt(String text, int key) {
// decrypt робить те саме, що й encrypt, тільки з протилежним значенням ключа
return encrypt(text, -key);
}

public int bruteForceKey(List<String> text) {
String encrypted = "";
String decrypted = "";

for (String line : text) {
encrypted = line;
for (int i = 0; i < 26; i++) {
decrypted = decrypt(line, i);
if (checkForCommonWords(decrypted)) {
break;
}
}
break;
}
Comment on lines +42 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that you decrypt it using lines. In some cases, it's very effective, but there are also some cases when it might not work as expected. Maybe it's good idea to take more than 1 line if it contains not enough letters a.e. always use the text > 256 symbols.


return calculateKey(encrypted, decrypted);
}

private int calculateKey(String encrypted, String decrypted) {
int firstLetterIndex = 0;
for (int i = 0; i < decrypted.length(); i++) {
if (Character.isLetter(decrypted.charAt(i))) {
firstLetterIndex = i;
break;
}
}

int indexEncrypted = Alphabet.ENGLISH_LOWERCASE.indexOf(Character.toLowerCase(encrypted.charAt(firstLetterIndex)));
int indexDecrypted = Alphabet.ENGLISH_LOWERCASE.indexOf(Character.toLowerCase(decrypted.charAt(firstLetterIndex)));

if (indexEncrypted < indexDecrypted) {
indexEncrypted = indexEncrypted + 26;
}
return indexEncrypted - indexDecrypted;
}

private boolean checkForCommonWords(String text) {
ArrayList<String> words = getWords(text);
boolean containsCommonWords = false;

for (String word : words) {
if (Alphabet.ENGLISH_COMMON_WORDS.contains(word.toLowerCase())) {
containsCommonWords = true;
break;
}
}

return containsCommonWords;
}

private ArrayList<String> getWords(String text) {
String[] words = text.replaceAll("[^a-zA-Z ]", "").split("\\s+");

ArrayList<String> wordList = new ArrayList<>();
for (String word : words) {
if (!word.isEmpty()) {
wordList.add(word);
}
}

return wordList;
}
}
15 changes: 0 additions & 15 deletions src/main/java/ua/com/javarush/gnew/file/FileManager.java

This file was deleted.

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

import ua.com.javarush.gnew.runner.Command;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class FileManager {

public List<String> read(Path path) throws IOException {
return Files.readAllLines(path);
}

public void write(Path path, List<String> lines) throws IOException {
String content = String.join("\n", lines);
Files.writeString(path, content);
}

public Path getNewPath(Path path, Command command) {
String markerOfAction = switch (command) {
case DECRYPT:
yield " [DECRYPTED].txt";
case ENCRYPT:
yield " [ENCRYPTED].txt";
default:
yield ".txt";
};

String originalPath = path.toString();
String newPath = originalPath.substring(0, originalPath.length() - 4) + markerOfAction;
return Path.of(newPath);
}

public Path getNewPath(Path path, int key) {
String originalPath = path.toString();
String newPath = originalPath.substring(0, originalPath.length() - 4) + " [BRUTE_FORCE] " + key + ".txt";
return Path.of(newPath);
}
}
31 changes: 31 additions & 0 deletions src/main/java/ua/com/javarush/gnew/language/Alphabet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ua.com.javarush.gnew.language;

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

public abstract class Alphabet {

public static final ArrayList<Character> ENGLISH_UPPERCASE = new ArrayList<>(Arrays.asList(
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'));
public static final ArrayList<Character> ENGLISH_LOWERCASE = new ArrayList<>(Arrays.asList(
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'));
Comment on lines +9 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could get rid of two lists and use only one by processing uppercase letters dynamically.

public static final ArrayList<String> ENGLISH_COMMON_WORDS = new ArrayList<>(Arrays.asList(
"the", "be", "to", "of", "and", "in", "that", "have", "for", "not",
"with", "he", "as", "you", "do", "this", "but", "his", "by", "from",
"they", "we", "say", "her", "she", "or", "will", "my", "one", "all",
"would", "there", "their", "what", "so", "up", "out", "if", "about",
"who", "get", "which", "go", "me", "it", "can", "has", "had", "are", "hello"
));

public static ArrayList<Character> rotateAlphabet(ArrayList<Character> originalAlphabet, int key) {

ArrayList<Character> rotatedAlphabet = new ArrayList<>(originalAlphabet);

Collections.rotate(rotatedAlphabet, -key);

return rotatedAlphabet;
}
}
12 changes: 0 additions & 12 deletions src/main/java/ua/com/javarush/gnew/language/Language.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public RunOptions parse(String[] args) {

case "-bf":
command = Command.BRUTEFORCE;
key = 0;
break;
case "-k":
if (i + 1 < args.length) {
Expand Down Expand Up @@ -58,5 +59,4 @@ public RunOptions parse(String[] args) {

return new RunOptions(command, key, filePath);
}

}
}
2 changes: 1 addition & 1 deletion src/main/java/ua/com/javarush/gnew/runner/RunOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ public String toString() {
", filePath=" + filePath +
'}';
}
}
}