Analys/diskussion av formatet i NGE och NGP
Allmänna kommentarer[redigera]
Det är skillnad på NGE-filer som genereras från importerade P12-filer, och på NGE-filer som genereras från "native NGP"-certifikat.
De förstnämnda verkar mer lika P12-filer och skulle sålunda säkert vara lättare att konvertera till P12. Å andra sidan är det ganska poänglöst, det är ju de sistnämnda vi behöver förstå. Jag har försökt kartlägga bägge i förhoppningen att likheter och skillnader dem emellan kan vara upplysande.
På samma sätt är det skillnad mellan NGP-filer importerade från P12-filer och sådana som skapats från "native NGP"-certifikat.
Slutligen så finns det likheter mellan NGP-filen och motsvarande exporterade NGE-fil.
NGE/NGP verkar i grunden ganska likt P12, då det löser ungefär samma uppgift. Det är precis som P12 en DER-enkodad ASN.1-fil.
Några noteringar: Om samma certifikat exporteras multipla gånger med persadm, så blir det något annorlunda för varje gång, även om det exporteras till samma disk. De ändringar jag kan se verkar dels relatera sig till ändringar av saltet, och dels ändringar i ett proprietärt fält i slutet av filen. Detta verkar ha en inre struktur, men delar av fältet består av kryptodata, så det framgår inte trivialt hur de kodar in t.ex. USB-device-info där.
Filstruktur för NGE[redigera]
Den övergripande filstrukturen för NGE ser ut så här:
SEQUENCE { . INTEGER versionNumber . OCTET STRING ngStr1 . SET wrappedCryptoData . OCTET STRING, encapsulates { . . SEQUENCE { . . . SEQUENCE { . . . . OBJECT IDENTIFIER sha1 (OIW) (1.3.14.3.2.26) . . . . NULL . . . } . . . OCTET STRING shaSum1 . . . OCTET STRING shaSum2 . . . INTEGER shaVal . . } . } }
Beskrivning av variabler:
- versionNumber // Oklar innebörd, version är en gissning. Verkar alltid vara 5.
- ngStr1 // Variabel längd: 16 bytes (P12) eller 96 bytes (native-NGP) + längden av certifikatets namn
- wrappedCryptoData // Se nedan.
- shaSum1 // Fix längd, 20 bytes av sha1-data
- shaSum2 // Fix längd, 20 bytes av sha1-data
- shaVal // Alltid 8192
Wrappade objekt[redigera]
wrappedCryptoData är ett SET av objekt som innehåller någon slags kryptodata. Det finns i princip tre sorters wrappade objekt:
- RSA-wrappers
- PBKDF2-wrappers
- x.509-certifikat-wrappers
RSA-wrappers[redigera]
RSA-wrappers har följande struktur:
SEQUENCE { . OBJECT IDENTIFIER id2WrapCrypto2 (1.2.752.36.4.1.2) . OCTET STRING, encapsulates { . . SEQUENCE { . . . OCTET STRING ngStr3 . . . OCTET STRING, encapsulates { . . . . SEQUENCE { . . . . . SEQUENCE { . . . . . . OBJECT IDENTIFIER rsaEncryption (PKCS #1) (1.2.840.113549.1.1.1) . . . . . . NULL . . . . . } . . . . . BIT STRING, encapsulates { . . . . . . SEQUENCE { . . . . . . . INTEGER . . . . . . . 00 <cryptodata> . . . . . . . INTEGER 65537 . . . . . . } . . . . . } . . . . } . . . } . . } . } }
Beskrivning av variabler:
- ngStr3 // ...?
- cryptodata // Antingen 128 eller 256 bytes med kryptodata för 1024 resp 2048 bitars nyckellängd.
PBKDF2-wrappers[redigera]
PBKDF2-wrappers har följande struktur:
SEQUENCE { . OBJECT IDENTIFIER id2WrapCrypto3 (1.2.752.36.4.1.3) . OCTET STRING, encapsulates { . . SEQUENCE { . . . OBJECT IDENTIFIER id2WrapCrypto2 (1.2.752.36.4.1.2) . . . OCTET STRING, encapsulates { . . . . SEQUENCE { . . . . . OCTET STRING ngStr4 . . . . . OCTET STRING, encapsulates { . . . . . . SEQUENCE { . . . . . . . INTEGER 0 . . . . . . . SEQUENCE { . . . . . . . . OBJECT IDENTIFIER data (PKCS #7) (1.2.840.113549.1.7.1) . . . . . . . . SEQUENCE { . . . . . . . . . OBJECT IDENTIFIER pkcs5PBES2 (PKCS #5 v2.0) (1.2.840.113549.1.5.13) . . . . . . . . . SEQUENCE { . . . . . . . . . . SEQUENCE { . . . . . . . . . . . OBJECT IDENTIFIER pkcs5PBKDF2 (PKCS #5 v2.0) (1.2.840.113549.1.5.12) . . . . . . . . . . . SEQUENCE { . . . . . . . . . . . . SEQUENCE { . . . . . . . . . . . . . OBJECT IDENTIFIER nexusPbkdf2Hash (1.2.752.115.1.2.1.1) . . . . . . . . . . . . . OCTET STRING nexusPbkdf2HashData . . . . . . . . . . . . } . . . . . . . . . . . . INTEGER iterations . . . . . . . . . . . } . . . . . . . . . . } . . . . . . . . . . SEQUENCE { . . . . . . . . . . . OBJECT IDENTIFIER des-EDE3-CBC (RSADSI encryptionAlgorithm) (1.2.840.113549.3.7) . . . . . . . . . . . OCTET STRING desData . . . . . . . . . . } . . . . . . . . . } . . . . . . . . } . . . . . . . . [0] ngPbkdf2Data . . . . . . . } . . . . . . } . . . . . } . . . . } . . . } . . } . } }
Beskrivning av variabler:
- ngStr4 // ... ?
- nexusPbkdf2HashData // 20 bytes med kryptodata till nexusPbkdf2Hash-funktionen ...?
- iterations // alltid 8192.
- desData // 8 bytes av kryptodata ...?
- ngPbkdf2Data // 32 bytes av kryptodata ...?
x509-wrappers[redigera]
x.509-certifikatwrappers har följande struktur:
SEQUENCE { . OBJECT IDENTIFIER id2WrapCrypto2 (1.2.752.36.4.1.2) . OCTET STRING, encapsulates { . . SEQUENCE { . . . OCTET STRING ngStr5 . . . x509certificate . . } . } }
Beskrivning av variabler:
- ngStr5 // ... ?
- x509certificate // Ett x.509-certifikat, i identiskt utförande som motsvarande i P12-filen.
Fördjupande information finns här: x509wrappers i NGE.
Proprietära fält (ngStrX)[redigera]
Det jag kallat ngStr ovan verkar vara ett likartat format på alla ställen, dvs. ngStr1 (header), ngStr3 (rsa), ngStr4 (pbkdf2), ngStr5 (x509).
Det består av ett antal block om 16 bytes, följt av ev. extradata. Denna extradata har i så fall pekats ut av något eller några av blocken.
Det verkar som om blockens typ avgörs av de första åtta byten, och de återstående åtta är någon slags data för den blocktypen. Ev. är det bara de fyra första byten som avgör typen, och byte 5-8 är någon slags parameter till blocktypen.
De olika fälten (ngStr1-5) verkar ha olika krav på vilka block som ingår, och i vilken ordning. Det skiljer sig också mellan NGE som kommer från "native-NGE" eller P12-certifikat. I vissa fall skiljer det sig mellan olika instanser av fälten i filen, t.ex. så har ngStr5 (x.509-certifikaten) olika block beroende på om certifikatet är från en CA-rot eller inte, typ.
Jag har hittills identifierat 18 olika blocktyper (12 om man bara räknar fyra första byten). Några av dem har jag identifierat ändamålet med, men inte de flesta. Här listar jag dem med de namn jag givit dem.
Observera att även om jag skrivit typ "00 00 00 xx, xx = namnets längd" så är det sannolikt att hela ordet avänds för att uttrycka längden. Jag har bara inte hittat exempel på en längd överskridande 255.
TYP1a_NAME:
00 00 00 03 00 00 00 00 00 00 00 xx 00 00 00 yy
- xx = position som namnet startar på
- yy = namnets längd
Pekar ut extradata för "namn". Positionen är en offset från fältets start, typiskt är den 0x80.
TYP1b_NAME:
00 00 00 03 00 00 00 02 00 00 00 xx 00 00 00 00
- xx = position som det tomma "namnet" (dvs extradatan) startar på
Pekar ut start av extradata, om något namn inte finns. Det sista ordet ska antagligen ses som en längd på 0.
TYP2a_NYCKELINFO:
00 00 01 02 00 00 00 00 00 00 00 xx 00 00 00 yy
- xx = position som infon startar på
- yy = infons längd (alltid 4).
Pekar ut extradata, som lagras efter namnet. Den är alltid 4 bytes, och är 00 00 00 00 eller 00 00 00 01.
TYP2b_NYCKELINFO:
00 00 01 02 00 00 00 02 00 00 00 xx 00 00 00 yy
- xx = position som nyckelinfo startar på
- yy = infons längd (alltid 20).
Pekar ut extradata, som alltid är 20 bytes och av formen "KEY" + 16 tecken hexstring + NUL. Den finns bara om TYP1b_NAME har används, dvs den ligger "efter" ett tomt namn.
TYP3_KRYPTOPAYLOAD:
00 00 01 20 00 00 00 00 00 00 00 xx 00 00 00 yy
- xx = position som payload startar på
- yy = payloadets längd (är alltid 0x80).
Finns bara i ngStr4 för "NGE-native"-filer. Pekar ut ett block av "kryptodata" som ligger efter nyckelinfon.
TYP4_ID/COUNT:
80 00 02 01 00 00 00 00 00 00 00 04 00 00 00 xx
- xx = verkar räknas upp för varje fält, eller något ditåt. Är kanske ett slags ID som knyter ihop olika wrappers.
TYP5a_OKÄNT:
00 00 00 02 00 00 00 00 00 00 00 01 00 00 00 01
TYP5b_OKÄNT:
00 00 00 02 00 00 00 06 00 00 00 01 00 00 00 xx
- xx == 00 på native-NGP ngStr3 (rsa), 01 på allt annat.
TYP6a_OKÄNT:
00 00 01 00 00 00 00 00 00 00 00 04 00 00 00 xx
- xx = 00 eller 10.
TYP6b_OKÄNT:
00 00 01 00 00 00 00 06 00 00 00 04 00 00 00 00
TYP7a_OKÄNT:
00 00 00 80 00 00 00 00 00 00 00 04 00 00 00 00
TYP7b_OKÄNT:
00 00 00 80 00 00 00 06 00 00 00 04 00 00 00 00
TYP8a_OKÄNT:
00 00 00 00 00 00 00 00 00 00 00 xx 00 00 00 yy
- xx = typiskt 04 om blocket är först i fältet, och 00 om det används i slutet som padding.
- yy = typiskt 01 eller 04 om blocket är först i fältet, och 00 om det används i slutet som padding.
TYP8b_OKÄNT:
00 00 00 00 00 00 00 06 00 00 00 04 00 00 00 03
TYP9_OKÄNT:
80 00 01 01 00 00 00 00 00 00 00 04 00 00 00 0C
TYP10_OKÄNT:
80 00 01 02 00 00 00 00 00 00 00 04 00 00 00 1E
TYP11_OKÄNT:
80 00 01 06 00 00 00 00 00 00 00 04 00 00 00 01
TYP12_OKÄNT:
80 00 01 07 00 00 00 00 00 00 00 04 00 00 00 04
Återstående arbete[redigera]
Det svåraste arbetet återstår tyvärr fortfarande, och det är att klura ut innebörden av de proprietära fälten. Grunden är lagd för många av fälten, se ovan. Fältet nexusPbkdf2HashData är antagligen tuffast att knäcka, då de säkerligen involverar kryptografiska algoritmer.
Appendix[redigera]
Om keyUsage i x.509-certifikaten[redigera]
Enligt RFC3280 (http://www.faqs.org/rfcs/rfc3280.html) så ska keyUsage (OID 2.5.29.15) tolkas så här:
4.2.1.3 Key Usage [...] KeyUsage ::= BIT STRING { digitalSignature (0), nonRepudiation (1), keyEncipherment (2), dataEncipherment (3), keyAgreement (4), keyCertSign (5), cRLSign (6), encipherOnly (7), decipherOnly (8) }
Det jag sett är följande bitmönster:
- 000000001 - Inloggning
- 000000010 - Underskrift
- 001100000 - CA-certifikatkedja
vilket verkar rimligt.
OID:er som används[redigera]
Se OID i BankID.
Terminologi[redigera]
- NGE = Nexus G... Exported (?)
- NGP = Nexus G... Protected-Store (?)
- P12 = PKCS#12 filformat
- OCSP = PKIX Online Certificate Status Protocol (se RFC3280)
- CRL = Certificate Revocation List