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
25 changes: 25 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.7.6</version>
</dependency>
</dependencies>

<build>
Expand All @@ -35,6 +40,26 @@
<artifactId>maven-surefire-report-plugin</artifactId>
<version>3.4.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- annotationProcessorPaths requires maven-compiler-plugin version 3.5 or higher -->
<!-- <version>${maven-compiler-plugin-version}</version>-->
<version>3.13.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>info.picocli</groupId>
<artifactId>picocli-codegen</artifactId>
<version>4.7.6</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
</compilerArgs>
</configuration>
</plugin>

</plugins>
</build>
</project>
15 changes: 8 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
### Commands:

```
-e Encrypt
-d Decrypt
-bf Brute force
-bf, --brute-force Brute force
-d, DECRYPT Decrypt
-e, ENCRYPT Encrypt
-f, --file=<file> File path
-k, --key=<key> Key
```

### Arguments:
### Usage:
```
-k Key
-f File path
Usage: <main class> [-de] [-bf] [-f=<file>] [-k=<key>]
```

### Example:
Expand All @@ -25,4 +26,4 @@
### Argument could be in any order
```
-e -f "/path/to/file.txt" -k 1
```
```
29 changes: 4 additions & 25 deletions src/main/java/ua/com/javarush/gnew/Main.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,11 @@
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 java.nio.file.Path;
import picocli.CommandLine;
import ua.com.javarush.gnew.runner.SimpleCLI;

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);

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";
int exitCode = new CommandLine(new SimpleCLI()).execute(args);

Path newFilePath = runOptions.getFilePath().resolveSibling(newFileName);
fileManager.write(newFilePath, encryptedContent);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
}
91 changes: 73 additions & 18 deletions src/main/java/ua/com/javarush/gnew/crypto/Cypher.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,88 @@
package ua.com.javarush.gnew.crypto;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import ua.com.javarush.gnew.language.LanguageFactory;

import java.util.HashMap;
import java.util.Map;


public class Cypher {
private final ArrayList<Character> originalAlphabet = 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', '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 String encrypt(String input, int key) {
key = Math.negateExact(key);
public String decrypt(String text, int shift) {
var alphabetLanguage = LanguageFactory.alphabet(text);
shift = alphabetLanguage.absKey(shift);

return encrypt(text, alphabetLanguage.length() - shift); // Для розшифрування використовується зворотний зсув
}

ArrayList<Character> rotatedAlphabet = new ArrayList<>(originalAlphabet);
Collections.rotate(rotatedAlphabet, key);
char[] charArray = input.toCharArray();
public String encrypt(String text, int shift) {
var alphabetLanguage = LanguageFactory.alphabet(text);
shift = alphabetLanguage.absKey(shift);
String alphabet = alphabetLanguage.getAlphabet();

StringBuilder builder = new StringBuilder();
for (char symbol : charArray) {
builder.append(processSymbol(symbol, rotatedAlphabet));
StringBuilder encryptedText = new StringBuilder();
int alphabetSize = alphabet.length();

for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (Character.isUpperCase(c)) {
int index = alphabet.indexOf(c);
if (index != -1) {
char encryptedChar = alphabet.charAt((index + shift) % alphabetSize);
encryptedText.append(encryptedChar);
} else {
encryptedText.append(c);
}
} else if (Character.isLowerCase(c)) {
int index = alphabet.toLowerCase().indexOf(c);
if (index != -1) {
char encryptedChar = alphabet.toLowerCase().charAt((index + shift) % alphabetSize);
encryptedText.append(encryptedChar);
} else {
encryptedText.append(c);
}
} else {
encryptedText.append(c); // Якщо це не буква, просто додаємо символ без змін
}
}
return builder.toString();
return encryptedText.toString();
}

private Character processSymbol(char symbol, ArrayList<Character> rotatedAlphabet) {
if (!originalAlphabet.contains(symbol)) {
return symbol;
public int analyzeFrequency(String text) {
var alphabetLanguage = LanguageFactory.alphabet(text);
String alphabet = alphabetLanguage.getAlphabet();
var letterFrequency = alphabetLanguage.getLetterFrequency();

Map<Character, Integer> frequencyMap = new HashMap<>();
int alphabetSize = alphabet.length();

for (char c : text.toUpperCase().toCharArray()) {
if (alphabet.indexOf(c) != -1) {
frequencyMap.put(c, frequencyMap.getOrDefault(c, 0) + 1);
}
}
int index = originalAlphabet.indexOf(symbol);

return rotatedAlphabet.get(index);
double minChiSquared = Double.MAX_VALUE;
int bestShift = 0;

for (int shift = 0; shift < alphabetSize; shift++) {
double chiSquared = 0.0;

for (int i = 0; i < alphabetSize; i++) {
char decryptedChar = alphabet.charAt((i + shift) % alphabetSize);
int observedCount = frequencyMap.getOrDefault(decryptedChar, 0);
double expectedCount = text.length() * letterFrequency[i] / 100.0;
chiSquared += Math.pow(observedCount - expectedCount, 2) / expectedCount;
}

if (chiSquared < minChiSquared) {
minChiSquared = chiSquared;
bestShift = shift;
}
}

return bestShift;
}

}
11 changes: 11 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,11 @@
package ua.com.javarush.gnew.language;

public interface Alphabet {

public String getLanguage();
public String getAlphabet();
public double[] getLetterFrequency();
public int absKey(int key);
public int length();

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

public class EnglishAlphabet implements Alphabet{
private final String Language = "EN";
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final double[] LETTER_FREQUENCY = {
8.167, 1.492, 2.782, 4.253, 12.702, 2.228, 2.015, 6.094,
6.966, 0.153, 0.772, 4.025, 2.406, 6.749, 7.507, 1.929,
0.095, 5.987, 6.327, 9.056, 2.758, 0.978, 2.360, 0.150,
1.974, 0.074
};

@Override
public String getLanguage() {
return Language;
}

@Override
public String getAlphabet() {
return ALPHABET;
}

@Override
public double[] getLetterFrequency() {
return LETTER_FREQUENCY;
}

@Override
public int absKey(int key) {
if(key<0) { key = ALPHABET.length()-Math.abs(key % ALPHABET.length());}
return key;
}

@Override
public int length() {
return ALPHABET.length();
}
}
12 changes: 0 additions & 12 deletions src/main/java/ua/com/javarush/gnew/language/Language.java

This file was deleted.

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

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LanguageFactory {

public static Alphabet alphabet(String text) {
Pattern pattern = Pattern.compile("[а-яА-ЯґҐєЄіІїЇ]+");
Matcher matcher = pattern.matcher(text);
if (matcher.find()) { // Український текст
return new UkrainianAlphabet();
} else { // Англійський текст
return new EnglishAlphabet();
}
}

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

public class UkrainianAlphabet implements Alphabet{
private final String Language = "UKR";
private final String ALPHABET = "АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ";
private final double[] LETTER_FREQUENCY = {
7.45, 4.92, 4.33, 1.06, 0.01, 2.98, 1.83, 0.77, 4.92, 5.68, 2.28, 5.61, 2.72, 4.03, 7.59,
9.13, 4.54, 0.81, 7.19, 5.45, 1.58, 2.06, 2.08, 0.99, 0.43, 1.47, 1.72, 0.18, 1.24, 0.32,
2.26, 1.27, 0.21
};

@Override
public String getLanguage() {
return Language;
}
@Override
public String getAlphabet() {
return ALPHABET;
}

@Override
public double[] getLetterFrequency() {
return LETTER_FREQUENCY;
}

@Override
public int absKey(int key) {
if(key<0) { key = ALPHABET.length()-Math.abs(key % ALPHABET.length());}
return key;
}
@Override
public int length() {
return ALPHABET.length();
}
}
62 changes: 0 additions & 62 deletions src/main/java/ua/com/javarush/gnew/runner/ArgumentsParser.java

This file was deleted.

Loading