ITI0011:harjutus 16
Kirjeldus
Kirjutada programm, mis loeb etteantud properties-failist kasutajanime ja parooli ning salvestab turvalisema variandi teise faili. Teise faili salvestatakse sama kasutaja andmed, aga parool on soolatud räsina (salted hash). Mõlemad failid on properties-tüüpi failid (vt viiteid allpool).
Algne fail:
username=Juku password=raskeparool123
Teie peate implementeerima failist lugemise ja kirjutamise meetodid - vastavalt readUserDataFromFile
ja saveNewUserDataToFile
.
Programm kontrollib, kas on olemas räsitud parooliga fail "newUser.properties". Kui see puudub, siis tehakse see fail SaveNewUserDataToFile meetodiga. Uues failis peavad olema võtmesõnad: "username", "password" ja "salt". Kui aga käivitamisel eksisteerib "newUser.properties" mõnest eelnevast käitamisest, siis programm kutsub esile authenticate meetodi, mis laseb kasutajal parooli pakkuda.
Esmalt oleks mõistlik realiseerida readUserDataFromFile
meetod, kuna uue faili andmed on otseses sõltuvuses meie antud faili sisust - mida võib vabalt muuta, aga kindlasti peavad olema võtmesõnad "username" ja "password".
Programmi eesmärk on näidata, et tundlikke andmeid pole hea mõte salvestada inimloetaval kujul.
Lugemine soolatud räsimise kohta: https://crackstation.net/hashing-security.htm
Kui pikad tekstid pole päris Teie rada, siis järgmine link on ka piisav lugemine: http://security.stackexchange.com/questions/51959/why-are-salted-hashes-more-secure
Kiire ülevaade Java Propertiesist: http://www.mkyong.com/java/java-properties-file-examples/
Mall
<source lang="java"> /**
* Created by odin on 20.04.15. */
import java.io.*; import java.security.MessageDigest; import java.util.HashMap; import java.util.Properties; import java.util.Scanner;
public class Main {
private static final String INPUT_FILE = "user.properties"; private static final String OUTPUT_FILE = "newUser.properties"; private static final int LENGTH_OF_SALT = 10; private static final int MAX_CHANCES_COUNT = 3;
public static void main(String[] args) { try { // check whether a user with a hashed password already exists if(new File(OUTPUT_FILE).isFile()) { HashMap<String, String> data = readUserDataFromFile(OUTPUT_FILE); String username = data.get("username"); String password = data.get("password"); String salt = data.get("salt"); authenticate(username, password, salt); } else { // "register" new user with salted hash HashMap<String, String> data = readUserDataFromFile(INPUT_FILE); String username = data.get("username"); String password = data.get("password"); String salt = md5MyString("saltyRandomString").substring(0, LENGTH_OF_SALT); password = md5MyString(password + salt); saveNewUserData(username, password, salt); } } catch (Exception e) { e.printStackTrace(); } }
private static void authenticate(String username, String password, String salt) throws Exception { Scanner userInput = new Scanner(System.in); String guess; System.out.println("Hello, You now have " + MAX_CHANCES_COUNT + " chances to guess " + username + "'s password."); int chances; for (chances = MAX_CHANCES_COUNT - 1; chances >= 0; chances--) { guess = userInput.next(); if (md5MyString(guess + salt).equals(password)) { System.out.println("That's correct!"); break; } else if (chances > 0) { System.out.println("That's invalid! " + chances + " guess" + (chances > 1 ? "es" : "") + " left."); } } if (chances < 0) System.out.println("Sorry, You didn't guess " + username + "'s password"); }
private static HashMap<String, String> readUserDataFromFile(String filename) throws IOException { HashMap resultMap = new HashMap<String, String>(); Properties prop = new Properties(); InputStream input = new FileInputStream(filename); prop.load(input); if (input != null) input.close(); prop.forEach((key, value) -> resultMap.put(key, value)); return resultMap; }
private static void saveNewUserData(String username, String password, String salt) throws IOException { Properties prop = new Properties(); OutputStream output = new FileOutputStream(OUTPUT_FILE); prop.setProperty("username", username); prop.setProperty("password", password); prop.setProperty("salt", salt); prop.store(output, null); if (output != null) output.close(); }
/* * Maagia, sellest ei pea aru saama. * tl;dr: * Hashimist on vaja, et andmete hoiustamisel poleks * privaatsed andmed (loe: paroolid) lihtsasti loetavad * */ private static String md5MyString(String str) throws Exception { MessageDigest m = MessageDigest.getInstance("MD5"); m.update(str.getBytes("UTF8")); byte s[] = m.digest(); String result = ""; for (int i = 0; i < s.length; i++) { result += Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6); } return result; }
} </source>