/**
* FFI wrapper over Windows CryptoApi (crypt32.dll)
* See `\Samples\UpdateCertificateList\updateCRL.js` for usage example.
*
* @module cryptApi
* @author xmax
*/
//TODO - rewrite. remove keyword const! remove function cryptApi. Use module.expors to expose API
var ffi = require('ffi');
var winApi = require('winApi');
var cryptApi = {};
var CRYPT32_DLL = 'crypt32.dll';
var cst;
//*********** Const ************
cryptApi.const = cst = {
CERT_QUERY_OBJECT_FILE: 0x00000001,
CERT_QUERY_OBJECT_BLOB: 0x00000002,
//encoded single certificate
CERT_QUERY_CONTENT_CERT: 1,
//encoded single CTL
CERT_QUERY_CONTENT_CTL: 2,
//encoded single CRL
CERT_QUERY_CONTENT_CRL: 3,
//serialized store
CERT_QUERY_CONTENT_SERIALIZED_STORE: 4,
//serialized single certificate
CERT_QUERY_CONTENT_SERIALIZED_CERT: 5,
//serialized single CTL
CERT_QUERY_CONTENT_SERIALIZED_CTL: 6,
//serialized single CRL
CERT_QUERY_CONTENT_SERIALIZED_CRL: 7,
//a PKCS#7 signed message
CERT_QUERY_CONTENT_PKCS7_SIGNED: 8,
//a PKCS#7 message, such as enveloped message. But it is not a signed message,
CERT_QUERY_CONTENT_PKCS7_UNSIGNED: 9,
//a PKCS7 signed message embedded in a file
CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED: 10,
//an encoded PKCS#10
CERT_QUERY_CONTENT_PKCS10: 11,
//an encoded PKX BLOB
CERT_QUERY_CONTENT_PFX: 12,
//-------------------------------------------------------------------------
//dwFormatType for CryptQueryObject
//-------------------------------------------------------------------------
//the content is in binary format
CERT_QUERY_FORMAT_BINARY: 1,
//the content is base64 encoded
CERT_QUERY_FORMAT_BASE64_ENCODED: 2,
CERT_SIMPLE_NAME_STR: 1,
CERT_OID_NAME_STR: 2,
CERT_X500_NAME_STR: 3,
//+-------------------------------------------------------------------------
// Certificate name string type flags OR'ed with the above types
//--------------------------------------------------------------------------
CERT_NAME_STR_SEMICOLON_FLAG: 0x40000000,
CERT_NAME_STR_NO_PLUS_FLAG: 0x20000000,
CERT_NAME_STR_NO_QUOTING_FLAG: 0x10000000,
CERT_NAME_STR_CRLF_FLAG: 0x08000000,
CERT_NAME_STR_COMMA_FLAG: 0x04000000
};
/*jshint bitwise: false*/
//-------------------------------------------------------------------------
//dwExpectedConentTypeFlags for CryptQueryObject
//-------------------------------------------------------------------------
//encoded single certificate
cst.CERT_QUERY_CONTENT_FLAG_CERT = 1 << cst.CERT_QUERY_CONTENT_CERT;
//encoded single CTL
cst.CERT_QUERY_CONTENT_FLAG_CTL = 1 << cst.CERT_QUERY_CONTENT_CTL;
//encoded single CRL
cst.CERT_QUERY_CONTENT_FLAG_CRL = 1 << cst.CERT_QUERY_CONTENT_CRL;
//serialized store
cst.CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE = 1 << cst.CERT_QUERY_CONTENT_SERIALIZED_STORE;
//serialized single certificate
cst.CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT = 1 << cst.CERT_QUERY_CONTENT_SERIALIZED_CERT;
//serialized single CTL
cst.CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL = 1 << cst.CERT_QUERY_CONTENT_SERIALIZED_CTL;
//serialized single CRL
cst.CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL = 1 << cst.CERT_QUERY_CONTENT_SERIALIZED_CRL;
//an encoded PKCS#7 signed message
cst.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED = 1 << cst.CERT_QUERY_CONTENT_PKCS7_SIGNED;
//an encoded PKCS#7 message. But it is not a signed message
cst.CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED = 1 << cst.CERT_QUERY_CONTENT_PKCS7_UNSIGNED;
//the content includes an embedded PKCS7 signed message
cst.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED = 1 << cst.CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED;
//an encoded PKCS#10
cst.CERT_QUERY_CONTENT_FLAG_PKCS10 = 1 << cst.CERT_QUERY_CONTENT_PKCS10;
//an encoded PFX BLOB
cst.CERT_QUERY_CONTENT_FLAG_PFX = 1 << cst.CERT_QUERY_CONTENT_PFX;
//content can be any type
cst.CERT_QUERY_CONTENT_FLAG_ALL = cst.CERT_QUERY_CONTENT_FLAG_CERT |
cst.CERT_QUERY_CONTENT_FLAG_CTL |
cst.CERT_QUERY_CONTENT_FLAG_CRL |
cst.CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
cst.CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT |
cst.CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL |
cst.CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL |
cst.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
cst.CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
cst.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
cst.CERT_QUERY_CONTENT_FLAG_PKCS10 |
cst.CERT_QUERY_CONTENT_FLAG_PFX;
//-------------------------------------------------------------------------
//dwExpectedFormatTypeFlags for CryptQueryObject
//-------------------------------------------------------------------------
//the content is in binary format
cst.CERT_QUERY_FORMAT_FLAG_BINARY = 1 << cst.CERT_QUERY_FORMAT_BINARY;
//the content is base64 encoded
cst.CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = 1 << cst.CERT_QUERY_FORMAT_BASE64_ENCODED;
//the content can be of any format
cst.CERT_QUERY_FORMAT_FLAG_ALL = cst.CERT_QUERY_FORMAT_FLAG_BINARY | cst.CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED;
/*jshint bitwise: true*/
//*********** TYPE **************
var refT = ffi.types.refType,
DWORD = ffi.types.uint32,
PBYTE = ffi.types.puint8,
LPSTR = ffi.types.ansiString,
BOOL = ffi.types.uint8,
Pointer = new refT(ffi.types.void),
PPointer = new refT(Pointer),
PVOID = new refT(ffi.types.void),
PDWORD = new refT(DWORD),
PWideString = new refT(ffi.types.wideString),
LPDWORD = PDWORD;
cryptApi.types = {
DWORD: DWORD,
PBYTE: PBYTE,
LPSTR: LPSTR,
BOOL: BOOL,
Pointer: Pointer,
PPointer: PPointer,
PVOID: PVOID,
PWideString: PWideString,
LPDWORD: LPDWORD
};
// structure definition
var CRYPTOAPI_BLOB = ffi.Struct({
cbData : DWORD,
pbData : PBYTE
}),
CRYPT_OBJID_BLOB = CRYPTOAPI_BLOB,
CERT_NAME_BLOB = CRYPTOAPI_BLOB,
PCERT_NAME_BLOB = new refT(CERT_NAME_BLOB),
CRYPT_INTEGER_BLOB = CRYPTOAPI_BLOB;
var HCERTSTORE = PVOID,
HCRYPTMSG = Pointer,
CRYPT_ALGORITHM_IDENTIFIER = ffi.Struct({
pszObjId : LPSTR,
Parameters : CRYPT_OBJID_BLOB
});
var FILETIME = ffi.Struct({
dwLowDateTime: DWORD,
dwHighDateTime: DWORD
});
var CERT_EXTENSION = ffi.Struct({
pszObjId :LPSTR,
fCritical :BOOL,
Value :CRYPT_OBJID_BLOB
}),
PCERT_EXTENSION = new refT(CERT_EXTENSION);
var CRL_ENTRY = ffi.Struct({
SerialNumber: CRYPT_INTEGER_BLOB,
RevocationDate: FILETIME, //TFILETIME;
cExtension: DWORD,
rgExtension : PCERT_EXTENSION
}),
PCRL_ENTRY = new refT(CRL_ENTRY);
var CRL_INFO = ffi.Struct({
dwVersion : ffi.types.uint32, //DWORD;
SignatureAlgorithm : CRYPT_ALGORITHM_IDENTIFIER,
Issuer : CERT_NAME_BLOB,
ThisUpdate : FILETIME,
NextUpdate : FILETIME,
cCRLEntry : DWORD,
rgCRLEntry : PCRL_ENTRY,
cExtension : DWORD,
rgExtension : PCERT_EXTENSION
}),
PCRL_INFO = new refT(CRL_INFO);
var CRL_CONTEXT = ffi.Struct({
dwCertEncodingType: ffi.types.uint32, //DWORD
pbCrlEncoded: ffi.types.puint8, // PBYTE,
cbCrlEncoded: ffi.types.uint32, // DWORD
pCrlInfo: PCRL_INFO,
hCertStore :HCERTSTORE
}),
PCCRL_CONTEXT = new refT(CRL_CONTEXT),
PPCCRL_CONTEXT = new refT(PCCRL_CONTEXT);
cryptApi.struct = {
CRYPT_ALGORITHM_IDENTIFIER: CRYPT_ALGORITHM_IDENTIFIER,
FILETIME: FILETIME,
CERT_EXTENSION: CERT_EXTENSION,
PCERT_EXTENSION: PCERT_EXTENSION,
CRL_ENTRY: CRL_ENTRY,
PCRL_ENTRY: PCRL_ENTRY,
CRL_INFO: CRL_INFO,
PCRL_INFO: PCRL_INFO,
CRL_CONTEXT: CRL_CONTEXT,
PCCRL_CONTEXT: PCCRL_CONTEXT,
PPCCRL_CONTEXT: PPCCRL_CONTEXT,
CRYPTOAPI_BLOB: CRYPTOAPI_BLOB,
CRYPT_OBJID_BLOB: CRYPT_OBJID_BLOB,
CERT_NAME_BLOB: CERT_NAME_BLOB,
PCERT_NAME_BLOB: PCERT_NAME_BLOB,
CRYPT_INTEGER_BLOB: CRYPT_INTEGER_BLOB
};
/*
function CertNameToStrA(dwCertEncodingType :DWORD;
pName :PCERT_NAME_BLOB;
dwStrType :DWORD;
psz :LPSTR; //OPTIONAL
csz :DWORD):DWORD ; stdcall;
//+-------------------------------------------------------------------------
//--------------------------------------------------------------------------
function CertNameToStrW(dwCertEncodingType :DWORD;
pName :PCERT_NAME_BLOB;
dwStrType :DWORD;
psz :LPWSTR; //OPTIONAL
csz :DWORD):DWORD ; stdcall;
*/
//function CertFreeCRLContext(pCrlContext :PCCRL_CONTEXT):BOOL
cryptApi.crypt32 = crypt32 = ffi.Library(CRYPT32_DLL, {
CertNameToStrA: {
rval: DWORD,
args: [
DWORD, //dwCertEncodingType
PCERT_NAME_BLOB, //pName :
DWORD, //dwStrType
LPSTR, // psz OPTIONAL
DWORD //csz
]
},
CryptQueryObject: {
rval: BOOL,
args: [
DWORD, //dwObjectType:
Pointer, //pvObject:
DWORD, //dwExpectedContentTypeFlags,
DWORD, //dwExpectedFormatTypeFlags,
DWORD, //dwFlags: ;
LPDWORD, //pdwMsgAndCertEncodingType,
LPDWORD, //pdwContentType,
LPDWORD, // pdwFormatType;
HCERTSTORE, //phCertStore
HCRYPTMSG, //phMsg
PPointer //ppvContext
]
},
CertFreeCRLContext: {
rval: BOOL,
args: [
PCCRL_CONTEXT //pCrlContext
]
}
});
/**
* Parse CRL (certificate revocation list) file.
* Result object has structure `{issuer: String, crl:{[{serial: [], revocationDate: Date }]}}`
* @param {String} fileName CRL file name
* @returns {Object}
*/
module.exports.getCRLInfo = function getCRLInfo(fileName){
var s = new ffi.types.wideString(),
result = { crl:[] },
i, res, ppvContext, countCert, rgCRLEntry, vContext,
sn, byteSN, serialPC, rvDate, bDat, Issuer, IssuerB,
rgCRLEntryRef, issuerStr = new LPSTR(); //ffi.types.ansiString()
s.setStr(fileName);
ppvContext = new PPCCRL_CONTEXT(1);
//ppvContext.alloc();
ppvContext.set( new PCCRL_CONTEXT(1) );
//D:\temp\ACSKIDD-Delta.crl
res = crypt32.CryptQueryObject(
cst.CERT_QUERY_OBJECT_FILE, //DWORD dwObjectType
s, //pvObject:
cst.CERT_QUERY_CONTENT_FLAG_CRL, //dwExpectedContentTypeFlags,
cst.CERT_QUERY_FORMAT_FLAG_BINARY, //dwExpectedFormatTypeFlags
0, //dwFlags: ;
winApi.nil, //pdwMsgAndCertEncodingType,
winApi.nil, //pdwContentType,
winApi.nil, // pdwFormatType;
winApi.nil, //phCertStore
winApi.nil, //phMsg
ppvContext //ppvContext
);
if (!res){
winApi.riseLastWinError();
}
vContext = ppvContext.get().get();
Issuer = vContext.pCrlInfo.get().Issuer;
IssuerB = new PCERT_NAME_BLOB(1);
IssuerB.set(Issuer);
res = crypt32.CertNameToStrA(
vContext.dwCertEncodingType,
IssuerB,
cst.CERT_X500_NAME_STR,
winApi.nil,
0
);
issuerStr.alloc(res);
res = crypt32.CertNameToStrA(
vContext.dwCertEncodingType,
IssuerB,
cst.CERT_X500_NAME_STR,
issuerStr,
res
);
result.issuer = issuerStr.getStr();
issuerStr.free();
IssuerB.free();
result.issuer = result.issuer.replace( "\u0000", "");
countCert = vContext.pCrlInfo.get().cCRLEntry || 0;
rgCRLEntryRef = vContext.pCrlInfo.get().rgCRLEntry;
for ( i = 0; i < countCert; i++ ){
rgCRLEntry = rgCRLEntryRef.get(i);
/*
if ( (i / 1000) === Math.round(i / 1000) ){
toLog(i);
if (console){
console.debug(i);
}
}
*/
sn = rgCRLEntry.SerialNumber;
serialPC = [];
if (sn.pbData.address() <= 0){
continue;
}
for(var y = sn.cbData - 1; y >= 0 ; y--){
byteSN = sn.pbData.get(y);
/*jshint bitwise: false*/
serialPC.push(
(byteSN >> 4).toString(16),
(byteSN & 0xF).toString(16)
);
/*jshint bitwise: true*/
}
rvDate = winApi.fileTimeToDate(rgCRLEntry.RevocationDate);
result.crl.push({serial: serialPC, revocationDate: rvDate });
}
res = crypt32.CertFreeCRLContext( ppvContext.get() );
if (!res){
winApi.riseLastWinError();
}
ppvContext.get().free();
ppvContext.free();
return result;
};