Fixed some issues, and made things readible from File.

This commit is contained in:
2025-03-19 21:57:36 +01:00
parent 32c93fd99f
commit 5f192d1860
8 changed files with 277 additions and 25 deletions

View File

@@ -1,5 +1,6 @@
plugins {
id 'java'
id 'com.gradleup.shadow' version '9.0.0-beta7'
}
group = 'me.zacharias'
@@ -10,8 +11,16 @@ repositories {
}
dependencies {
implementation 'com.google.code.gson:gson:2.12.1'
implementation("org.jetbrains:annotations:23.1.0")
}
test {
useJUnitPlatform()
}
jar{
manifest {
attributes 'Main-class': 'me.zacharias.bank.Main'
}
}

View File

@@ -2,21 +2,25 @@ package me.zacharias.bank;
import me.zacharias.bank.transaction.Transaction;
import me.zacharias.bank.transaction.TransactionType;
import org.intellij.lang.annotations.MagicConstant;
import java.util.ArrayList;
import java.util.UUID;
public abstract class Account {
public class Account {
ArrayList<Transaction> transactions;
String name;
double balance;
UUID id;
@MagicConstant(valuesFromClass = Interests.class)
double interestRate;
public Account(String name, int balance) {
public Account(String name, double balance, @MagicConstant(valuesFromClass = Interests.class) double interestRate) {
this.name = name;
this.balance = balance;
transactions = new ArrayList<>();
id = UUID.randomUUID();
this.interestRate = interestRate;
}
public void WithdrawTransaction(double amount, String description, UUID destination) {
@@ -85,9 +89,13 @@ public abstract class Account {
return transactions;
}
abstract double getInterestRate();
public double getInterest() {
return balance * (1+getInterestRate());
return balance * (1+interestRate);
}
public static class Interests
{
public static final double BANK_ACCOUNT_INTEREST_RATE = 0.001;
public static final double SAVINGS_ACCOUNT_INTEREST_RATE = 0.05;
}
}

View File

@@ -0,0 +1,7 @@
package me.zacharias.bank;
public enum AccountType {
BANK_ACCOUNT,
SAVINGS_ACCOUNT,
UNKNOWN,
}

View File

@@ -1,12 +1,12 @@
package me.zacharias.bank;
public class BankAccount extends Account {
public BankAccount(String name, int balance) {
super(name, balance);
}
import static me.zacharias.bank.Account.Interests.BANK_ACCOUNT_INTEREST_RATE;
@Override
double getInterestRate() {
return 0.001;
/**
* This class only works as a way to have prefilled information, but can be replaced by just calling the {@link Account#Account(String, double, double)} instead
*/
public class BankAccount extends Account {
public BankAccount(String name, double balance) {
super(name, balance, BANK_ACCOUNT_INTEREST_RATE);
}
}

View File

@@ -1,5 +1,84 @@
package me.zacharias.bank;
public class Main {
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.Console;
import java.io.File;
import java.util.UUID;
import static me.zacharias.bank.Utils.*;
public class Main {
User user;
static Gson gson;
static {
GsonBuilder gsonBuilder = new GsonBuilder();
//gsonBuilder.registerTypeAdapter(Account.class, new AccountAdapter());
gson = gsonBuilder.create();
}
public static void main(String[] args) {
String user = SHA256("zacharias");
File userFile = new File("./users/" + user + ".json");
if(!userFile.exists()) {
User u = new User("zacharias");
u.createAccount("Konto", 0.0);
Account a = u.getAccount("Konto");
a.DepositTransaction(104, "Spawnar pängar", UUID.randomUUID());
String json = gson.toJson(u);
try {
WriteFile(userFile, Encrypt(json, "pass"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
new Main();
}
public Main() {
Console console = System.console();
if(console == null) {
System.out.println("No console.");
//return;
}
String name = "zacharias";//console.readLine("Enter your name: ");
String userHash = SHA256(name);
File userFile = new File("./users/" + userHash + ".json");
if(!userFile.exists()) {
System.out.println("User does not exist");
return;
}
String password = "pass";//new String(console.readPassword("Enter your password: "));
String userData;
try {
userData = Decrypt(ReadFile(userFile), password);
} catch (Exception e) {
throw new RuntimeException(e);
}
System.out.println(userData);
user = gson.fromJson(userData, User.class);
Account a = user.getAccount("Konto");
System.out.println(a.getClass().getSimpleName());
System.out.println(a.getBalance());
}
}

View File

@@ -1,13 +1,13 @@
package me.zacharias.bank;
import static me.zacharias.bank.Account.Interests.SAVINGS_ACCOUNT_INTEREST_RATE;
/**
* This class only works as a way to have prefilled information, but can be replaced by just calling the {@link Account#Account(String, double, double)} instead
*/
public class SavingsAccount extends Account{
public SavingsAccount(String name, int balance) {
super(name, balance);
}
@Override
double getInterestRate() {
return 0.05;
public SavingsAccount(String name, double balance) {
super(name, balance, SAVINGS_ACCOUNT_INTEREST_RATE);
}
}

View File

@@ -6,25 +6,41 @@ import java.util.UUID;
public class User {
UUID id;
String name;
String password;
ArrayList<Account> accounts;
public User(String name, String password) {
public User(String name) {
this.name = name;
this.password = password;
id = UUID.randomUUID();
accounts = new ArrayList<>();
}
public void createAccount(String name, int balance) {
public void createAccount(String name, double balance) {
Account account = new BankAccount(name, balance);
accounts.add(account);
}
public void createSavingsAccount(String name, int balance) {
public void createSavingsAccount(String name, double balance) {
Account account = new SavingsAccount(name, balance);
accounts.add(account);
}
public Account getAccount(String name) {
for (Account account : accounts) {
if (account.name.equals(name)) {
return account;
}
}
return null;
}
public Account getAccount(UUID id) {
for (Account account : accounts) {
if (account.id.equals(id)) {
return account;
}
}
return null;
}
public void deleteAccount(Account account) {
Account base = null;

View File

@@ -0,0 +1,133 @@
package me.zacharias.bank;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class Utils {
public static String SHA256(String input) {
MessageDigest sha256 = null;
try {
sha256 = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[] hash = sha256.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
private static final int KEY_SIZE = 256;
private static final int ITERATIONS = 65536;
private static final int SALT_LENGTH = 16;
private static final int IV_LENGTH = 12;
public static String Encrypt(String data, String username) throws Exception {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_LENGTH];
random.nextBytes(salt);
byte[] iv = new byte[IV_LENGTH];
random.nextBytes(iv);
SecretKey key = deriveKey(username, salt);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
byte[] encryptedData = cipher.doFinal(data.getBytes());
byte[] combined = new byte[salt.length + iv.length + encryptedData.length];
System.arraycopy(salt, 0, combined, 0, salt.length);
System.arraycopy(iv, 0, combined, salt.length, iv.length);
System.arraycopy(encryptedData, 0, combined, salt.length + iv.length, encryptedData.length);
return Base64.getEncoder().encodeToString(combined);
}
public static String Decrypt(String data, String username) throws Exception {
byte[] combined = Base64.getDecoder().decode(data);
// Extract salt, IV, and encrypted data
byte[] salt = new byte[SALT_LENGTH];
byte[] iv = new byte[IV_LENGTH];
byte[] cipherText = new byte[combined.length - SALT_LENGTH - IV_LENGTH];
System.arraycopy(combined, 0, salt, 0, salt.length);
System.arraycopy(combined, salt.length, iv, 0, iv.length);
System.arraycopy(combined, salt.length + iv.length, cipherText, 0, cipherText.length);
SecretKey key = deriveKey(username, salt);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec);
byte[] decrptedData = cipher.doFinal(cipherText);
return new String(decrptedData);
}
private static SecretKey deriveKey(String password, byte[] salt) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_SIZE);
return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
}
public static String ReadFile(File file)
{
if(!file.exists())
{
throw new RuntimeException(new FileNotFoundException(file.getPath()+" not found"));
}
try {
BufferedReader br = new BufferedReader(new FileReader(file));
StringBuilder sb = new StringBuilder();
String line;
while((line = br.readLine()) != null)
{
sb.append(line);
}
return sb.toString();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public static void WriteFile(File file, String data) {
if(file.exists())
{
file.delete();
}
try {
file.createNewFile();
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
bw.write(data);
bw.close();
}catch (IOException e)
{
throw new RuntimeException(e);
}
}
}