TlačiťTlačiť Slovenčina English Hľadať RSS

© 2005 – 2024 Roman Horváth, všetky práva vyhradené. Dnes je 23. 4. 2024.

Stránka sa načítava, prosím čakajte…

Dátum: 8. 8. 2021, pred tromi rokmi

  
obrázok 
Ukážka vzhľadu aplikácie po spustení. 
 

~

import knižnica.*;
import static knižnica.Svet.*;
import java.util.Arrays;

/**
 * Toto šifrovanie používa tabuľku povolených znakov a heslo. Správa aj heslo
 * smú obsahovať len znaky, ktoré sú uvedené v tabuľke povolených znakov.
 * Okrem tabuľky ide o jeden z najjednoduchších spôsobov šifrovania: heslo
 * určuje o koľko znakov sa majú posúvať znaky v zašifrovanej správe. Prvý
 * znak hesla určuje posun prvého znaku správy, druhý druhého a tak ďalej.
 * Znaky hesla sa používajú cyklicky, to znamená, že ak má heslo napríklad
 * päť znakov, tak šiesty znak správy je posúvaný opäť podľa prvého znaku
 * hesla a tak ďalej.
 */
public class MojaŠifra extends GRobot
{
	/**
	 * Tabuľka povolených znakov správy a hesla. Pri štarte aplikácie je
	 * zotriedená, inak by nebola použiteľná, pretože na rýchlejšie
	 * vyhľadávanie v nej používame binárne vyhľadávanie.
	 */
	public final static char[] tabuľka = {'0', '1', '2', '3', '4', '5', '6',
		'7', '8', '9', ',', '.', ';', ':', '-', '*', '/', '+', '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', 'Ž', ' '};

	// Atribúty na uloženie hesla, nespracovanej správy a spracovanej správy.
	// Ak šifrujeme, tak nespracovaná správa je nezašifrovaná správa
	// a spracovaná je zašifrovaná. Ak dešifrujeme, tak je to naopak.
	private String heslo = "heslo", nespracovaná = "";
	private final StringBuffer spracovaná = new StringBuffer();

	// Tlačidlá na ovládanie aplikácie:
	private final Tlačidlo zadajHeslo = new Tlačidlo("Zadaj heslo");
	private final Tlačidlo šifruj = new Tlačidlo("Šifruj");
	private final Tlačidlo dešifruj = new Tlačidlo("Dešifruj");

	private MojaŠifra()
	{
		super(400, 200, "Šifrovanie");

		// Úprava písma vnútornej konzoly (na spestrenie):
		// strop.písmo("Cambria", 12);

		// Výpis pôvodnej tabuľky (na účely ladenia):
		// vypíšRiadok(tabuľka);

		// Aby bola tabuľka použiteľná, musí byť zotriedená:
		Arrays.sort(tabuľka);
			// Pretože na vyhľadávanie prislúchajúceho znaku v nej používame
			// binárne vyhľadávanie.

		// Výpis zotriedenej tabuľky (na účely ladenia):
		// vypíšRiadok(tabuľka);

		// Vizuálne nastavenia prvkov a aplikácie:
		skry();
		zadajHeslo.skoč(0, 30);
		šifruj.skoč(-70, -30);
		dešifruj.skoč(+70, -30);
		zbaľ();
	}

	// Metóda slúžiaca na šifrovanie textu.
	private void šifruj()
	{
		// Vymažeme spracovanú správu:
		spracovaná.setLength(0);

		// Zistíme dĺžku nespracovanej správy (aby sme ju nemuseli zisťovať
		// pri každej iterácii cyklu for):
		int dĺžka = nespracovaná.length();

		// Zistíme dĺžku hesla (aby sa nám ľahšie počítalo):
		int dĺžkaHesla = heslo.length();

		// V cykle zašifrujeme každý znak správy (pričom výsledok ukladáme
		// do spracovanej správy).
		for (int i = 0; i < dĺžka; ++i)
		{
			// Najskôr prečítame znak nespracovanej správy:
			int znak = Arrays.binarySearch(tabuľka, nespracovaná.charAt(i));

			// Pre istotu overíme, či bol nájdený v tabuľke povolených znakov:
			if (znak < 0) throw new RuntimeException("Neplatný znak správy.");
				// Overenie síce v rámci tejto aplikácie vykonávame vždy pri
				// zadávaní správy aj hesla (s pomocou metódy jeKorektné),
				// ale to overenie slúži len na rýchle informovanie
				// používateľa, čiže jeho účelom je zabezpečenie
				// používateľského komfortu.
				//
				// Na tomto mieste (pri šifrovaní a rovnako aj pri
				// dešifrovaní) musíme urobiť overenie v každom prípade,
				// pretože heslo nemusí pochádzať od používateľa. Môže
				// pochádzať z iného zdroja. Bez tohto overenia by táto
				// metóda nebola bezpečná a univerzálna.

			int posun = Arrays.binarySearch(tabuľka,
				heslo.charAt(i % dĺžkaHesla));

			// Overenie toho, či je znak hesla platným znakom (čiže či je
			// v tabuľke povolených znakov):
			if (posun < 0) throw new RuntimeException("Neplatný znak hesla.");
				// Overovanie platnosti znakov hesla na tomto mieste je menej
				// efektívne, pretože po prejdení všetkých znakov hesla sú
				// ďalšie kontroly zbytočné… Skutočný zmysel má len prvá
				// kontrola. Táto kontrola by sa dala presunúť do samostatného
				// cyklu pred začiatkom šifrovania.

			// Do spracovanej správy pridáme znak nespracovanej správy
			// posunutý o hodnotu znaku hesla:
			spracovaná.append(tabuľka[((znak + posun) % tabuľka.length)]);
		}
	}

