You can use CryptEncodeObjectEx
to decode the most cryptographic objects inclusive OIDs.
In case of OIDs the encoding and decoding is pity simple so you can do this manually.
Two first numbers 1.2 will be encoded with a little special way. For example x.y will be encoded as 40*x+y. In case of 1.2 we have 40*1+2 = 42 or 0x2a.
All next characters will be interpret as 7-bit numbers where the highest bit (bit number 7 if we start with 0) is 0 is the byte is the last one and 1 if the bit is not the last one. For example, 840 is 0x348. To encode this we should use 2 bytes in the last one will be saved 0x48. In the previous one should be saved 0x3 with an additional bit from 0x48 (because of 7 bit codding and not 8 bit encoding). So we should encode 0x3*2=0x6 in the first byte. Because 0x6 will be the not the last byte in the encoding of the integer (0x48 byte will follow) we should add 0x80 to the encoded value. So we receive 0x80+0x6=0x86. So 840 will be encoded as 0x86 and 0x48.
In the same way 10040 is 0x2738. The last byte is 0x38 and the first one is 0x27*2 (because of 7 bit codding): 0x27*2=0x4e. Because 0x4e is not the last byte we should add 0x80 to the encoded value: 0x4e+0x80=0xce. So 10040 will be encoded as two bytes 0xce and 0x38.
The 4 and 1 will be encoded just as 0x04 and 0x01.
So 1.2.840.10040.4.1 should be encoded as 2a 86 48 ce 38 04 01 like you already knows.
All this you can read in 8.19 of ITU-T X.690 (ISO/IEC 8825-1)
UPDATED based on the comment: Something is wrong in you encoding/decoding program. The OID "1.2.840.113549.1.1.1" will be do represented as 2A 86 48 86 F7 0D 01 01 01
and not as 2a 86 48 83 f6 8d 01 01 01
like you wrote. To verify this you can use following small C program:
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment (lib, "Crypt32.lib")
void PrintOffset(DWORD dwMargineOffset)
{
while (dwMargineOffset--)
_tprintf (TEXT(" "));
}
void HexDump (PBYTE pData, DWORD dwDataLength)
{
while (dwDataLength--) {
_tprintf (TEXT("%02X"), *pData++);
if (dwDataLength) _tprintf (TEXT(" "));
}
}
void DumpOID (DWORD dwMargineOffset, PBYTE pData, DWORD dwDataLength)
{
PCCRYPT_OID_INFO pCryptOidInfo;
DWORD dw, i;
char szOID[256];
// i
// first byte is encoded as x.y 40*x+y = 43 = 0x2B
//
//_tprintf(TEXT("%d.%d"), *pData/40, *pData%40);
i = wsprintfA (szOID, "%d.%d", *pData/40, *pData%40);
dwDataLength--;
pData++;
while (dwDataLength--) {
if (*pData & 0x80) {
dw = 0;
#pragma warning(disable:4127)
while (TRUE) {
#pragma warning(default:4127)
dw <<= 7; // *128
dw += (*pData & 0x7F);
if (*pData++ & 0x80)
dwDataLength--;
else
break;
}
//_tprintf(TEXT(".%d"), dw);
i += wsprintfA (szOID+i, ".%d", dw);
}
else
//_tprintf(TEXT(".%d"), *pData++);
i += wsprintfA (szOID+i, ".%d", *pData++);
}
PrintOffset(dwMargineOffset);
_tprintf (TEXT("%hs"), szOID);
// try find OID in the list of known IODs
pCryptOidInfo = CryptFindOIDInfo (CRYPT_OID_INFO_OID_KEY, szOID, 0);
if (pCryptOidInfo)
_tprintf (TEXT(" (\"%ls\")"), pCryptOidInfo->pwszName);
else
_tprintf (TEXT(" (Unknown OID)"));
}
int main()
{
BOOL bIsSuccess;
DWORD cbEncoded = 0;
PBYTE pbyData = NULL;
BYTE byData[] = {0x2a, 0x86, 0x48, 0x83, 0xf6, 0x8d, 0x01, 0x01, 0x01};
BYTE byData2[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01};
LPSTR pszOid = "1.2.840.113549.1.1.1";
DumpOID (0, byData, sizeof(byData));
_tprintf (TEXT("\n"));
DumpOID (0, byData2, sizeof(byData2));
_tprintf (TEXT("\n"));
bIsSuccess = CryptEncodeObjectEx (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
X509_OBJECT_IDENTIFIER,
(const void *)&pszOid,
CRYPT_ENCODE_ALLOC_FLAG,
NULL,
&pbyData,
&cbEncoded);
if (bIsSuccess) {
HexDump (pbyData, cbEncoded);
_tprintf (TEXT("\n"));
pbyData = (PBYTE) LocalFree (pbyData);
}
return 0;
}
The program produce the following output 06 09 2A 86 48 86 F7 0D 01 01 01
where the first byte 0x06 of the BER encoding mean OID data type, the next byte 0x09 means the data length and next 9 bytes 2A 86 48 86 F7 0D 01 01 01
are the encoded OID 1.2.840.113549.1.1.1.
The full output of the program is
1.2.840.8226433.1.1 (Unknown OID)
1.2.840.113549.1.1.1 ("RSA")
06 09 2A 86 48 86 F7 0D 01 01 01