Za buduću referencu, evo popisa stvari kod kojih se može lako pogriješiti te nekih napomena:
Kanonizirani oblik za "PoslovniProstorZahtjev" izgleda ovako
Konačni zahtjev izgleda ovako
Na mjesto prve tri točkice ide signatura dobivena za kanonizirani "SignedInfo" dok se certifikat prilaže pod "X509Certificate."
Također izgleda kako polja "X509IssuerSerial", "X509IssuerName", "X509SerialNumber", "X509SubjectName" nisu neophodna, što je i logično jer se svi ti podaci nalaze u certifikatu. Može li izostavljanje tih polja uzrokovati probleme kasnije, to tek treba vidjeti :D
- GUID mora biti lower case. Funkcija StringFromGUID2 daje upper case string.
- Kako je već gore navedeno, SHA1 hash byte order za kanonizirani "PoslovniProstorZahtjev" kakvog daje CryptCreateHash je ispravan i samo ga je potrebno prebaciti u base64, direktno iz binarnog formata. Vidim da ljudi ponegdje binarni SHA1 (20 byte) prebace prvo u heksadecimalni string (40 byte) i tek onda u base64 (56 byte,) dok konačni base64 string treba biti duljine 28 byte. Za prebacivanje u base64 može se koristiti i CryptoAPI funkcija CryptBinaryToString. Ja sam, kao mazohista, išao ručno. Pripazite se nekih funcija koje bi mogle dodavati previše paddinga, u obliku znaka jednakosti (=). U nikojem slučaju broj tih znakova ne smije prelaziti dva, dok sam primijetio da neke funkcije znaju stavljati i tri ili čak četiri.
- Byte order potpisa kanoniziranog "SignedInfo" dobivenog od CryptSignHash ne odgovara i njega je potrebno pretvoriti u big endian prije prebacivanja u base64. Ukoliko netko sumnja, za prebaciti byte order sve što je potrebno je prvom i zadnjem bajtu u nizu zamijeniti mjesta, potom zadnjem i pradzadnjem i tako dalje.
- PFXImportCertStore s kojim bi se trebalo moći direktno koristiti fiskal1.pfx certifikat nisam uspio dobiti očekivane rezultate. Konačno sam se odlučio importirati certifikat u certificate store i potom pronaći certifikat preko njegovog SHA1 hasha i funkcije CertFindCertificateInStore. SHA1 certifikata se može izračunati iz certifikata ukoliko ga eksportirate u binarni DER format ili se može naći preko windows dijaloga ili u certmgr.msc nakon importiranja DER certifikata.
- Format datuma može biti dd-mm-yyyy ili dd.mm.yyyy.
- OIB koji se šalje u sklopu zahtjeva mora odgovarati OIBu iz certifikata. To je samo po sebi očito, ali eto može se i tu lako pogriješiti :P
- Certifikat koji se dodaje pod "X509Certificate" je base64 DER formata javnog certifikata, kakvog je moguće eksportirati iz certmgr.msc. Možete ga i proračunski odrediti iz binarnog DER formata.
- Za sve base64 stringove (signatura, certifikat) nebitno je hoće li base64 string biti "u jednom komadu" ili će nakon nekog broja znakova biti ubačen "LF" (0x0A) znak. To mi se činilo očito, ali u napadu bunila morao sam provjeriti.
-
Kanoniziranom XMLu nije mjesto u konačnom zahtjevu, on se koristi samo za izračun hasha i signature. Ovo je isto očito, ali tek nakon što se pročita RFC :D
Kanonizirani oblik za "PoslovniProstorZahtjev" izgleda ovako
<PoslovniProstorZahtjev xmlns="http://www.apis-it.hr/fin/2012/types/f73" xmlns:sp="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Id="PoslovniProstorZahtjev"><Zaglavlje><IdPoruke>47d016ff-0f0d-49b8-a212-4c44ca8152e6</IdPoruke><DatumVrijeme>15.03.2013T08:33:19</DatumVrijeme></Zaglavlje><PoslovniProstor><Oib>86545165826</Oib><OznPoslProstora>TERA</OznPoslProstora><AdresniPodatak><Adresa><Ulica>Laginjina</Ulica><KucniBroj>44</KucniBroj><KucniBrojDodatak>b</KucniBrojDodatak><BrojPoste>52100</BrojPoste><Naselje>Pula</Naselje><Opcina>Pula</Opcina></Adresa></AdresniPodatak><RadnoVrijeme>radnim danom od 8 do 22, subotom 10-24, nedjeljom zatvoreno</RadnoVrijeme><DatumPocetkaPrimjene>01.07.2013</DatumPocetkaPrimjene><SpecNamj>86545165826</SpecNamj></PoslovniProstor></PoslovniProstorZahtjev>
SHA1 hash u base64 formatu za gornji XML iznosixcS790cSwmcPRUusSDnWxiVqdKg=
Kanonizirani oblik "SignedInfo" će izgledati ovako<ds:SignedInfo xmlns="http://www.apis-it.hr/fin/2012/types/f73" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:sp="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod><ds:Reference URI="#PoslovniProstorZahtjev"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>xcS790cSwmcPRUusSDnWxiVqdKg=</ds:DigestValue></ds:Reference></ds:SignedInfo>
Signatura za gornji XML ovisi o certifikatu, tako da ovdje nema smisla davati primjer.Konačni zahtjev izgleda ovako
<?xml version="1.0" encoding="UTF-8"?><sp:Envelope xmlns:sp="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><sp:Body><PoslovniProstorZahtjev xmlns="http://www.apis-it.hr/fin/2012/types/f73" Id="PoslovniProstorZahtjev"><Zaglavlje><IdPoruke>47d016ff-0f0d-49b8-a212-4c44ca8152e6</IdPoruke><DatumVrijeme>15.03.2013T08:33:19</DatumVrijeme></Zaglavlje><PoslovniProstor><Oib>86545165826</Oib><OznPoslProstora>TERA</OznPoslProstora><AdresniPodatak><Adresa><Ulica>Laginjina</Ulica><KucniBroj>44</KucniBroj><KucniBrojDodatak>b</KucniBrojDodatak><BrojPoste>52100</BrojPoste><Naselje>Pula</Naselje><Opcina>Pula</Opcina></Adresa></AdresniPodatak><RadnoVrijeme>radnim danom od 8 do 22, subotom 10-24, nedjeljom zatvoreno</RadnoVrijeme><DatumPocetkaPrimjene>01.07.2013</DatumPocetkaPrimjene><SpecNamj>86545165826</SpecNamj></PoslovniProstor><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:Reference URI="#PoslovniProstorZahtjev"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>xcS790cSwmcPRUusSDnWxiVqdKg=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>...</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>...</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature></PoslovniProstorZahtjev></sp:Body></sp:Envelope>
Ja sam se odlučio za XML bez whitespacea. U čitljivom formatu to bi izgledalo ovako<?xml version="1.0" encoding="utf-8"?>
<sp:Envelope xmlns:sp="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<sp:Body>
<PoslovniProstorZahtjev xmlns="http://www.apis-it.hr/fin/2012/types/f73" Id="PoslovniProstorZahtjev">
<Zaglavlje>
<IdPoruke>47d016ff-0f0d-49b8-a212-4c44ca8152e6</IdPoruke>
<DatumVrijeme>15.03.2013T08:33:19</DatumVrijeme>
</Zaglavlje>
<PoslovniProstor>
<Oib>86545165826</Oib>
<OznPoslProstora>TERA</OznPoslProstora>
<AdresniPodatak>
<Adresa>
<Ulica>Laginjina</Ulica>
<KucniBroj>44</KucniBroj>
<KucniBrojDodatak>b</KucniBrojDodatak>
<BrojPoste>52100</BrojPoste>
<Naselje>Pula</Naselje>
<Opcina>Pula</Opcina>
</Adresa>
</AdresniPodatak>
<RadnoVrijeme>radnim danom od 8 do 22, subotom 10-24, nedjeljom zatvoreno</RadnoVrijeme>
<DatumPocetkaPrimjene>01.07.2013</DatumPocetkaPrimjene>
<SpecNamj>86545165826</SpecNamj>
</PoslovniProstor>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<ds:Reference URI="#PoslovniProstorZahtjev">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ds:DigestValue>xcS790cSwmcPRUusSDnWxiVqdKg=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>...</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
</PoslovniProstorZahtjev>
</sp:Body>
</sp:Envelope>
samo treba zapamtiti da whitespace mijenja kanonizirani oblik i samim time hash neće biti isti. Zato ovaj gornji čitljiviji oblik nije "kompatibilian" s ostatkom primjera.Na mjesto prve tri točkice ide signatura dobivena za kanonizirani "SignedInfo" dok se certifikat prilaže pod "X509Certificate."
Također izgleda kako polja "X509IssuerSerial", "X509IssuerName", "X509SerialNumber", "X509SubjectName" nisu neophodna, što je i logično jer se svi ti podaci nalaze u certifikatu. Može li izostavljanje tih polja uzrokovati probleme kasnije, to tek treba vidjeti :D