Programování

V Javě věříme

Důvěřovat všem? Nikomu nevěř? Zní to trochu jako Akty X, ale pokud jde o důvěrné informace, vědět, komu důvěřujete, je stejně důležité jako vědět, čemu jim důvěřujete. Tento koncept je stejně důležitý pro aplikace i pro lidi. Koneckonců jsme z aplikací udělali správce našich informací a správce našich zdrojů. Je to pravda v celém podniku - aplikace obsahují důležité informace o našem podnikání a našich zákaznících - a platí to i pro stolní počítače. Nemohu vám říci, kolikrát jsem byl požádán, jak napsat applet, který skenuje disk uživatele, aby jeden uživatel mohl ovládat prohlížeč jiného uživatele nebo zachytit soukromé informace.

Java, která je platformou pro vývoj sítí, kterou je, se musela s problémem důvěry vyrovnat přímo. Výsledkem je rozhraní Java Security API a Java Cryptography Architecture.

Krátký pohled dozadu

Než se ponořím do API, kódu a komentářů, rád bych se krátce vrátil k diskusi z minulého měsíce. Pokud se k nám připojujete poprvé, možná budete chtít zálohovat měsíc a přečíst si „Podepsáno a doručeno: Úvod do zabezpečení a ověřování.“ Tento sloupec poskytuje důkladný úvod ke všem pojmům a konceptům, které budu tento měsíc používat.

Zabezpečení a ověřování řeší dvě zásadní záležitosti: prokazování zprávy bylo vytvořeno určitou entitou a prokazování zprávy nebylo po vytvoření změněno. Jedním ze způsobů, jak splnit oba tyto cíle, je použití digitálních podpisů.

Digitální podpisy silně závisí na odvětví kryptografie známém jako kryptografie veřejného klíče. Algoritmy veřejného klíče se vyznačují skutečností, že se spoléhají spíše na pár párů klíčů (jeden soukromý a jeden veřejný) než na jediný klíč. Subjekt udržuje svůj soukromý klíč v tajnosti, ale zpřístupňuje svůj veřejný klíč.

Algoritmus digitálního podpisu bere jako vstup zprávu a soukromý klíč entity a generuje digitální podpis. Digitální podpis je vytvořen takovým způsobem, že kdokoli může vzít veřejný klíč entity a použít jej k ověření, že entita ve skutečnosti podepsala dotyčnou zprávu. Kromě toho, pokud byla původní zpráva pozměněna, podpis již nelze ověřit. Digitální podpisy poskytují jednu další výhodu: jakmile entita podepsala a distribuovala zprávu, je nemožné, aby její původce popřel, že podepsal zprávu (aniž by si přesto nárokoval odcizení jeho soukromého klíče).

Motorů a poskytovatelů

Rozhraní Java Cryptography API definuje sadu nástrojů Java pro zabezpečení a ověřování. Java Cryptography Architecture (JCA) popisuje, jak používat API. Aby byla zajištěna nejvyšší míra flexibility pro vývojáře i pro koncového uživatele, zahrnuje JCA dva hlavní principy:

  1. Architektura by měla podporovat nezávislost a rozšiřitelnost algoritmu. Vývojář musí být schopen psát aplikace, aniž by je příliš úzce spojoval s konkrétním algoritmem. Při vývoji nových algoritmů je navíc nutné je snadno integrovat do stávajících algoritmů.

  2. Architektura by měla podporovat nezávislost a interoperabilitu implementace. Vývojář musí být schopen psát aplikace, aniž by je vázal na implementaci algoritmu konkrétního dodavatele. Kromě toho musí implementovat algoritmus poskytovaný různými dodavateli.

Pro splnění těchto dvou požadavků založili vývojáři rozhraní Java Cryptography API svůj design na systému motorů a poskytovatelů.

Motory vytvářejí instance generátorů digestu zpráv, generátorů digitálních podpisů a generátorů párů klíčů. Každá instance se používá k provedení odpovídající funkce.

Kanonický engine v JCA je třída, která poskytuje statickou metodu (nebo metody) s názvem getInstance (), který vrací instanci třídy, která implementuje kryptograficky významný algoritmus. The getInstance () metoda přichází ve formě jednoho argumentu i dvou argumentů. V obou případech je prvním argumentem název algoritmu. JCA poskytuje seznam standardních jmen, i když ne všechny budou uvedeny v konkrétním vydání. Druhý argument vybírá poskytovatele.

Poskytovatel SUN

Pouze jeden poskytovatel - SLUNCE - je dodáván v JDK 1.1. SUN poskytuje jak implementaci NIST Digital Signature Algorithm (DSA), tak implementaci algoritmů digestu zpráv MD5 a NIST SHA-1.