	// Metóda slúžiaca na dešifrovanie textu.
	private void dešifruj()
	{
		// (Rovnaké procesy ako pri šifrovaní.)
		spracovaná.setLength(0);
		int dĺžka = nespracovaná.length();
		int dĺžkaHesla = heslo.length();

		for (int i = 0; i < dĺžka; ++i)
		{
			// (Rovnaké procesy ako pri šifrovaní.)
			int znak = Arrays.binarySearch(tabuľka, nespracovaná.charAt(i));
			if (znak < 0) throw new RuntimeException("Neplatný znak správy.");
			int posun = Arrays.binarySearch(tabuľka,
				heslo.charAt(i % dĺžkaHesla));
			if (posun < 0) throw new RuntimeException("Neplatný znak hesla.");

			// Aj pri dešifrovaní pridáme do spracovanej správy znak
			// nespracovanej správy posunutý o hodnotu znaku hesla,
			// ale spätne:
			spracovaná.append(tabuľka[((tabuľka.length + znak - posun) %
				tabuľka.length)]);
		}
	}

	// Súkromná metóda slúžiaca na overenie korektnosti hesla a správy.
	// Na korektné fungovanie algoritmu je nevyhnutné, aby heslo aj správa
	// obsahovali iba znaky uvedené v tabuľke povolených znakov.
	private static boolean jeKorektné(String veta)
	{
		if (veta.isEmpty()) return false;
		for (int i = veta.length() - 1; i >= 0; --i)
			if (Arrays.binarySearch(tabuľka, veta.charAt(i)) < 0) return false;
		return true;
	}

