Microsoft CryptoAPI
Olli Voima
Seminaariesitelmä
17.10.1996
JYVÄSKYLÄN YLIOPISTO
MATEMATIIKAN LAITOS
Sisällys
Microsoftin CryptoAPI:n avulla ohjelma voi salata käytössään olevat tiedot sekä muistissa että levyllä ilman, että ohjelmoijalla täytyy olla suurta kryptografian ja eri salausmenetelmien tuntemusta. CryptoAPI:a käyttämällä myöskään siirtotien ei tarvitse olla millään tavalla turvallinen, koska kaikki viestit itse ovat jo salattuja. Tällä hetkellä CryptoAPI on saatavilla ainoastaan 32-bittisiä Windows -sovelluksia varten, mutta Microsoft aikoo julkaista sen muillekin käyttöjärjestelmille.
CryptoAPI sisältää funktioita eri tarkoituksiin:
Microsoftin salausjärjestelmä on kolmiosainen koostuen sovelluksesta, käyttöjärjestelmästä ja CSP:stä (Kuva 1). Sovellukset kommunikoivat käyttöjärjestelmän kanssa CryptoAPIn funktioiden kautta. Käyttöjärjestelmä kommunikoi CSP:n kanssa CryptoSPIn (Cryptographic Service Provider Interface) funktioiden kautta. Sovellukset eivät siis kommunikoi CSP:n kanssa suoraan, vaan kaikki kutsut menevät käyttöjärjestelmän kautta. Windows 95- ja Windows NT 4.0 -käyttöjärjestelmien mukana tulee yksi CSP, Microsoft Base Cryptographic Provider v1.0. Liitteissä A ja B ovat esimerkkiohjelmat CryptoAPI:n käytöstä.
Kuva . Salausmenetelmän arkkitehtuuri.
CSP:t ovat toisistaan riippumattomattomia moduleja, jotka suorittavat tiedon salaamisen. CSP:t implementoivat CryptoAPI:n eri tavoilla. Ne voivat esimerkiksi hoitaa tiedon salaamisen eri algoritmeillä. CSP-modulien pitäisi olla täysin sovelluksista riippumattomia, joten sovellukset voivat käyttää eri CSP:tä tarpeen mukaan. Jotkut sovellukset tosin vaativat tietyn CSP:n. Käytännössä CSP muodostuu vähintään yhdestä DLL (Dynamic-Link Library) -tiedostosta ja allekirjoitustiedostosta. Allekirjoitustiedoston avulla käyttöjärjestelmä varmistaa, että DLL -tiedosto pysyy muuttumattomana. CSP voi tallettaa osan tiedoistaan joko muihin tietokoneen sovelluksien palveluihin RPC (Remote Procedure Call)-kutsujen kautta, tai eri laitteisiin niiden ajurien kautta (kuva 2). Tällainen laite voi olla esimerkiksi älykortti. Näin varmistetaan, että salausmenetelmien kannalta kriittistä tietoa, kuten avaimia, ei pidetä samassa muistiavaruudessa sovelluksen kanssa.
Kuva . CSP tietojen tallennus.
Jokaisella CSP:llä on oma avaintietokanta, missä se säilyttää pysyviä avaimia. Jokaisella avaintietokannalla on yksi tai useampia avainsäiliöitä (key container), joista jokainen sisältää kaikki tietylle käyttäjälle tai CryptoAPI-asiakkaalle kuuluvat avainparit (kuva 3). Avainpari muodostuu julkisesta ja yksityisestä avaimesta. CSP säilyttää kaikki avainsäiliöt ja niiden sisältämät avaimet yhteyksien välillä. Jokaisella käyttäjällä on yleensä kaksi avainparia. Toista paria käytetään yhteyskohtaisten avainten salaamiseen ja toista digitaalisten allekirjoitusten luomiseen. Avainsäiliössä ei ole pakko olla juuri kahta avainparia. Niissä ei välttämättä täydy ole yhtään paria, mutta pareja voi olla säiliössä tarvittaessa myös useampiakin. Yhteyskohtaisia avaimia käytetään tiedon salaamiseen ja purkamiseen. Yhteyskohtaiset avaimet ovat vaihtuvia ja niitä ei säilytetä avaintietokannassa.
Kuva . Avaintietokannan rakenne.
CSP-järjestelmän avulla sovellukset voivat käyttää eri salausmenetelmiä ilman, että niiden itsensä täytyy tietää salausmenetelmistä juuri mitään. Tiedon salaamisen ja purkamisen lisäksi CSP:t voivat autentikoida käyttäjiä. Käyttäjältä voidaan myös tarpeen vaatiessa pyytää hyväksyntää jollekin tietylle operaatiolle. CSP ei ole rajoittunut mihinkään tiettyyn tapaan autentikoida käyttäjä. Käyttäjältä voidaan vaatia esimerkiksi salasana tai hänen henkilöllisyytensä voidaan varmistaa puheentunnistuslaitteella ennen kuin viesti salataan tai allekirjoitetaan. Sovellusten mahdollisuutta käsitellä kryptografisia tietoja on rajoitettu voimakkaasti:
Jokaisella CSP:llä on nimi ja tyyppi. Windows95:n ja Windows NT 4.0:n mukana tuleva CSP on nimeltään Microsoft Base Cryptographic Provider v1.0 ja sen tyyppi on PROV_RSA_FULL. Jokaisen CSP:n nimi on ainutlaatuinen, mutta usealla CSP:llä voi olla sama tyyppi. CSP:t on jaettu perheisiin tyypin mukaan.
Kun sovellus ottaa yhteyden CSP:hen, CryptoAPI-funktiot toimivat CSP:n tyypin mukaisesti. Sovellukset toimivat yleensä vain yhden tietynlaisen CSP:n kanssa, mutta halutessaan ne voivat ottaa yhteyden myös useampaan CSP:hen. CSP:n tyyppi määrittelee mm. seuraavat asiat:
CSP saa tukea muitakin kuin oman tyyppinsä määräämiä algoritmejä. Microsoft Base Cryptographic Provider v1.0 on tyyppiä PROV_RSA_FULL. PROV_RSA_FULL käyttää avainten vaihtamiseen ja digitaalisten allekirjoitusten luomiseen RSA- algoritmiä, jossa avaimen pituus on 512 bittiä. RC2- ja RC4-algoritmeissä avaimen pituus on 40 bittiä. Hajoitteet luodaan MD2-, MD5- ja SHA-algoritmeillä.
Salausmenetelmien turvallisuus riippuu aina avaimista. Ne pitää pystyä pitämään salassa. CryptoAPI käyttää kahdenlaisia avaimia eli yhteyskohtaisia avaimia ja julkisten ja salaisten avainten pareja. Yhteyskohtaisia avaimia käytetään symmetristen salausmenetelmien kanssa. Julkisten ja salaisten avainten pareilla salataan lähinnä yhteyskohtaisia avaimia. Lisäksi julkisten ja salaisten avainten pareja käytetään viestien digitaaliseen allekirjoittamiseen.
Kaikki avaimet säilytetään aina CSP:ssä. CSP:t huolehtivat myös avainten luomisesta, tuhoamisesta ja käyttämisestä. Avaimia voidaan myös tarpeen vaatiessa tulostaa CSP:stä. CSP toteuttaa avainten hallinnan täysin sovelluksista riippumatta. Sovellukset eivät saa tietoonsa yksityiskohtia käytetyistä algoritmeistä. Sovellukset voivat luoda kuinka monta yhteyskohtaista avainta tahansa. CSP ei säilytä yhteyskohtaisia avaimia yhteyksien välillä. Kun yhteyskohtaisia avaimia luodaan, täytyy algoritmin olla jokin CSP:n tukemista symmetrisistä algoritmeistä. CSP säilyttää jokaista käyttäjää kohden yleensä kahta julkisen ja salaisen avaimen paria. Avaintenvaihtoavainparia (key exchange key pair) käytetään yhteyskohtaisten avainten salaamiseen. Näin yhteyskohtaisia avaimia voidaan turvallisesti varastoida ja vaihtaa toisten käyttäjien kanssa. Digitaalisen allekirjoituksen avainparia (digital signature key pair) käytetään digitaalisten allekirjoitusten luomiseen.
Kahden avainparin käyttämiseen on useita syitä. CSP voi esimerkiksi käyttää eri algoritmiä avainten vaihtamiseen ja digitaalisten allekirjoitusten luomiseen. Tämä siksi, että joitakin salaamiseen tai avainten vaihtamiseen tarkoitettuja algoritmejä ei voi käyttää digitaalisten allekirjoitusten luomiseen ja toisinpäin. Saman avaimen käyttäminen tiedon salaamiseen ja sen allekirjoittamiseen voi myös helpottaa salausmenetelmän murtamista.
Avaimet säilytetään yleensä CSP:ssä eikä sovelluksen muistiavaruudessa. Joissakin tapauksissa voi olla kuitenkin tarvetta siirtää avaimet pois CSP:stä. Tällaisia tapauksia ovat:
Avaimet talletetaan avainpaketteihin, kun ne siirretään CSP:n ulkopuolelle. Avainpaketit on ainoa tapa siirtää avain ulos CSP:stä. Avainpaketteihin voidaan tallettaa vain yhteyskohtaisia avaimia tai avainparien julkisia avaimia. Salaisia avaimia ei voi tallettaa avainpaketteihin, eikä niitä saa muillakaan tavoin CSP:n ulkopuolelle.
Avainpaketti muodostuu otsikkotiedoista, joita seuraa itse avain. Sovellukset eivät yleensä tutki avainpakettien sisältöä, vaan CSP hoitaa niiden purkamisen. Turvallisuuden lisäämiseksi ne voidaan salata aiotun vastaanottajan avaintenvaihtoavainparin julkisella avaimella. Avainpaketti voidaan myös allekirjoittaa lähettäjän avaintenvaihtoavainparin salaisella avaimella eheyden varmistamiseksi
Sovellus voi joskus haluta tallettaa yhteyskohtaisen avaimen myöhempää käyttöä varten. Tällöin avain täytyy siirtää ulos CSP:stä. Avaimen siirtäminen tapahtuu seuraavasti:
Turvallisuuden lisäämiseksi avainpaketti voidaan vielä allekirjoittaa käyttäjän avaimenvaihtoavainparin avaimella ennen levylle tallentamista. Allekirjoituksen avulla voidaan varmistaa, että avainpaketti on pysynyt koskemattomana, kun se myöhemmin taas luetaan levyltä.
Mikäli kaksi sovellusta tai käyttäjää haluavat käyttää salattua viestiliikennettä, täytyy heidän ensin vaihtaa julkisia avaimia. Avaintenvaihdon jälkeen voivat käyttäjät lähettää salattuja ja allekirjoitettuja viestejä toisilleen.
Ensimmäiseksi viestin lähettäjän pitää siirtää julkinen avain ulos CSP:stä avainpakettiin. Tämän jälkeen avainpaketti lähetetään vastaanottajalle jollakin turvallisella tavalla. CryptoAPI ei määrää tapaa, jolla avainpaketti siirretään lähettäjältä vastaanottajalle. Saatuaan avainpaketin vastaanottaja voi siirtää sen oman CSP:n sisälle.
Julkisia avaimia sisältävät avainpaketit eivät ole salaisia, joten ei haittaa, vaikka joku kolmas osapuoli saisi avainpaketin haltuunsa. Lähettäjän ja vastaanottajan pitää ainoastaan pystyä varmistamaan, että avainpaketti tulee todella siltä henkilöltä, joka lähettäjä väittää olevansa ja että avainpaketti pysyy siirron aikana muuttumattomana.
Sekä lähettäjän että vastaanottajan tulee tietää viestien salaamiseen käytetty yhteyskohtainen avain. Helpoin tapa välittää yhteyskohtainen avain vastaanottajalle on, että lähettäjä salaa viestin yhteyskohtaisella avaimella, salaa yhteyskohtaisen avaimen vastaanottajan julkisella avaimella ja lähettää sitten salatun viestin ja salatun yhteyskohtaisen avaimen vastaanottajalle. Se tapahtuu seuraavasti (Kuva 4):
Tämän jälkeen samaa yhteyskohtaista avainta voidaan käyttää kaikkien uusien viestien salaamiseen. Avainta voidaan tarpeen vaatiessa vaihtaa lähettämällä uusi avain avainpakettina vastaanottajan avaintenvaihtoavainparin julkisella avaimella salattuna.
Kuva . Salatun viestin ja avainpaketin lähettäminen.
Tällainen tapa avaimen siirtämiseksi lähettäjältä vastaanottajalle on kuitenkin haavoittuvainen yhdelle hyökkäystavalle. Tietoliikennettä kuunteleva henkilö voi saada haltuunsa useita salattuja viestejä ja salatun yhteyskohtaisen avaimen. Myöhemmin kuuntelija voi lähettää jonkin kaappaamistaan viesteistä vastaanottajalle ja vastaanottaja luulee, että viesti tuli alkuperäiseltä lähettäjältä. Ongelman voi osittain ratkaista liittämällä kaikkiin viesteihin joko tietoa niiden lähetysajasta tai jonkinlainen juokseva sarjanumero.
CryptoAPI sisältää myös funktioita tiedon salaamiseen ja purkamiseen. Viestiä salattaessa pitää ensin luoda yhteyskohtainen avain ja määrittää salaamisessa käytettävä algoritmi. Käytettävän algoritmin tulee olla jokin symmetrinen algoritmi, koska CryptoAPI ei salli itse tietojen salaamista epäsymmetrisillä algoritmeillä. Yhteyskohtaisia avaimia epäsymmetrisillä algoritmeillä voidaan kyllä salata. Avaimen lisäksi salaamisessa voidaan määrätä käytettäväksi jotakin tiettyä alustusvektoria. Käytetty yhteyskohtainen avain täytyy tallettaa, mikäli salattu viesti halutaan purkaa jollakin myöhemmällä yhteyskerralla. Avaimen tallettaminen on sovelluksen tehtävä, koska CSP tuhoaa käytetyt yhteyskohtaiset avaimet yhteyden päättyessä. Avain talletetaan siten, että se siirretään ulos CSP:stä ja salataan käyttäjän avaintenvaihtoavainparin julkisella avaimella. Salattu avain voidaan tämän jälkeen tallettaa esimerkiksi koneen levylle.
Talletettu salattu viesti on yleensä tietyssä tarkkaanmäärätyssä muodossa. Muoto riippuu tallettavasta sovelluksesta. Sovelluksen täytyy tallettaa seuraavat tiedot:
Yleensä sovellukset tallettavat kaikki tarvittavat tiedot samaan tiedostoon.
Salauksessa käytetyt yhteyskohtaiset avaimet täytyy ennen salauksen purkamista siirtää takaisin CSP:hen. Salatut avainpaketit siirretään CSP:hen ja CSP purkaa ne käyttäjän avaintenvaihtoavainparin salaisella avaimella. Näin CSP saa tietoonsa alkuperäiset salauksessa käytetyt avaimet. Sovelluksen pitää huolehtia muiden tarvittavien tietojen, kuten alustusvektorien, toimittamisesta CSP:lle. Yleensä ne on talletettu samaan tiedostoon salatun viestin ja käytettyjen avainten kanssa.
Viestin digitaalisella allekirjoituksella voidaan varmistua sen lähettäjästä. CSP:t sisältävät yleensä algoritmit digitaalista allekirjoitusta varten. Digitaalista allekirjoitusta varten luodaan alkuperäisestä viestistä ensin hajoite. Tämän jälkeen hajoite salataan joko käyttäjän digitaalisen allekirjoituksen avainparin tai avaintenvaihtoavainparin salaisella avaimella. Digitaalisen allekirjoituksen avainparin salaista avainta pitäisi käyttää, kun allekirjoitetaan jotakin käyttäjän omaa tietoa. Avaintenvaihtoavainparin salaista avainta tulisi käyttää allekirjoitettaessa tietoa, joka ei ole suoranaisesti käyttäjälle kuuluvaa. Tällaisia ovat esimerkiksi yhteyden aikana käytettävät yhteyskohtaiset avaimet.
Allekirjoitus tarkastetaan luomalla ensin hajoite alkuperäisestä viestistä. Tämän jälkeen allekirjoitus puretaan lähettäjän julkisella avaimella. Mikäli allekirjoitus ja hajoite vastaavat toisiaan, on lähettäjä oikea ja viesti pysynyt siirtotiellä muuttumattomana.
Turvallisen salausalgoritmin kehittäminen ja koodaaminen ei ole helppoa. Moni aluksi turvalliselta näyttävä algoritmi osoittautuu myöhemmin turvattomaksi. CryptoAPIn avulla ohjelmoijan ei tarvitse tietää kryptografiasta juuri mitään, mutta hän pystyy kuitenkin käyttämään erilaisia salausmenetelmiä sovelluksissaan. CryptoAPIn avulla ohjelmista voidaan tehdä turvallisia, mikäli vain itse CryptoAPI on turvallinen. CryptoAPIn turvallisuus riippuu kolmesta asiasta:
CSP:ssä implementoidut algoritmit vaikuttavat suuresti CSP:n turvallisuuteen. Mikäli algoritmit eivät ole kunnollisia, on koko CSP:n turvallisuus kyseenalainen. Tämä vaikuttaa aina vain yhden CSP:n tai tietyn tyyppisten CSP:iden turvallisuuteen. Ohjelmoijan kannattaakin aina ennen CSP:n käyttöä varmistaa, että sen tukemat algoritmit ovat turvallisia.
Tapa, jolla CryptoAPI luo yhteyskohtaiset avaimet ja eri avainparit vaikuttaa kaikkien CSP-modulien turvallisuuteen. Avainten tulisi aina olla täysin satunnaisia. Pienikin epäsatunnaisuus avaimissa heikentää kaikkia salausmenetelmiä. Microsoft ei ole julkistanut vielä tapaa, jolla avaimet luodaan.
Salausalgoritmien implementointi ja avainten valinta vaikuttavat kaikkien sovelluksen salaustarpeiden turvallisuuteen, niin tietoliikenteen turvallisuuteen kuin tietojen säilyttämiseen sovelluksen muistiavaruudessa ja tallettamiseen koneen levylle. Tapa, jolla CSP tallettaa käyttäjien avainparien salaiset avaimet, on merkityksellinen ainoastaan, jos murtautuja pääsee käsittelemään itse tietokoneen levyllä tai muistissa olevia tietoja. Tietoliikenteen turvallisuuteen se ei vaikuta. Salaisten avainten tulee olla talletettuina koneen levyillä mahdollisimman hyvin salattuina, ettei avainten turvallisuus vaarannu, vaikka murtautuja pääsisikin käsiksi avaintietokantoihin. Microsoft ei ole julkistanut tapaa, jolla avaimet on talletettuina levylle.
CryptoAPIn turvallisuuteen kannattaa suhtautua tietyllä varauksella niin kauan, kuin Microsoft ei ole julkistanut, kuinka avaimet luodaan tai talletetaan levyille. Täysin satunnaisten avaimien luominen ei ole mahdotonta ja avaimet voidaan salata jotakin vahvaa salausmenetelmää käyttäen ennen niiden tallettamista levyille. Ennen kuin Microsoft ilmoittaa, kuinka avaimet luodaan ja talletetaan, ei järjestelmän turvallisuudesta ole takeita.
Vahvat salausmenetelmät ovat Yhdysvaltojen lain mukaan ampumatarvikkeita, ja niiden viemiseen maasta tarvitaan hallituksen vientilupa. Tämän vuoksi ohjelmoijat eivät voi aivan vapaasti kirjoittaa omia CSP-moduleita. Valmiissa CSP -moduleissa, joita saa viedä Yhdysvaltojen ulkopuolelle, ei ole mukana vahvoja salausalgoritmejä. Esimerkiksi joidenkin symmetristen algoritmien avainten pituus on rajoitettu 40 bittiin. 40 bittiä pitkä avain on riittämätön minkään vähääkään arvokkaamman tai pitkäaikaisemman tiedon salaamiseen. CSPDK:ta (CSP Development Kit) voi anoa Microsoftilta, joka hakee vientilupaa jokaiselle anojalle erikseen. Lisätietoa anomismenettelystä saa Internetistä Microsoftin WWW-palvelimelta osoitteesta <URL: http://pct.microsoft.com/capi/cspref1.html>.
filecrypt.c salaa tiedoston CryptoAPI:n avulla. Käytettävä CSP on Windows NT 4.0:n mukana tuleva Microsoft Base Cryptographic Provider v1.0. Symmetrisenä algoritminä siinä on esimerkiksi RC2. filecrypt.c käyttää RC2-algoritmiä tiedon salaamiseen. Kohdetiedostossa on ensimmäisenä avainpaketin pituus ja salauksessa käytetty avain avainpakettina. Avainpaketti on salattu käyttäjän avainmenvaihtoavainparin julkisella avaimella. Avainpaketin pituuden ja avainpaketin jälkeen tulee itse lähdetiedosto salattuna. filecrypt.c toimii ainakin Windows NT 4.0 Workstation -käyttöjärjestelmässä. filecrypt.c:llä salatut tiedostot puretaan filedecrypt.c:llä (ks Liite B).
/********************************************************
* *
* filecrypt.c - Ohjelma tiedostojen salaamista varten. * * * * Tekijä: Olli Voima <voima@math.jyu.fi> * * Pvm: 18.9.1996 * * * * Käyttö: filecrypt -i<inputfile> -o<outputfile> *
* *
********************************************************/
#define _WIN32_WINNT 0x400
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#define LOHKO 160
#define PUSKURI (LOHKO+16)
int crypt(char *ifile, char *ofile){
FILE *input, *output;
HCRYPTPROV hProv; // kahva CSP:hen
HCRYPTKEY hAvain=0; // kahva salausavaimeen
HCRYPTKEY hVaihtoavain=0; /* kahva avaimenvaihtoavain-parin julkiseen avaimeen */
BYTE Puskuri[PUSKURI];
DWORD Maara;
BYTE *Avainpaketti = NULL;
DWORD Pakettipituus;
int eof=0;
// Avataan lähdetiedosto
if (!(input = fopen(ifile, "rb"))){
fprintf(stderr, "Tiedostoa %s ei voi avata.\n", ifile);
goto loppu;
}
// Avataan tulostustiedosto
if (!(output = fopen(ofile, "wb"))){
fprintf(stderr, "Tiedostoa %s ei voi avata.\n", ofile);
goto loppu;
}
// Kahva CSP:hen. Käytetään oletus-CSP:tä
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)){
/* Mikäli käyttäjällä ei ole Key Containeria, hänelle luodaan sellainen. */
if (GetLastError() == NTE_BAD_KEYSET){
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)){
printf("Virhe %x funktiossa CryptAcquireContext.\n", GetLastError());
goto loppu;
}
} else {
printf("Virhe %x funktiossa CryptAcquireContext.\n", GetLastError());
goto loppu;
}
}
// Kahva avaimenvaihtoavaimeen
if (!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hVaihtoavain)){
if (GetLastError() == NTE_NO_KEY) {
/* Mikäli käyttäjällä ei ole avaimenvaihtoavainparia, hänelle luodaan sellainen */
if (!CryptGenKey(hProv, AT_KEYEXCHANGE, 0 , &hVaihtoavain)){
printf("Wirhe %x funktioissa CryptGenKey.\n", GetLastError());
goto loppu;
}
} else {
printf("Virhe %x funktiossa CryptGetUserKey.\n", GetLastError());
goto loppu;
}
}
/* Luodaan yhteyskohtainen avain. Algoritminä käytetään RC2:a. */
if (!CryptGenKey(hProv, CALG_RC2, CRYPT_EXPORTABLE, &hAvain)){
printf("Virhe %x funktioissa CryptGenKey.\n", GetLastError());
goto loppu;
}
/* Määritetään avainpaketin koko ja varataan sille muistia. */
if (!CryptExportKey(hAvain, hVaihtoavain, SIMPLEBLOB, 0, NULL, &Pakettipituus)){
printf("Virhe %x määritettäessä avainpaketin kokoa.\n", GetLastError());
goto loppu;
}
if (!(Avainpaketti=malloc(Pakettipituus))){
printf("Muistin varaaminen epäonnistui.\n");
goto loppu;
}
// Siirretään avain avainpakettiin.
if (!CryptExportKey(hAvain, hVaihtoavain, SIMPLEBLOB, 0, Avainpaketti, &Pakettipituus)){
printf("Virhe %x funktioissa CryptExportKey.\n", GetLastError());
free(Avainpaketti);
goto loppu;
}
// Kirjoitetaan avainpaketin koko tiedostoon.
fwrite(&Pakettipituus, sizeof(DWORD), 1, output);
if (ferror(output)){
printf("Virhe kirjoitettaessa avainpaketin kokoa.\n");
free(Avainpaketti);
goto loppu;
}
//Kirjoitetaan avainpaketti tiedostoon.
fwrite(Avainpaketti, 1, Pakettipituus, output);
if (ferror(output)){
printf("Virhe kirjoitettaessa avainpaketin kokoa.\n");
free(Avainpaketti);
goto loppu;
}
// Vapautetaan muistia.
free(Avainpaketti);
// Salataan lähdetiodosto kohdetiedostoon.
do{
// Luetaan PUSKURIn verran tavuja lähdetiedostosta.
Maara = fread(Puskuri, 1, PUSKURI, input);
if (ferror(input)){
printf("Virhe luettaessa tietoja.\n");
goto loppu;
}
eof=feof(input);
// Salataan luetut tavut
if (!CryptEncrypt(hAvain, 0, eof, 0, Puskuri, &Maara, PUSKURI)){
printf("Virhe %x funktioissa CryptEncrypt.\n", GetLastError());
goto loppu;
}
// Kirjoitetaan data kohdetiedostoon
fwrite(Puskuri, 1, Maara, output);
if (ferror(output)){
printf("Virhe kirjoitettaessa tietoja.\n");
goto loppu;
}
} while (!feof(input));
loppu:
// Tuhotaan yhteyskohtainen avain.
if (hAvain !=0) CryptDestroyKey(hAvain);
// Tuhotaan vaihtoavain
if (hVaihtoavain !=0) CryptDestroyKey(hVaihtoavain);
// Vapautetaan CSP:n kahva
if (hProv !=0) CryptReleaseContext(hProv, 0);
// Suljetaan lähdetiedosto
if (input != NULL) fclose(input);
// Suljetaan kohdetiedosto
if (output != NULL) fclose(output);
return 0;
}
int kaytto_ohje(void){
fprintf(stderr, "Usage: filecrypt -i<inputfile> -o<outputfile>\n");
return 0;
}
char *hae_optio(int argc, char *argv[], int optio){
int i;
for (i=1; i<argc; i++){
if (argv[i][0]!='-'){
kaytto_ohje();
exit(1);
}
if (argv[i][1]==optio) return (argv[i]+2);
}
}
int main(int argc, char *argv[]){
char *ifile=NULL, *ofile=NULL;
if (argc!=3){
kaytto_ohje();
return 1;
}
if (strlen(ofile=strdup(hae_optio(argc, argv, 'o')))==0){
kaytto_ohje();
return 1;
}
if (strlen(ifile=strdup(hae_optio(argc, argv, 'i')))==0){
kaytto_ohje();
return 1;
}
crypt(ifile, ofile);
return 0;
}
filedecrypt.c purkaa filecrypt.c:llä salatut tiedostot. filedecrypt.c käyttää CryptoAPIa ja Microsoft Base Cryptographic Provider v1.0 CSP:tä. Ensin filedecrypt.c lukee salatusta lähdetiedostosta salausavaimen sisältävän avainpaketin ja siirtää sen CSP:hen. Tämän jälkeen filedecrypt.c lukee lähdetiedostosta itse salatun tiedon ja kirjoittaa sen selväkielisenä kohdetiedostoon. filedecrypt.c toimii ainakin Windows NT 4.0 Workstation -käyttöjärjestelmässä.
/********************************************************
* *
* filedecrypt.c - Ohjelma tiedostojen purkamista varten.*
* *
* Tekijä: Olli Voima <voima@math.jyu.fi> *
* Pvm: 19.9.1996 *
* *
* Käyttö: filedecrypt -i<inputfile> -o<outputfile> *
* *
********************************************************/
#define _WIN32_WINNT 0x400
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#define LOHKO 160
#define PUSKURI (LOHKO+16)
int crypt(char *ifile, char *ofile){
FILE *input, *output;
HCRYPTPROV hProv; // kahva CSP:hen
HCRYPTKEY hAvain=0; // kahva salausavaimeen
BYTE Puskuri[PUSKURI];
DWORD Maara;
BYTE *Avainpaketti = NULL;
DWORD Pakettipituus;
int eof=0;
// Avataan lähdetiedosto
if (!(input = fopen(ifile, "rb"))){
fprintf(stderr, "Tiedostoa %s ei voi avata.\n", ifile);
goto loppu;
}
// Avataan tulostustiedosto
if (!(output = fopen(ofile, "wb"))){
fprintf(stderr, "Tiedostoa %s ei voi avata.\n", ofile);
goto loppu;
}
// Kahva CSP:hen. Käytetään oletus-CSP:tä
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)){
/*
Mikäli käyttäjällä ei ole Key Containeria, hänelle luodaan sellainen. Tällöin hänellä ei ole kyllä avaimenvaihtoavainpariakaan, joten hän ei voi siirtää avainpakettia CSP:hen ja purkaa sitä ja siten purkaa
salattua tiedostoa.
*/
if (GetLastError() == NTE_BAD_KEYSET){
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)){
printf("Virhe %x funktiossa CryptAcquireContext.\n", GetLastError());
goto loppu;
}
} else {
printf("Virhe %x funktiossa CryptAcquireContext.\n", GetLastError());
goto loppu;
}
}
/* Luetaan avainpaketin pituus tiedostosta ja varataan avainpaketille muistia. */
fread(&Pakettipituus, sizeof(DWORD), 1, input);
if (ferror(input) || feof(input)){
printf("Virhe luettaessa avainpaketin pituutta.\n");
goto loppu;
}
if ((Avainpaketti=malloc(Pakettipituus)) == NULL){
printf("Muistin varaaminen ei onnistu.\n");
goto loppu;
}
// Luetaan avainpaketti tiedostosta
fread(Avainpaketti, 1, Pakettipituus, input);
if (ferror(input) || feof(input)){
printf("Virhe luettaessa avainpakettia.\n");
goto loppu;
}
// Siirretään avainpaketti CSP:hen
if (!CryptImportKey(hProv, Avainpaketti, Pakettipituus, 0, 0, &hAvain)){
printf("Virhe %x funktiossa CryptImportKey.\n", GetLastError());
goto loppu;
}
/* Puretaan lähdetiedosto ja kirjoitetaan se kohdetiedostoon */
do {
// Luetaan PAKETTI tavua lähdetiedostosta
Maara = fread(Puskuri, 1, PUSKURI, input);
if (ferror(input)){
printf("Virhe luettaessa tiedostoa.\n");
goto loppu;
}
eof=feof(input);
// Puretaan data.
if(!CryptDecrypt(hAvain, 0, eof, 0, Puskuri, &Maara)){
printf("Virhe %x funktiossa CryptDecrypt.\n", GetLastError());
goto loppu;
}
// Kirjoitetaan data kohdetiedostoon
fwrite(Puskuri, 1, Maara, output);
if (ferror(output)){
printf("Virhe kirjoitettaessa tietoja.\n");
goto loppu;
}
} while(!feof(input));
loppu:
// Tuhotaan yhteyskohtainen avain.
if (hAvain !=0) CryptDestroyKey(hAvain);
// Tuhotaan vaihtoavain
if (Avainpaketti) free(Avainpaketti);
// Vapautetaan CSP:n kahva
if (hProv !=0) CryptReleaseContext(hProv, 0);
// Suljetaan lähdetiedosto
if (ifile != NULL) fclose(input);
// Suljetaan kohdetiedosto
if (ofile != NULL) fclose(output);
return 0;
}
int kaytto_ohje(void){
fprintf(stderr, "Usage: filecrypt -i<inputfile> -o<outputfile>\n");
return 0;
}
char *hae_optio(int argc, char *argv[], int optio){
int i;
for (i=1; i<argc; i++){
if (argv[i][0]!='-'){
kaytto_ohje();
exit(1);
}
if (argv[i][1]==optio) return (argv[i]+2);
}
}
int main(int argc, char *argv[]){
char *ifile=NULL, *ofile=NULL;
if (argc!=3){
kaytto_ohje();
return 1;
}
if (strlen(ofile=strdup(hae_optio(argc, argv, 'o')))==0){
kaytto_ohje();
return 1;
}
if (strlen(ifile=strdup(hae_optio(argc, argv, 'i')))==0){
kaytto_ohje();
return 1;
}
crypt(ifile, ofile);
return 0;
}