Programmering

I Java stoler vi på

Stol på alle? Stol ikke på nogen? Lyder lidt som X-filer, men når det kommer til fortrolige oplysninger, er det lige så vigtigt at vide, hvem det er, du stoler på, som at vide, hvad du stoler på dem med. Dette koncept er lige så vigtigt for applikationer som for mennesker. Når alt kommer til alt, har vi indgivet ansøgere til opbevaring af vores information og forvaltere af vores ressourcer. Det er sandt på tværs af virksomheden - applikationer indeholder kritisk information om vores forretning og vores kunder - og det gælder på skrivebordet. Jeg kan ikke fortælle dig, hvor mange gange jeg er blevet spurgt, hvordan jeg skriver en applet, der scanner en brugers drev, så en bruger kan kommandere en anden brugers browser eller fange private oplysninger.

Java, som er den netværksudviklingsplatform, det er, har været nødt til at tackle problemet med tillid på hovedet. Resultatet er Java Security API og Java Cryptography Architecture.

Et kort blik bagud

Før jeg dykker ned i API'er, kode og kommentarer, vil jeg gerne kort gennemgå sidste måneds diskussion. Hvis du tilmelder os os for første gang, kan du tage backup af en måned og læse "Underskrevet og leveret: En introduktion til sikkerhed og godkendelse." Denne kolonne giver en grundig introduktion til alle de termer og koncepter, jeg bruger denne måned.

Sikkerhed og godkendelse adresserer to afgørende bekymringer: at bevise en besked blev oprettet af en bestemt enhed, og at bevise en besked blev ikke manipuleret med, efter at den blev oprettet. En måde at nå begge disse mål er ved brug af digitale signaturer.

Digitale signaturer afhænger stærkt af en gren af ​​kryptografi kendt som public key-kryptografi. Offentlige nøglealgoritmer er kendetegnet ved, at de er afhængige af et matchet par nøgler (en privat og en offentlig) snarere end en enkelt nøgle. En enhed holder sin private nøgle hemmelig, men gør sin offentlige nøgle tilgængelig.

En digital signaturalgoritme tager som input en besked og en enheds private nøgle og genererer en digital signatur. Den digitale signatur oprettes på en sådan måde, at enhver kan tage enhedens offentlige nøgle og bruge den til at verificere, at enheden faktisk har underskrevet den pågældende meddelelse. Desuden, hvis den oprindelige besked er blevet manipuleret med, kan signaturen ikke længere bekræftes. Digitale signaturer giver en yderligere fordel: når en enhed har underskrevet og distribueret en besked, er det umuligt for dets ophavsmand at benægte at have underskrevet meddelelsen (uden at hævde, at hans eller hendes private nøgle blev stjålet, alligevel).

Af motorer og udbydere

Java Cryptography API definerer Java-værktøjssættet til sikkerhed og godkendelse. Java Cryptography Architecture (JCA) beskriver, hvordan man bruger API'en. For at sikre den højeste grad af fleksibilitet for både udvikleren og slutbrugeren omfatter JCA to vejledende principper:

  1. Arkitekturen skal understøtte algoritmeuafhængighed og udvidelse. En udvikler skal være i stand til at skrive applikationer uden at binde dem for tæt til en bestemt algoritme. Når nye algoritmer udvikles, skal de desuden let integreres med eksisterende algoritmer.

  2. Arkitekturen skal understøtte implementeringsuafhængighed og interoperabilitet. En udvikler skal være i stand til at skrive applikationer uden at binde dem til en bestemt leverandørs implementering af en algoritme. Derudover skal implementeringer af en algoritme leveret af forskellige leverandører interoperere.

For at opfylde disse to krav baserede udviklerne af Java Cryptography API deres design på et system af motorer og udbydere.

Motorer producerer forekomster af meddelelsesfordøjelsesgeneratorer, generatorer for digital signatur og nøglepargeneratorer. Hver forekomst bruges til at udføre den tilsvarende funktion.

Den kanoniske motor i JCA er en klasse, der giver en statisk (eller metoder) navngivet getInstance (), som returnerer en forekomst af en klasse, der implementerer en kryptografisk signifikant algoritme. Det getInstance () Metoden kommer i både en-argument og en to-argument form. I begge tilfælde er det første argument navnet på algoritmen. JCA giver en liste over standardnavne, men ikke alle vil blive leveret i en bestemt udgivelse. Det andet argument vælger en udbyder.

SUN-udbyderen