Třída MessageDigest

Začneme tím, že se podíváme na kód, který ze zprávy generuje souhrn zprávy.

MessageDigest messagedigest = MessageDigest.getInstance ("SHA");

MessageDigest messagedigest = MessageDigest.getInstance ("SHA", "SUN");

Jak jsem před chvílí zmínil, getInstance () metoda má dvě příchutě. První vyžaduje zadání pouze algoritmu. Druhý vyžaduje zadání algoritmu i poskytovatele. Oba vracejí instanci třídy, která implementuje algoritmus SHA.

Dále předáme zprávu generátorem přehledu zpráv.

int n = 0; byte [] rgb = nový bajt [1 000]; while ((n = inputstreamMessage.read (rgb))> -1) {messagedigest.update (rgb, 0, n); }

Zde předpokládáme, že zpráva je k dispozici jako vstupní proud. Tento kód funguje dobře pro velké zprávy neznámé délky. The Aktualizace() metoda také přijímá jeden bajt jako argument pro zprávy o délce několika bytů a bajtové pole pro zprávy pevné nebo předvídatelné velikosti.

rgb = messagedigest.digest ();

Poslední krok zahrnuje generování samotného přehledu zpráv. Výsledný přehled je zakódován do pole bajtů.

Jak vidíte, JCA pohodlně skrývá všechny podrobnosti implementace na nízké úrovni a podrobnosti specifické pro algoritmus, což vám umožní pracovat na vyšší a abstraktnější úrovni.

Jedním z rizik takového abstraktního přístupu je samozřejmě zvýšená pravděpodobnost, že nerozpoznáme chybný výstup způsobený chybami. Vzhledem k roli kryptografie to může být významný problém.

Zvažte chybu „off-by-one“ v řádku aktualizace níže:

int n = 0; byte [] rgb = nový bajt [1 000]; while ((n = inputstreamMessage.read (rgb))> -1) {messagedigest.update (rgb, 0, n - 1); }

Programátoři C, C ++ a Java používají idiom limit-minus-one tak často, že jeho psaní se stává téměř automatickým - i když to není vhodné. Výše uvedený kód bude zkompilován a spustitelný soubor bude spuštěn bez chyby nebo varování, ale výsledný přehled zpráv bude chybný.

Naštěstí je JCA dobře promyšlený a dobře navržený, takže potenciální úskalí jako výše uvedená jsou relativně vzácná.

Než přejdeme k generátorům párů klíčů, podívejte se na

MessageDigestGenerator, kompletní zdrojový kód pro program, který generuje přehled zpráv.

Třída KeyPairGenerator

Ke generování digitálního podpisu (a šifrování dat) potřebujeme klíče.

Generování klíčů ve své formě nezávislé na algoritmu není podstatně obtížnější než vytváření a používání přehledu zpráv.

KeyPairGenerator keypairgenerator = KeyPairGenerator.getInstance ("DSA");

Stejně jako ve výše uvedeném příkladu zprávy tento kód vytvoří instanci třídy, která generuje klíče kompatibilní s DSA. Druhý (v případě potřeby) argument určuje poskytovatele.

Po vytvoření instance generátoru párů klíčů je nutné ji inicializovat. Generátory párů klíčů můžeme inicializovat jedním ze dvou způsobů: nezávislý na algoritmu nebo závislý na algoritmu. Metoda, kterou použijete, závisí na rozsahu kontroly nad konečným výsledkem.

keypairgenerator.initialize (1024, nový SecureRandom ());

Klíče založené na různých algoritmech se liší v tom, jak jsou generovány, ale mají jeden společný parametr - klíč síla. Síla je relativní pojem, který zhruba odpovídá tomu, jak těžké bude „zlomit“ klíč. Pokud použijete inicializátor nezávislý na algoritmu, můžete zadat pouze sílu - jakékoli hodnoty závislé na algoritmu předpokládají rozumné výchozí hodnoty.

DSAKeyPairGenerator dsakeypairgenerator = (DSAKeyPairGenerator) keypairgenerator; DSAParams dsaparams = new DSAParams () {private BigInteger p = BigInteger (...); soukromý BigInteger q = BigInteger (...); soukromý BigInteger g = BigInteger (...); public BigInteger getP () {návrat p; } public BigInteger getQ () {return q; } public BigInteger getG () {return g; }}; dsakeypairgenerator.initialize (dsaparams, nový SecureRandom ());