	/** Reakcia na voľbu (stlačenie) niektorého z tlačidiel. */
	@Override public void voľbaTlačidla()
	{
		// Ak bolo zvolené tlačidlo „Zadaj heslo“:
		if (zadajHeslo.aktivované())
		{
			// Premenná opakuj musí byť deklarovaná pred cyklom do-while,
			// inak by nemohla byť použitá v podmienke cyklu:
			boolean opakuj;

			// Uloženie starého hesla do dočasnej premennej:
			String zmena = heslo;

			do
			{
				// „Reset“ premennej opakuj na false (predpokladáme, že
				// opakovanie nebude treba, ale ak sa niečo pokazí, premennú
				// nastavíme na true a cyklus sa tým zopakuje):
				opakuj = false;

				// Upravíme reťazec hesla v dočasnej premennej:
				zmena = upravReťazec(zmena, "Zadaj heslo", "Zmena hesla");

				// Ak používateľ zadávanie nezrušil…
				if (null != zmena)
				{
					// … overí sa platnosť hesla. Ak je heslo vyhovujúce,
					// uloží sa. Ak nie, vyžiada sa opakované zadanie.
					if (jeKorektné(zmena)) heslo = zmena; else
					{
						chyba("Heslo obsahuje nepovolený znak.");
						opakuj  = true;
							// (Poznámka: Pôvodné heslo sme vložili do
							// dočasnej premennej pred cyklom, takže v ďalšej
							// iterácii sa bude upravovať nová verzia, ktorá
							// nevyhovela – používateľ sa bude môcť opraviť.)
					}
				}
			}
			while (opakuj);
		}

		// Ak bolo zvolené tlačidlo „Šifruj“:
		else if (šifruj.aktivované())
		{
			// Premenná opakuj musí byť deklarovaná pred cyklom do-while,
			// inak by nemohla byť použitá v podmienke cyklu:
			boolean opakuj;

			// Predvolíme zadanie prázdnej správy:
			String správa = "";

			do
			{
				// „Reset“ premennej opakuj na false (predpokladáme, že
				// opakovanie nebude treba, ale ak sa niečo pokazí, premennú
				// nastavíme na true a cyklus sa tým zopakuje):
				opakuj = false;

				// Upravíme aktuálnu správu – pri prvej iterácii je prázdna,
				// pri každej ďalšej naposledy zadaná:
				správa = upravReťazec(správa, "Zadaj otvorený text",
					"Šifrovanie");

				// Ak používateľ zadávanie nezrušil, pokračujeme ďalšími
				// kontrolami:
				if (null != správa)
				{
					// Overenie korektnosti správy (či obsahuje len povolené
					// znaky):
					if (jeKorektné(správa))
					{
						// Do premennej nespracovaná uložíme zadanú verziu
						// správy…
						nespracovaná = správa;

						try
						{
							// … a pokúsime sa ju zašifrovať:
							šifruj();
							upravReťazec(spracovaná.toString(),
								"", "Zašifrovaná správa");
						}
						catch (RuntimeException e)
						{
							// V prípade vzniku akejkoľvek chyby ju vypíšeme
							// v dialógu, ale zadanie už neopakujeme:
							chyba(e.getMessage());
						}
					}
					else
					{
						// Ak správa obsahovala nepovolený znak, oznámime to
						// používateľovi a vyžiadame opakovanie zadania:
						chyba("Správa obsahuje nepovolený znak.");
						opakuj  = true;
					}
				}
			}
			while (opakuj);
		}

		// Ak bolo zvolené tlačidlo „Dešifruj“:
		else if (dešifruj.aktivované())
		{
			// Premenná opakuj musí byť deklarovaná pred cyklom do-while,
			// inak by nemohla byť použitá v podmienke cyklu:
			boolean opakuj;

			// Predvolíme zadanie prázdnej správy:
			String správa = "";

			do
			{
				// „Reset“ premennej opakuj na false (predpokladáme, že
				// opakovanie nebude treba, ale ak sa niečo pokazí, premennú
				// nastavíme na true a cyklus sa tým zopakuje):
				opakuj = false;

				// Upravíme aktuálnu správu – pri prvej iterácii je prázdna,
				// pri každej ďalšej naposledy zadaná:
				správa = upravReťazec(správa, "Zadaj zašifrovaný text",
					"Dešifrovanie");

				// Ak používateľ zadávanie nezrušil, pokračujeme ďalšími
				// kontrolami:
				if (null != správa)
				{
					// Overenie korektnosti správy (či obsahuje len povolené
					// znaky):
					if (jeKorektné(správa))
					{
						// Do premennej nespracovaná uložíme zadanú verziu
						// správy…
						nespracovaná = správa;

						try
						{
							// … a pokúsime sa ju dešifrovať:
							dešifruj();
							upravReťazec(spracovaná.toString(),
								"", "Dešifrovaná správa");
						}
						catch (RuntimeException e)
						{
							// V prípade vzniku akejkoľvek chyby ju vypíšeme
							// v dialógu, ale zadanie už neopakujeme:
							chyba(e.getMessage());
						}
					}
					else
					{
						// Ak správa obsahovala nepovolený znak, oznámime to
						// používateľovi a vyžiadame opakovanie zadania:
						chyba("Správa obsahuje nepovolený znak.");
						opakuj  = true;
					}
				}
			}
			while (opakuj);
		}
	}

	/** Hlavná metóda. */
	public static void main(String[] args)
	{
		// Načítanie konfigurácie a spustenie aplikácie…
		použiKonfiguráciu("MojaŠifra.cfg");
		new MojaŠifra();
	}
}

Ukážky výsledkov

Heslo: heslo
Pôvodná správa: Roman
Zašifrovaná správa: č,8Ť5
Heslo: činčila
Pôvodná správa: Babka išla večer na návštevu.
Zašifrovaná správa: ,ŕžTŕlŕó-Žč7 JN3nWŕlŽr7icť:.Ľ

Tipy na testovanie a zlepšovanie

  1. Skúste dešifrovať správu so zlým heslom.
  1. Ak „odrežme“ iba jeden (posledný) znak hesla, tak sa začiatok správy dešifruje korektne – tu sa samo naznačuje slabé miesto šifrovania.
  2. Ak heslo úplne „pomiešame“ (alebo len pridáme znak na začiatok), výsledok bude nečitateľný.
  1. Tipy:
  • Môžete šifrovať správu viacerými heslami – dá sa doprogramovať, aby to bolo jednoduchšie, napríklad šifrovanie vetou, pričom každé slovo by bolo samostatné heslo, ale pamätajte – čím dlhšie heslo, tým lepšie.
  • Môžete pridať do tabuľky šifier viac znakov, aj keď ich nikdy nepoužijete, zmení sa tým spôsob premiešania, ale pozor, čím viac znakov pridáte, tým viac sa priblížite ku štandardu ASCII/Unicode a to tiež nie je dobré.