Kun én udbyder - SOL - leveres i JDK 1.1. SUN tilvejebringer både en implementering af NIST Digital Signature Algorithm (DSA) og en implementering af MD5 og NIST SHA-1-meddelelsesfordøjelsesalgoritmerne.

Klassemeddelelse Fordøjelse

Vi begynder med at se på kode, der genererer en meddelelsesfordøjelse fra en besked.

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

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

Som jeg nævnte lige for et øjeblik siden, getInstance () metoden kommer i to varianter. Den første kræver kun algoritmen, der skal specificeres. Det andet kræver, at både algoritmen og udbyderen skal specificeres. Begge returnerer en forekomst af en klasse, der implementerer SHA-algoritmen.

Dernæst sender vi meddelelsen gennem meddelelsesfordøjelsesgeneratoren.

int n = 0; byte [] rgb = ny byte [1000]; mens ((n = inputstreamMessage.read (rgb))> -1) {messagedigest.update (rgb, 0, n); }

Her antager vi, at meddelelsen er tilgængelig som en inputstrøm. Denne kode fungerer godt til store meddelelser af ukendt længde. Det opdater () Metoden accepterer også en enkelt byte som et argument for meddelelser med et par byte i længden og et byte-array til meddelelser af en fast eller forudsigelig størrelse.

rgb = messagedigest.digest ();

Det sidste trin involverer generering af beskedfordøjelsen selv. Den resulterende fordøjelse er kodet i en række bytes.

Som du kan se, skjuler JCA bekvemt alle implementeringer på lavt niveau og algoritmespecifikke detaljer, så du kan arbejde på et højere, mere abstrakt niveau.

Selvfølgelig er en af ​​risiciene ved en sådan abstrakt tilgang den øgede sandsynlighed for, at vi ikke genkender fejlagtig output som følge af bugs. I betragtning af kryptografis rolle kan dette være et væsentligt problem.

Overvej fejlen "off-by-one" i opdateringslinjen nedenfor:

int n = 0; byte [] rgb = ny byte [1000]; mens ((n = inputstreamMessage.read (rgb))> -1) {messagedigest.update (rgb, 0, n - 1); }

C-, C ++- og Java-programmører bruger limit-minus-one-idiomet så ofte, at det bliver næsten automatisk at skrive - selv når det ikke er passende. Koden ovenfor kompileres, og den eksekverbare kører uden fejl eller advarsel, men den resulterende meddelelsesfordøjelse vil være forkert.

Heldigvis er JCA gennemtænkt og godt designet, hvilket gør potentielle faldgruber som den ovenfor relativt sjældne.

Inden vi går videre til nøglepargeneratorer, skal du kigge på

MessageDigestGenerator, den komplette kildekode til et program, der genererer en meddelelsesfordøjelse.

Klasse KeyPairGenerator

For at generere en digital signatur (og kryptere data) har vi brug for nøgler.

Nøglegenerering i sin algoritmeuafhængige form er ikke væsentligt vanskeligere end at oprette og bruge en meddelelsesfordøjelse.

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

Som i eksemplet med meddelelsesfordøjelsen ovenfor opretter denne kode en forekomst af en klasse, der genererer DSA-kompatible nøgler. Et andet (om nødvendigt) argument specificerer udbyderen.

Når en nøglepar-generatorinstans er oprettet, skal den initialiseres. Vi kan initialisere nøglepargeneratorer på en af ​​to måder: algoritmeuafhængig eller algoritmeafhængig. Hvilken metode, du bruger, afhænger af den kontrolmængde, du ønsker over det endelige resultat.

keypairgenerator.initialize (1024, ny SecureRandom ());

Nøgler baseret på forskellige algoritmer adskiller sig i, hvordan de genereres, men de har en parameter til fælles - nøglerne styrke. Styrke er et relativt udtryk, der omtrent svarer til, hvor hårdt nøglen vil være at "bryde". Hvis du bruger den algoritmeuafhængige initialisering, kan du kun angive styrken - alle algoritmeafhængige værdier antager rimelige standarder.

DSAKeyPairGenerator dsakeypairgenerator = (DSAKeyPairGenerator) tastaturgenerator; DSAParams dsaparams = nye DSAParams () {private BigInteger p = BigInteger (...); privat BigInteger q = BigInteger (...); privat BigInteger g = BigInteger (...); offentlig BigInteger getP () {return p; } offentlig BigInteger getQ () {return q; } offentlig BigInteger getG () {return g; }}; dsakeypairgenerator.initialize (dsaparams, ny SecureRandom ());