Výchozí nastavení jsou obvykle dostatečně dobrá, ale pokud potřebujete větší kontrolu, je k dispozici. Předpokládejme, že jste použili motor k vytvoření generátoru klíčů kompatibilních s DSA, jako v předchozím kódu. V zákulisí motor načetl a vytvořil instanci instance třídy, která implementuje DSAKeyPairGenerator rozhraní. Pokud použijeme generický generátor párů klíčů, který jsme obdrželi DSAKeyPairGenerator, pak získáme přístup k metodě inicializace závislé na algoritmu.

K inicializaci generátoru párů klíčů DSA potřebujeme tři hodnoty: prime P, subprime Q, a základna G. Tyto hodnoty jsou zachyceny v instanci vnitřní třídy, která je předána do inicializovat () metoda.

The SecureRandom třída poskytuje bezpečný zdroj náhodných čísel použitých při generování dvojice klíčů.

návrat keypairgenerator.generateKeyPair ();

Poslední krok zahrnuje generování samotného páru klíčů.

Než přejdeme k digitálním podpisům, podívejte se na KeyTools, kompletní zdrojový kód programu, který generuje pár klíčů.

Podpis třídy

Vytvoření a použití instance Podpis třída se podstatně neliší od žádného ze dvou předchozích příkladů. Rozdíly spočívají v tom, jak se instance používá - buď k podepsání, nebo k ověření zprávy.

Signature signature = Signature.getInstance ("DSA");

Stejně jako dříve používáme engine k získání instance příslušného typu. Další postup závisí na tom, zda zprávu podepisujeme nebo ověřujeme.

signature.initSign (privatekey);

Abychom mohli podepsat zprávu, musíme nejprve inicializovat instanci podpisu pomocí soukromého klíče entity, která zprávu podepisuje.

signature.initVerify (publickey);

Abychom mohli ověřit zprávu, musíme inicializovat instanci podpisu s veřejným klíčem entity, která tvrdí, že zprávu podepsala.

int n = 0; byte [] rgb = nový bajt [1 000]; while ((n = inputstreamMessage.read (rgb))> -1) {signature.update (rgb, 0, n); }

Dále, bez ohledu na to, zda podepisujeme nebo ověřujeme, musíme předat zprávu generátorem podpisů. Všimnete si, jak podobný je proces s předchozím příkladem generování přehledu zpráv.

Poslední krok spočívá v generování podpisu nebo ověření podpisu.

rgb = signature.sign ();

Pokud podepisujeme zprávu, podepsat() metoda vrací podpis.

signature.verify (rgbSignature);

Pokud ověřujeme podpis dříve vygenerovaný ze zprávy, musíme použít znak ověřit () metoda. Jako parametr bere dříve generovaný podpis a určuje, zda je nebo není stále platný.

Než věci zabalíme, podívejte se na Sign.java, kompletní zdrojový kód pro program, který podepisuje zprávu, a Verify.java, kompletní zdrojový kód pro program, který ověří zprávu.

Závěr

Pokud se vyzbrojíte nástroji a technikami, které jsem tento měsíc představil, budete více než připraveni zabezpečit své aplikace. Díky Java Cryptography API je proces téměř bez námahy. Vydání 1.2 sady Java Developers Kit slibuje ještě více. Zůstaňte naladěni.

Příští měsíc se vrátím zpět na middlewarové území. Vezmu si trochu RMI, nějaké podprocesy a hromadu kódu a ukážu vám, jak vytvořit svůj vlastní middleware orientovaný na zprávy.

Todd Sundsted píše programy od doby, kdy byly počítače dostupné v pohodlných modelech pro stolní počítače. Ačkoli se Todd původně zajímal o vytváření aplikací distribuovaných objektů v C ++, přešel k programovacímu jazyku Java, když se stal jasnou volbou pro tento druh věcí. Kromě psaní je Todd prezidentem společnosti Etcee, která nabízí služby v oblasti školení, mentoringu, poradenství a vývoje softwaru.

Další informace o tomto tématu

  • Stáhněte si kompletní zdrojový kód //www.javaworld.com/jw-01-1999/howto/jw-01-howto.zip
  • Přehled Java Security API //www.javasoft.com/products/jdk/1.1/docs/guide/security/JavaSecurityOverview.html
  • Java Cryptography Architecture //www.javasoft.com/products/jdk/1.1/docs/guide/security/CryptoSpec.html
  • Stránka zabezpečení Sunu Java //java.sun.com/security/index.html
  • Časté dotazy RSA o kryptografii //www.rsa.com/rsalabs/faq/
  • Kryptografická politika a informace //www.crypto.com/
  • Přečtěte si Toddovy předchozí sloupce Java s pokyny //www.javaworld.com/topicalindex/jw-ti-howto.html

Tento příběh „V prostředí Java důvěřujeme“ původně publikoval server JavaWorld.