Selvom standardindstillingerne normalt er gode nok, er de tilgængelige, hvis du har brug for mere kontrol. Lad os antage, at du brugte motoren til at oprette en generator af DSA-kompatible nøgler, som i koden ovenfor. Bag kulisserne lastede motoren op og instantierede en instans af en klasse, der implementerer DSAKeyPairGenerator interface. Hvis vi caster den generiske nøglepargenerator, vi modtog til DSAKeyPairGenerator, så får vi adgang til den algoritmeafhængige initialiseringsmetode.

For at initialisere en DSA-nøglepargenerator har vi brug for tre værdier: prime P, subprime Q, og basen G. Disse værdier er fanget i en indre klasseinstans, der sendes til initialisere () metode.

Det SecureRandom klasse giver en sikker kilde til tilfældige tal, der bruges i nøglepargenerering.

returner keypairgenerator.generateKeyPair ();

Det sidste trin involverer generering af selve nøgleparret.

Inden vi går videre til digitale signaturer, skal du kigge på KeyTools, den komplette kildekode for et program, der genererer et nøglepar.

Klassesignatur

Oprettelse og brug af en forekomst af Underskrift klasse er ikke væsentligt forskellig fra nogen af ​​de to tidligere eksempler. Forskellene ligger i, hvordan forekomsten bruges - enten til at underskrive eller bekræfte en besked.

Signatur signatur = Signature.getInstance ("DSA");

Ligesom før bruger vi motoren til at få en forekomst af den passende type. Hvad vi gør næste afhænger af, om vi underskriver eller bekræfter en besked.

signatur.initSign (privatstyring);

For at underskrive en meddelelse skal vi først initialisere signaturforekomsten med den private nøgle for den enhed, der underskriver meddelelsen.

signatur.initVerify (offentlig nøgle);

For at bekræfte en meddelelse skal vi initialisere signaturforekomsten med den offentlige nøgle for den enhed, der hævder, at den har underskrevet meddelelsen.

int n = 0; byte [] rgb = ny byte [1000]; mens ((n = inputstreamMessage.read (rgb))> -1) {signatur.opdatering (rgb, 0, n); }

Dernæst, uanset om vi underskriver eller verificerer, skal vi sende meddelelsen gennem signaturgeneratoren. Du bemærker, hvor meget processen ligner det tidligere eksempel på generering af en meddelelsesfordøjelse.

Det sidste trin består i at generere signaturen eller verificere en signatur.

rgb = signatur.sign ();

Hvis vi underskriver en besked, skilt() metode returnerer signaturen.

signatur.verificere (rgbSignature);

Hvis vi verificerer signaturen, der tidligere er genereret fra en meddelelse, skal vi bruge verificere() metode. Det tager som parameter den tidligere genererede signatur og bestemmer, om den stadig er gyldig.

Inden vi afslutter tingene, skal du kigge på Sign.java, den komplette kildekode for et program, der signerer en besked, og Verify.java, den komplette kildekode for et program, der verificerer en besked.

Konklusion

Hvis du bevæber dig med de værktøjer og teknikker, jeg har præsenteret denne måned, er du mere end klar til at sikre dine applikationer. Java Cryptography API gør processen næsten ubesværet. Udgivelse 1.2 af Java Developers Kit lover endnu mere. Bliv hængende.

Næste måned går jeg tilbage til middleware-området. Jeg skal tage lidt RMI, nogle threading og en bunke kode og vise dig, hvordan du bygger din egen meddelelsesorienterede middleware.

Todd Sundsted har skrevet programmer, siden computere blev tilgængelige i praktisk desktop-modeller. Selvom det oprindeligt var interesseret i at oprette distribuerede objektapplikationer i C ++, gik Todd videre til Java-programmeringssproget, da det blev det oplagte valg for den slags ting. Ud over at skrive er Todd præsident for Etcee, der tilbyder uddannelse, mentoring, rådgivning og softwareudviklingstjenester.

Lær mere om dette emne

  • Download den komplette kildekode //www.javaworld.com/jw-01-1999/howto/jw-01-howto.zip
  • Java Security API Oversigt //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
  • Suns Java-sikkerhedsside //java.sun.com/security/index.html
  • RSA's ofte stillede spørgsmål om kryptografi //www.rsa.com/rsalabs/faq/
  • Kryptografisk politik og information //www.crypto.com/
  • Læs Todds tidligere How-To Java-kolonner //www.javaworld.com/topicalindex/jw-ti-howto.html

Denne historie, "I Java we trust" blev oprindeligt udgivet af JavaWorld.