1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-06 00:13:33 +00:00
Files
MeshAgent/modules/win-crypto.js
Ylian Saint-Hilaire 3c80473a94 Major agent update.
2018-09-05 11:01:17 -07:00

775 lines
38 KiB
JavaScript

/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var CRYPT_DECODE_NOCOPY_FLAG = 1;
var CERT_X500_NAME_STR = 3;
var CNG_RSA_PUBLIC_KEY_BLOB = 72;
var X509_CERT_TO_BE_SIGNED = 2;
var X509_CERT_REQUEST_TO_BE_SIGNED = 4;
var PROV_RSA_FULL = 1;
var CRYPT_MACHINE_KEYSET = 0x20;
var AT_SIGNATURE = 2;
var PKCS_7_ASN_ENCODING = 0x00010000;
var X509_ASN_ENCODING = 0x00000001;
var X509_DSS_PUBLICKEY = 38;
var X509_PUBLIC_KEY_INFO = 8;
var CERT_V1 = 0;
var CERT_V2 = 1;
var CERT_V3 = 2;
var BCRYPT_RNG_USE_ENTROPY_IN_BUFFER = 0x00000001;
var RSA_CSP_PUBLICKEYBLOB = 19;
var CRYPT_ALGORITHMS = {SHA1: "1.2.840.113549.1.1.5", SHA256: "1.2.840.113549.1.1.11", SHA384: "1.2.840.113549.1.1.12", SHA512: "1.2.840.113549.1.1.13"};
var CRYPT_ALGORITHMS_EX = { "1.2.840.113549.1.1.5": "SHA1", "1.2.840.113549.1.1.11": "SHA256", "1.2.840.113549.1.1.12": "SHA384", "1.2.840.113549.1.1.13": "SHA512" };
var CRYPT_EXTENSION_OIDS =
{
szOID_AUTHORITY_KEY_IDENTIFIER: "2.5.29.1",
szOID_KEY_ATTRIBUTES: "2.5.29.2",
szOID_CERT_POLICIES_95: "2.5.29.3",
szOID_KEY_USAGE_RESTRICTION: "2.5.29.4",
szOID_SUBJECT_ALT_NAME: "2.5.29.7",
szOID_ISSUER_ALT_NAME: "2.5.29.8",
szOID_BASIC_CONSTRAINTS: "2.5.29.10",
szOID_KEY_USAGE: "2.5.29.15",
szOID_PRIVATEKEY_USAGE_PERIOD: "2.5.29.16",
szOID_BASIC_CONSTRAINTS2: "2.5.29.19",
szOID_CERT_POLICIES: "2.5.29.32",
szOID_ANY_CERT_POLICY: "2.5.29.32.0",
szOID_INHIBIT_ANY_POLICY: "2.5.29.54",
szOID_AUTHORITY_KEY_IDENTIFIER2: "2.5.29.35",
szOID_SUBJECT_KEY_IDENTIFIER: "2.5.29.14",
szOID_SUBJECT_ALT_NAME2: "2.5.29.17",
szOID_ISSUER_ALT_NAME2: "2.5.29.18",
szOID_CRL_REASON_CODE: "2.5.29.21",
szOID_REASON_CODE_HOLD: "2.5.29.23",
szOID_CRL_DIST_POINTS: "2.5.29.31",
szOID_ENHANCED_KEY_USAGE: "2.5.29.37",
szOID_ANY_ENHANCED_KEY_USAGE: "2.5.29.37.0"
};
var CRYPT_KEY_ALGORITHMS =
{
"1.2.840.113549": "RSA",
"1.2.840.113549.1": "PKCS",
"1.2.840.113549.2": "RSA_HASH",
"1.2.840.113549.3": "RSA_ENCRYPT",
"1.2.840.113549.1.1": "PKCS_1",
"1.2.840.113549.1.2": "PKCS_2",
"1.2.840.113549.1.3": "PKCS_3",
"1.2.840.113549.1.4": "PKCS_4",
"1.2.840.113549.1.5": "PKCS_5",
"1.2.840.113549.1.6": "PKCS_6",
"1.2.840.113549.1.7": "PKCS_7",
"1.2.840.113549.1.8": "PKCS_8",
"1.2.840.113549.1.9": "PKCS_9",
"1.2.840.113549.1.10": "PKCS_10",
"1.2.840.113549.1.12": "PKCS_12",
"1.2.840.113549.1.1.1": "RSA_RSA",
"1.2.840.113549.1.1.2": "RSA_MD2RSA",
"1.2.840.113549.1.1.3": "RSA_MD4RSA",
"1.2.840.113549.1.1.4": "RSA_MD5RSA",
"1.2.840.113549.1.1.5": "RSA_SHA1RSA",
"1.2.840.113549.1.1.6": "RSA_SETOAEP_RSA",
"1.2.840.113549.1.1.7": "RSAES_OAEP",
"1.2.840.113549.1.1.8": "RSA_MGF1",
"1.2.840.113549.1.1.9": "RSA_PSPECIFIED",
"1.2.840.113549.1.1.10": "RSA_SSA_PSS",
"1.2.840.113549.1.1.11": "RSA_SHA256RSA",
"1.2.840.113549.1.1.12": "RSA_SHA384RSA",
"1.2.840.113549.1.1.13": "RSA_SHA512RSA",
"1.2.840.113549.1.3.1": "RSA_DH"
};
var CRYPT_KEY_USAGES =
{
CERT_DIGITAL_SIGNATURE_KEY_USAGE: 0x80,
CERT_NON_REPUDIATION_KEY_USAGE: 0x40,
CERT_KEY_ENCIPHERMENT_KEY_USAGE: 0x20,
CERT_DATA_ENCIPHERMENT_KEY_USAGE: 0x10,
CERT_KEY_AGREEMENT_KEY_USAGE: 0x08,
CERT_KEY_CERT_SIGN_KEY_USAGE: 0x04,
CERT_OFFLINE_CRL_SIGN_KEY_USAGE: 0x02,
CERT_CRL_SIGN_KEY_USAGE: 0x02,
CERT_ENCIPHER_ONLY_KEY_USAGE: 0x01
};
function WinCrypto()
{
this._ObjectID = 'win-crypto';
this._marshal = require('_GenericMarshal');
this._Kernel32 = this._marshal.CreateNativeProxy('Kernel32.dll');
this._Kernel32.CreateMethod('FileTimeToSystemTime');
this._Kernel32.CreateMethod('SystemTimeToFileTime');
this._Kernel32.CreateMethod('GetLastError');
this._Kernel32.CreateMethod('GetSystemTime');
this._Bcrypt = this._marshal.CreateNativeProxy('Bcrypt.dll');
this._Bcrypt.CreateMethod('BCryptGenRandom');
this._Bcrypt.CreateMethod('BCryptCloseAlgorithmProvider');
this._Bcrypt.CreateMethod('BCryptOpenAlgorithmProvider');
this._Crypt32 = this._marshal.CreateNativeProxy('Crypt32.dll');
this._Crypt32.CreateMethod('CertStrToNameA');
this._Crypt32.CreateMethod('CertCreateCertificateContext');
this._Crypt32.CreateMethod('CertCreateSelfSignCertificate');
this._Crypt32.CreateMethod('CryptAcquireCertificatePrivateKey');
this._Crypt32.CreateMethod('CryptDecodeObject');
this._Crypt32.CreateMethod('CryptDecodeObjectEx');
this._Crypt32.CreateMethod('CryptEncodeObject');
this._Crypt32.CreateMethod('CryptHashCertificate');
this._Crypt32.CreateMethod('CryptSignMessage');
this._Crypt32.CreateMethod('CryptSignAndEncodeCertificate');
this._Crypt32.CreateMethod('CryptStringToBinaryA');
this._Crypt32.CreateMethod('CryptVerifyMessageSignature');
this.CRYPT_KEY_ALGORITHMS_OIDS = {};
for(var i in CRYPT_KEY_ALGORITHMS)
{
this.CRYPT_KEY_ALGORITHMS_OIDS[CRYPT_KEY_ALGORITHMS[i]] = i;
}
this.CRYPT_ENHANCED_KEY_USAGES =
{
CLIENT_AUTH: '1.3.6.1.5.5.7.3.2',
SERVER_AUTH: '1.3.6.1.5.5.7.3.1'
};
this.X509_ASN_ENCODING = 0x00000001;
this.PKCS_7_ASN_ENCODING = 0x00010000;
this.BCRYPT_RNG_ALGORITHM = this._marshal.CreateVariable('RNG', { wide: true });
this.createKeyRestriction = function createKeyRestriction()
{
var retVal =
{
CERT_DATA_ENCIPHERMENT_KEY_USAGE: false,
CERT_DIGITAL_SIGNATURE_KEY_USAGE: false,
CERT_KEY_AGREEMENT_KEY_USAGE: false,
CERT_KEY_CERT_SIGN_KEY_USAGE: false,
CERT_KEY_ENCIPHERMENT_KEY_USAGE: false,
CERT_NON_REPUDIATION_KEY_USAGE: false,
CERT_OFFLINE_CRL_SIGN_KEY_USAGE: false
}
return (retVal);
}
this.makeCert = function makeCert(options)
{
if (!options._algorithm || !CRYPT_ALGORITHMS[options._algorithm]) { throw ('Invalid Algorithm specified: ' + options._algorithm); }
var extensions = [];
var ext = null;
// CN => Common Name
// T => Title
// L => Locality Name
// O => Organization Name
// C => Country
// S => State or Province
// STREET => Street Address
var inStr = '';
var delimiter = '';
for(var i in options)
{
if (!i.startsWith('_'))
{
inStr += (delimiter + i + '=' + options[i]);
if (delimiter == '') { delimiter = ', '; }
}
}
console.log('Certificate Options: ' + inStr);
// Check Extensions
if (options._keyRestrictions)
{
var restrictions = this._marshal.CreateVariable(1);
restrictions.byte = restrictions.toBuffer()[0];
for(var i in options._keyRestrictions)
{
if (options._keyRestrictions[i] == true && CRYPT_KEY_USAGES[i]) { restrictions.byte |= CRYPT_KEY_USAGES[i]; }
}
if (restrictions.byte != 0)
{
var restrictionInfo = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 20 : 40);
if(this._marshal.PointerSize == 4)
{
restrictionInfo.Deref(8, 4).toBuffer().writeUInt32LE(1);
restrictions.pointerBuffer().copy(restrictionInfo.Deref(12, 4).toBuffer());
}
else
{
restrictionInfo.Deref(16, 4).toBuffer().writeUInt32LE(1);
restrictions.pointerBuffer().copy(restrictionInfo.Deref(24, 8).toBuffer());
}
var encodedObject = this.CryptEncodeObject(this.X509_ASN_ENCODING, CRYPT_EXTENSION_OIDS.szOID_KEY_USAGE_RESTRICTION, restrictionInfo);
encodedObject.oid = CRYPT_EXTENSION_OIDS.szOID_KEY_USAGE_RESTRICTION;
extensions.push(encodedObject);
}
}
if (extensions.length > 0)
{
ext = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? (16 * extensions.length) : (32 * extensions.length));
ext.stor = [];
for(var i in extensions)
{
var oid = this._marshal.CreateVariable(extensions[i].oid); ext.stor.push(oid);
var x = this._marshal.PointerSize == 4 ? (16 * i) : (32 * i);
oid.pointerBuffer().copy(ext.Deref(x, this._marshal.PointerSize).toBuffer());
ext.Deref(x + this._marshal.PointerSize, 4).toBuffer().writeUInt32LE(1);
ext.Deref(x + this._marshal.PointerSize == 4 ? 8 : 16, 4).toBuffer().writeUInt32LE(extensions[i]._size);
extensions[i].pointerBuffer().copy(ext.Deref(x + this._marshal.PointerSize == 4 ? 12 : 24, this._marshal.PointerSize).toBuffer());
}
var extContainer = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 8 : 16);
extContainer.Deref(0, 4).toBuffer().writeUInt32LE(extensions.length);
ext.pointerBuffer().copy(extContainer.Deref(this._marshal.PointerSize, this._marshal.PointerSize).toBuffer());
extContainer.ext = ext;
ext = extContainer;
}
var pszX500 = this._marshal.CreateVariable(inStr);
var cbEncoded = this._marshal.CreateVariable(4);
var pbEncoded = 0;
cbEncoded.toBuffer().writeUInt32LE(0);
if(this._Crypt32.CertStrToNameA(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, 0, pbEncoded, cbEncoded, 0).Val == 0)
{
throw ("Error calling 'CertStrToName', Error Code = " + this._Kernel32.GetLastError().Val);
}
pbEncoded = this._marshal.CreateVariable(cbEncoded.toBuffer().readUInt32LE());
if (this._Crypt32.CertStrToNameA(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, 0, pbEncoded, cbEncoded, 0).Val == 0)
{
throw ("Error calling 'CertStrToName', Error Code = " + this._Kernel32.GetLastError().Val);
}
var blob = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 8 : 16);
blob.toBuffer().writeUInt32LE(cbEncoded.toBuffer().readUInt32LE(), 0);
pbEncoded.pointerBuffer().copy(blob.toBuffer(), this._marshal.PointerSize);
var keyProvider = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 28 : 48);
var containerName = this._marshal.CreateVariable(options.CN, { wide: true });
containerName.pointerBuffer().copy(keyProvider.toBuffer());
keyProvider.toBuffer().writeUInt32LE(PROV_RSA_FULL, this._marshal.PointerSize == 4 ? 8 : 16);
keyProvider.toBuffer().writeUInt32LE(CRYPT_MACHINE_KEYSET, this._marshal.PointerSize == 4 ? 12 : 20);
keyProvider.toBuffer().writeUInt32LE(AT_SIGNATURE, this._marshal.PointerSize == 4 ? 24 : 40);
var cryptAlgorithm = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 12 : 24);
var algo = this._marshal.CreateVariable(CRYPT_ALGORITHMS[options._algorithm]);
algo.pointerBuffer().copy(cryptAlgorithm.toBuffer());
var expiration = this._marshal.CreateVariable(16);
this._Kernel32.GetSystemTime(expiration);
// If today is Feb-29, change the expiration to Feb-28, because that's simpler than dealing with leap-year exception complexity
if (expiration.toBuffer().readUInt16LE(2) == 2 && expiration.toBuffer().readUInt16LE(6) == 29) { exipiration.toBuffer().writeUInt16LE(28, 6); }
var year = expiration.toBuffer().readUInt16LE(0);
year += options._years;
expiration.toBuffer().writeUInt16LE(year, 0);
var pCert = this._Crypt32.CertCreateSelfSignCertificate(0, blob, 0, keyProvider, cryptAlgorithm, 0, expiration, ext ? ext : 0);
if (pCert.Val == 0) {
console.log('Error Code = ' + this._Kernel32.GetLastError().Val);
}
console.log('pCert = ' + pCert.Val);
var privateKey = this._marshal.CreatePointer();
var keyspec = this._marshal.CreateVariable(4);
var needFree = this._marshal.CreateVariable(4);
if(pCert.Val != 0)
{
var keyResult = this._Crypt32.CryptAcquireCertificatePrivateKey(pCert, 0, 0, privateKey, keyspec, needFree);
pCert.privateKey = privateKey.Deref();
pCert.privateKey.keySpec = keyspec.toBuffer().readUInt32LE();
pCert.privateKey.needFree = needFree.toBuffer().readUInt32LE();
console.log('keyResult=' + keyResult.Val);
console.log('NeedFree=' + needFree.toBuffer().readUInt32LE());
console.log('KeySpec=' + keyspec.toBuffer().readUInt32LE());
var certInfo = pCert.Deref(this._marshal.PointerSize == 4 ? 12 : 24, this._marshal.PointerSize).Deref(this._marshal.PointerSize == 4 ? 112 : 208);
var signatureAlgorithm = certInfo.Deref(this._marshal.PointerSize == 4 ? 12 : 24, this._marshal.PointerSize);
var publicKeyInfo = certInfo.Deref(this._marshal.PointerSize == 4 ? 56 : 96, this._marshal.PointerSize == 4 ? 24 : 48);
var keyAlgorithm = publicKeyInfo.Deref(0, this._marshal.PointerSize == 4 ? 12 : 24);
var keyLen = publicKeyInfo.Deref(this._marshal.PointerSize == 4 ? 12 : 16, 4).toBuffer().readUInt32LE();
var key = publicKeyInfo.Deref(this._marshal.PointerSize == 4 ? 16 : 32, this._marshal.PointerSize).Deref(keyLen);
pCert.publicKey = key.toBuffer();
pCert.publicKey._key = key;
pCert.publicKey.oid = keyAlgorithm.Deref().String;
pCert.SubjectPublicKeyInfo = publicKeyInfo;
pCert.Subject = certInfo.Deref(this._marshal.PointerSize == 4 ? 48 : 80, this._marshal.PointerSize == 4 ? 112 : 208);
console.log('PublicKey/OID: ' + pCert.publicKey.oid);
}
pCert.parent = this;
pCert.signMessage = function signMessage(message, options)
{
var crypto = this.parent;
var signMessagePara = crypto._marshal.CreateVariable(crypto._marshal.PointerSize == 4 ? 68 : 120);
var cbSize = signMessagePara.Deref(0, 4);
var msgEncodingType = signMessagePara.Deref(4, 4);
var pcertContext = signMessagePara.Deref(8, crypto._marshal.PointerSize);
var hashAlgorithm = signMessagePara.Deref(crypto._marshal.PointerSize == 4 ? 12 : 16, crypto._marshal.PointerSize == 4 ? 12 : 24);
var cMsgCert = signMessagePara.Deref(crypto._marshal.PointerSize == 4 ? 28 : 48, 4);
var rgpMsgCert = signMessagePara.Deref(crypto._marshal.PointerSize == 4 ? 32 : 56, crypto._marshal.PointerSize);
var algorithm = crypto._marshal.CreateVariable(CRYPT_ALGORITHMS[options.hashAlgorithm]);
var msgArray = crypto._marshal.CreateVariable(message.length);
var rgcbToBeSigned = crypto._marshal.CreateVariable(4);
var signedMessage;
var signedMessageLen = crypto._marshal.CreateVariable(4);
signedMessageLen.toBuffer().writeUInt32LE(0);
rgcbToBeSigned.toBuffer().writeUInt32LE(message.length);
message.copy(msgArray.toBuffer());
algorithm.pointerBuffer().copy(hashAlgorithm.toBuffer());
msgEncodingType.toBuffer().writeUInt32LE(options.encodingType);
cbSize.toBuffer().writeUInt32LE(crypto._marshal.PointerSize == 4 ? 68 : 120);
msgEncodingType.toBuffer().writeUInt32LE(PKCS_7_ASN_ENCODING);
this.pointerBuffer().copy(pcertContext.toBuffer());
cMsgCert.toBuffer().writeUInt32LE(1);
pcertContext.getPointerPointer().toBuffer().copy(rgpMsgCert.toBuffer());
var result = crypto._Crypt32.CryptSignMessage(signMessagePara, 0, 1, msgArray.getPointerPointer(), rgcbToBeSigned, 0, signedMessageLen).Val;
if (result != 0)
{
signedMessage = crypto._marshal.CreateVariable(signedMessageLen.toBuffer().readUInt32LE());
if(crypto._Crypt32.CryptSignMessage(signMessagePara, 0, 1, msgArray.getPointerPointer(), rgcbToBeSigned, signedMessage, signedMessageLen).Val != 0)
{
var retVal = signedMessage.toBuffer();
retVal._owner = signedMessage;
return (retVal);
}
else
{
throw ('Error Signing Message: ' + crypto._Kernel32.GetLastError().Val);
}
}
else
{
throw ('Error Signing Message: ' + crypto._Kernel32.GetLastError().Val);
}
};
return (pCert);
};
this.verifyMessage = function verifyMessage(message, options)
{
var verifyParam = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 20 : 32);
var _cbSize = verifyParam.Deref(0, 4);
var _dwMsgAndCertEncodingType = verifyParam.Deref(4, 4);
var signedMessage = this._marshal.CreateVariable(message.length);
message.copy(signedMessage.toBuffer());
var decodedLength = this._marshal.CreateVariable(4);
decodedLength.toBuffer().writeUInt32LE(0);
_cbSize.toBuffer().writeUInt32LE(this._marshal.PointerSize == 4 ? 20 : 32);
_dwMsgAndCertEncodingType.toBuffer().writeUInt32LE(options.encodingType);
var result = this._Crypt32.CryptVerifyMessageSignature(verifyParam, 0, signedMessage, message.length, 0, decodedLength, 0).Val;
if(result != 0)
{
var signerCert = this._marshal.CreatePointer();
var decoded = this._marshal.CreateVariable(decodedLength.toBuffer().readUInt32LE());
console.log('Decoded Length = ' + decodedLength.toBuffer().readUInt32LE());
if (this._Crypt32.CryptVerifyMessageSignature(verifyParam, 0, signedMessage, message.length, decoded, decodedLength, 0).Val != 0)
{
var retVal = decoded.toBuffer();
retVal._owner = decoded;
return (retVal);
}
else
{
throw ('Error Verifying Message: ' + this._Kernel32.GetLastError().Val);
}
}
else
{
throw ('Error Verifying Message2');
}
};
this.loadCert = function loadCert(encodedCert, options)
{
console.log('LoadCert: ' + options.encodingType, 'Length: ' + encodedCert.length);
var pbCertEncoded = this._marshal.CreateVariable(encodedCert.length);
encodedCert.copy(pbCertEncoded.toBuffer());
var pcert = this._Crypt32.CertCreateCertificateContext(options.encodingType, pbCertEncoded, encodedCert.length);
if(pcert.Val == 0)
{
throw ('Error loading Certificate: ' + this._Kernel32.GetLastError().Val);
}
else
{
pcert._marshal = this._marshal;
pcert._Crypt32 = this._Crypt32;
pcert._Kernel32 = this._Kernel32;
pcert._raw = pbCertEncoded;
pcert.getInfo = getInfo;
return (pcert);
}
};
this.EncodeString = function EncodeString(inStr)
{
var pszX500 = this._marshal.CreateVariable(inStr);
var dwSize = this._marshal.CreateVariable(4);
dwSize.toBuffer().writeUInt32LE(0);
if (this._Crypt32.CertStrToNameA(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, 0, 0, dwSize, 0).Val == 0)
{
throw ("Error calling 'CertStrToName', Error Code = " + this._Kernel32.GetLastError().Val);
}
var pbEncoded = this._marshal.CreateVariable(dwSize.toBuffer().readUInt32LE());
if (this._Crypt32.CertStrToNameA(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, 0, pbEncoded, dwSize, 0).Val == 0)
{
throw ("Error calling 'CertStrToName', Error Code = " + this._Kernel32.GetLastError().Val);
}
pbEncoded._size = dwSize.toBuffer().readUInt32LE();
return (pbEncoded);
};
this.CryptEncodeObject = function CryptEncodeObject(encodingType, structType, data)
{
var dwBufferSize = this._marshal.CreateVariable(4);
var stype = typeof (structType) == 'number' ? structType : this._marshal.CreateVariable(structType);
if(this._Crypt32.CryptEncodeObject(encodingType, stype, data, 0, dwBufferSize).Val == 0)
{
throw ('Error Calling CryptEncodeObject (' + this._Kernel32.GetLastError().Val + ')');
}
var outData = this._marshal.CreateVariable(dwBufferSize.toBuffer().readUInt32LE());
if (this._Crypt32.CryptEncodeObject(encodingType, stype, data, outData, dwBufferSize).Val == 0)
{
throw ('Error Calling CryptEncodeObject (' + this._Kernel32.GetLastError().Val + ')');
}
outData._size = dwBufferSize.toBuffer().readUInt32LE();
return (outData);
}
this.MakeCertFromPublicKey = function MakeCertFromPublicKey(options)
{
if (!options.Issuer || !options.Subject || !options.PublicKey || !options.SigningCert || !options.SignatureAlgorithm) { throw ('Missing Parameters: Issuer, Subject, PublicKey, SigningCert, SignatureAlgorithm'); }
var extensions = [];
var certinfo = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 112 : 208);
certinfo.Deref(0, 4).toBuffer().writeUInt32LE(CERT_V3);
var provider = this._marshal.CreatePointer();
var r = this._Bcrypt.BCryptOpenAlgorithmProvider(provider, this.BCRYPT_RNG_ALGORITHM, 0, 0).Val;
if (r != 0)
{
throw('Error opening RandomNumberGenerator')
}
var serial = this._marshal.CreateVariable(8);
r = this._Bcrypt.BCryptGenRandom(provider.Deref(), serial, serial._size, BCRYPT_RNG_USE_ENTROPY_IN_BUFFER).Val;
r = this._Bcrypt.BCryptCloseAlgorithmProvider(provider.Deref(), 0).Val;
// SerialNumber
certinfo.Deref(this._marshal.PointerSize == 4 ? 4 : 8, 4).toBuffer().writeUInt32LE(serial._size);
serial.pointerBuffer().copy(certinfo.Deref(this._marshal.PointerSize == 4 ? 8 : 16, this._marshal.PointerSize).toBuffer());
certinfo._serial = serial;
// Signature Algorithm
certinfo._SignatureAlgorithm = certinfo.Deref(this._marshal.PointerSize == 4 ? 12 : 24, this._marshal.PointerSize == 4 ? 12 : 24);
certinfo._SignatureAlgorithm.oid = this._marshal.CreateVariable(options.SignatureAlgorithm);
certinfo._SignatureAlgorithm.oid.pointerBuffer().copy(certinfo._SignatureAlgorithm.Deref(0, this._marshal.PointerSize).toBuffer());
// Issuer
certinfo._Issuer_Unencoded = this._marshal.CreateVariable(options.Issuer);
certinfo._Issuer_encodedSize = this._marshal.CreateVariable(4);
if (this._Crypt32.CertStrToNameA(X509_ASN_ENCODING, certinfo._Issuer_Unencoded, CERT_X500_NAME_STR, 0, 0, certinfo._Issuer_encodedSize, 0).Val == 0) { throw ('Error Calling CertStrToName'); }
certinfo._Issuer_encoded = this._marshal.CreateVariable(certinfo._Issuer_encodedSize.toBuffer().readUInt32LE());
if (this._Crypt32.CertStrToNameA(X509_ASN_ENCODING, certinfo._Issuer_Unencoded, CERT_X500_NAME_STR, 0, certinfo._Issuer_encoded, certinfo._Issuer_encodedSize, 0).Val == 0) { throw ('Error Calling CertStrToName'); }
certinfo._Issuer_encoded._size = certinfo._Issuer_encodedSize.toBuffer().readUInt32LE();
certinfo.Deref(this._marshal.PointerSize == 4 ? 24 : 48, 4).toBuffer().writeUInt32LE(certinfo._Issuer_encoded._size);
certinfo._Issuer_encoded.pointerBuffer().copy(certinfo.Deref(this._marshal.PointerSize == 4 ? 28 : 56, this._marshal.PointerSize).toBuffer());
// Subject
var delimiter = '';
var inStr = '';
for (var i in options.Subject)
{
inStr += (delimiter + i + '=' + options.Subject[i]);
if (delimiter == '') { delimiter = ', '; }
}
var pszX500 = this._marshal.CreateVariable(inStr);
var cbEncoded = this._marshal.CreateVariable(4);
var pbEncoded = 0;
cbEncoded.toBuffer().writeUInt32LE(0);
if (this._Crypt32.CertStrToNameA(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, 0, pbEncoded, cbEncoded, 0).Val == 0)
{
throw ("Error calling 'CertStrToName', Error Code = " + this._Kernel32.GetLastError().Val);
}
pbEncoded = this._marshal.CreateVariable(cbEncoded.toBuffer().readUInt32LE());
if (this._Crypt32.CertStrToNameA(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, 0, pbEncoded, cbEncoded, 0).Val == 0)
{
throw ("Error calling 'CertStrToName', Error Code = " + this._Kernel32.GetLastError().Val);
}
pbEncoded._size = cbEncoded.toBuffer().readUInt32LE();
certinfo._Subject = pbEncoded;
certinfo.Deref(this._marshal.PointerSize == 4 ? 48 : 80, 4).toBuffer().writeUInt32LE(pbEncoded._size);
pbEncoded.pointerBuffer().copy(certinfo.Deref(this._marshal.PointerSize == 4 ? 52 : 88, this._marshal.PointerSize).toBuffer());
// SubjectPublicKeyInfo
certinfo._pkinfo = certinfo.Deref(this._marshal.PointerSize == 4 ? 56 : 96, this._marshal.PointerSize == 4 ? 24 : 48);
certinfo._pkinfo._oid = this._marshal.CreateVariable(options.PublicKey.oid);
certinfo._pkinfo._oid.pointerBuffer().copy(certinfo._pkinfo.Deref(0, this._marshal.PointerSize).toBuffer());
certinfo._pkinfo.Deref(this._marshal.PointerSize == 4 ? 12 : 24, 4).toBuffer().writeUInt32LE(options.PublicKey.length);
certinfo._pkinfo._key = this._marshal.CreateVariable(options.PublicKey.length);
options.PublicKey.copy(certinfo._pkinfo._key.toBuffer());
certinfo._pkinfo._key.pointerBuffer().copy(certinfo._pkinfo.Deref(this._marshal.PointerSize == 4 ? 16 : 32, this._marshal.PointerSize).toBuffer());
var ft_notBefore_dt = new Date(); ft_notBefore_dt.setTime(Date.now() - 3600000); // One Hour Ago
var ft_notBefore = this.dateToFileTime(ft_notBefore_dt);
ft_notBefore.toBuffer().copy(certinfo.Deref(this._marshal.PointerSize == 4 ? 32 : 64, 8).toBuffer());
var ft_notAfter_dt = new Date(); ft_notAfter_dt.setTime(Date.now() + (365 * 24 * 60 * 60 * 1000)); // One Year from now
var ft_notAfter = this.dateToFileTime(ft_notAfter_dt);
ft_notAfter.toBuffer().copy(certinfo.Deref(this._marshal.PointerSize == 4 ? 40 : 72, 8).toBuffer());
// Parse Extensions
if (options.KeyUsage)
{
var restrictionInfo = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 20 : 40);
restrictionInfo.bitBlob = this._marshal.CreateVariable(1);
var b = 0;
for (var i in options.KeyUsage)
{
if (CRYPT_KEY_USAGES[options.KeyUsage[i]]) { b |= CRYPT_KEY_USAGES[options.KeyUsage[i]]; }
}
restrictionInfo.bitBlob.toBuffer()[0] = b;
if (this._marshal.PointerSize == 4)
{
restrictionInfo.Deref(8, 4).toBuffer().writeUInt32LE(restrictionInfo.bitBlob._size);
restrictionInfo.bitBlob.pointerBuffer().copy(restrictionInfo.Deref(12, 4).toBuffer());
}
else
{
restrictionInfo.Deref(16, 4).toBuffer().writeUInt32LE(restrictionInfo.bitBlob._size);
restrictionInfo.bitBlob.pointerBuffer().copy(restrictionInfo.Deref(24, 8).toBuffer());
}
restrictionInfo.encodedObject = this.CryptEncodeObject(this.X509_ASN_ENCODING, CRYPT_EXTENSION_OIDS.szOID_KEY_USAGE_RESTRICTION, restrictionInfo);
restrictionInfo.encodedObject.oid = CRYPT_EXTENSION_OIDS.szOID_KEY_USAGE_RESTRICTION;
restrictionInfo.encodedObject.ri = restrictionInfo;
extensions.push(restrictionInfo.encodedObject);
}
if (options.EnhancedKeyUsages)
{
var eku = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 8 : 16);
eku.Deref(0, 4).toBuffer().writeUInt32LE(options.EnhancedKeyUsages.length); // cUsageIdentifier
eku._array = this._marshal.CreateVariable(options.EnhancedKeyUsages.length * this._marshal.PointerSize);
for(var i in options.EnhancedKeyUsages)
{
eku._array[i] = this._marshal.CreateVariable(options.EnhancedKeyUsages[i]);
eku._array[i].pointerBuffer().copy(eku._array.Deref(i * this._marshal.PointerSize, this._marshal.PointerSize).toBuffer());
}
eku._array.pointerBuffer().copy(eku.Deref(this._marshal.PointerSize, this._marshal.PointerSize).toBuffer()); //rgpszUsageIdentifier
eku.oid = CRYPT_EXTENSION_OIDS.szOID_ENHANCED_KEY_USAGE;
eku.encodedObject = this.CryptEncodeObject(this.X509_ASN_ENCODING, CRYPT_EXTENSION_OIDS.szOID_ENHANCED_KEY_USAGE, eku);
eku.encodedObject.oid = CRYPT_EXTENSION_OIDS.szOID_ENHANCED_KEY_USAGE;
eku.encodedObject.eku = eku;
extensions.push(eku.encodedObject);
}
// Add Extensions
if (extensions.length > 0)
{
var ext = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? (16 * extensions.length) : (32 * extensions.length));
ext.stor = [];
for (var i in extensions)
{
var oid = this._marshal.CreateVariable(extensions[i].oid); ext.stor.push(oid);
var x = this._marshal.PointerSize == 4 ? (16 * i) : (32 * i);
oid.pointerBuffer().copy(ext.Deref(x, this._marshal.PointerSize).toBuffer());
ext.Deref(x + this._marshal.PointerSize, 4).toBuffer().writeUInt32LE(1);
ext.Deref(x + (this._marshal.PointerSize == 4 ? 8 : 16), 4).toBuffer().writeUInt32LE(extensions[i]._size);
extensions[i].pointerBuffer().copy(ext.Deref(x + (this._marshal.PointerSize == 4 ? 12 : 24), this._marshal.PointerSize).toBuffer());
}
certinfo._ext = ext;
certinfo.Deref(this._marshal.PointerSize == 4 ? 104 : 192, 4).toBuffer().writeUInt32LE(extensions.length); // cExtension
ext.pointerBuffer().copy(certinfo.Deref(this._marshal.PointerSize == 4 ? 108 : 200, this._marshal.PointerSize).toBuffer()); // rgExtension
}
return (certinfo);
};
this.SignCertificate = function SignCertificate(signingCert, certinfo)
{
var sig = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 12 : 24);
sig.oid = this._marshal.CreateVariable(CRYPT_ALGORITHMS.SHA1);
sig.oid.pointerBuffer().copy(sig.Deref(0, this._marshal.PointerSize).toBuffer());
var dwSize = this._marshal.CreateVariable(4);
if (this._Crypt32.CryptSignAndEncodeCertificate(signingCert.privateKey, signingCert.privateKey.keySpec, this.X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, certinfo, sig, 0, 0, dwSize).Val != 0)
{
// success
var pbEncoded = this._marshal.CreateVariable(dwSize.toBuffer().readUInt32LE());
console.log('KeySpec: ' + signingCert.privateKey.keySpec);
console.log(this._Crypt32.CryptSignAndEncodeCertificate(signingCert.privateKey, signingCert.privateKey.keySpec, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, certinfo, sig, 0, pbEncoded, dwSize).Val);
console.log('dwSize: ' + dwSize.toBuffer().readUInt32LE());
pbEncoded._size = dwSize.toBuffer().readUInt32LE();
return (pbEncoded);
}
else {
throw ('Error: ' + this._Kernel32.GetLastError().Val);
}
};
this.SignCertRequest_old = function SignCertRequest_old(signingCert, publicKeyInfo)
{
//var certRequestInfo = this.MakeCertRequestInfo(publicKeyInfo.subject, publicKeyInfo);
var certRequestInfo = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 44 : 88);
certRequestInfo.subject = certRequestInfo.Deref(this._marshal.PointerSize == 4 ? 4 : 8, this._marshal.PointerSize == 4 ? 8 : 16);
certRequestInfo.subjectPublicKeyInfo = certRequestInfo.Deref(this._marshal.PointerSize == 4 ? 12 : 24, this._marshal.PointerSize == 4 ? 24 : 48);
signingCert.Subject.toBuffer().copy(certRequestInfo.subject.toBuffer());
signingCert.SubjectPublicKeyInfo.toBuffer().copy(certRequestInfo.subjectPublicKeyInfo.toBuffer());
var sig = this._marshal.CreateVariable(this._marshal.PointerSize == 4 ? 12 : 24);
sig.oid = this._marshal.CreateVariable(CRYPT_ALGORITHMS.SHA1);
sig.oid.pointerBuffer().copy(sig.Deref(0,this._marshal.PointerSize).toBuffer());
var dwSize = this._marshal.CreateVariable(4);
if (this._Crypt32.CryptSignAndEncodeCertificate(signingCert.privateKey, signingCert.privateKey.keySpec, this.X509_ASN_ENCODING, X509_CERT_REQUEST_TO_BE_SIGNED, certRequestInfo, sig, 0, 0, dwSize).Val != 0)
{
// success
var pbEncoded = this._marshal.CreateVariable(dwSize.toBuffer().readUInt32LE());
console.log('KeySpec: ' + signingCert.privateKey.keySpec);
console.log(this._Crypt32.CryptSignAndEncodeCertificate(signingCert.privateKey, signingCert.privateKey.keySpec, X509_ASN_ENCODING, X509_CERT_REQUEST_TO_BE_SIGNED, certRequestInfo, sig, 0, pbEncoded, dwSize).Val);
console.log('dwSize: ' + dwSize.toBuffer().readUInt32LE());
pbEncoded._size = dwSize.toBuffer().readUInt32LE();
return (pbEncoded);
}
else
{
throw ('Error: ' + this._Kernel32.GetLastError().Val);
}
};
this.dateToFileTime = function dateToFileTime(dt)
{
var systemtime = this._marshal.CreateVariable(16);
var filetime = this._marshal.CreateVariable(8);
systemtime.Deref(0,2).toBuffer().writeUInt16LE(dt.getUTCFullYear());
systemtime.Deref(2, 2).toBuffer().writeUInt16LE(dt.getUTCMonth() + 1);
systemtime.Deref(6, 2).toBuffer().writeUInt16LE(dt.getUTCDate());
systemtime.Deref(8, 2).toBuffer().writeUInt16LE(dt.getUTCHours());
systemtime.Deref(10, 2).toBuffer().writeUInt16LE(dt.getUTCMinutes());
systemtime.Deref(12, 2).toBuffer().writeUInt16LE(dt.getUTCSeconds());
systemtime.Deref(14, 2).toBuffer().writeUInt16LE(dt.getUTCMilliseconds());
if(this._Kernel32.SystemTimeToFileTime(systemtime, filetime).Val == 0)
{
throw ('Error converting time: ' + this._Kernel32.GetLastError().Val);
}
return (filetime);
};
this.fileTimeToDate = function fileTimeToDate(ft)
{
var systemtime = this._marshal.CreateVariable(16);
if(this._Kernel32.FileTimeToSystemTime(ft, systemtime).Val == 0)
{
throw ('Error convertin filetime: ' + this._Kernel32.GetLastError().Val);
}
var ret = Date.now();
var buffer = systemtime.toBuffer();
ret.setUTCFullYear(buffer.readUInt16LE(0));
ret.setUTCMonth(buffer.readUInt16LE(2));
ret.setUTCDate(buffer.readUInt16LE(6));
ret.setUTCHours(buffer.readUInt16LE(8));
ret.setUTCMinutes(buffer.readUInt16LE(10));
ret.setUTCSeconds(buffer.readUInt16LE(12));
ret.setUTCMilliseconds(buffer.readUInt16LE(14));
return (ret);
};
}
function getInfo(options)
{
var certInfo = this.Deref(this._marshal.PointerSize == 4 ? 12 : 24, this._marshal.PointerSize).Deref(this._marshal.PointerSize == 4 ? 112 : 208);
var signatureAlgorithm = certInfo.Deref(this._marshal.PointerSize == 4 ? 12 : 24, this._marshal.PointerSize);
var publicKeyInfo = certInfo.Deref(this._marshal.PointerSize == 4 ? 56 : 96, this._marshal.PointerSize == 4 ? 24 : 48);
var keyAlgorithm = publicKeyInfo.Deref(0, this._marshal.PointerSize == 4 ? 12 : 24);
var publicKeyLen = publicKeyInfo.Deref(this._marshal.PointerSize == 4 ? 12 : 24, 4).toBuffer().readUInt32LE();
var publicKey = publicKeyInfo.Deref(this._marshal.PointerSize == 4 ? 16 : 32, this._marshal.PointerSize).Deref(publicKeyLen).toBuffer().toString('base64');
var thumbprintHash = 'SHA1Stream';
if (options)
{
switch(options.thumbprint)
{
case 'MD5':
thumbprintHash = 'MD5Stream';
break;
case 'SHA1':
thumbprintHash = 'SHA1Stream';
break;
case 'SHA256':
thumbprintHash = 'SHA256Stream';
break;
case 'SHA384':
thumbprintHash = 'SHA384Stream';
break;
case 'SHA512':
thumbprintHash = 'SHA512Stream';
break;
}
}
var retVal = {};
retVal.version = certInfo.toBuffer().readUInt32LE();
retVal.signatureAlgorithm = CRYPT_ALGORITHMS_EX[signatureAlgorithm.Deref().String];
retVal.keyAlgorithm = CRYPT_KEY_ALGORITHMS[keyAlgorithm.Deref().String];
retVal.publicKey = publicKey;
retVal.thumbprint = require(thumbprintHash).create().syncHash(this._raw.toBuffer()).toString('hex');
retVal.thumbprintAlgorithm = options ? options.thumbprint : 'SHA1';
return (retVal);
}
module.exports = new WinCrypto();
//var cng = new WinCrypto();
//var cert = cng.makeCert({ CN: 'Bryan Test', T: 'My Title', _algorithm: 'SHA256', _years: 1 });
//var msg = cert.signMessage(Buffer.from('this is testing'), { hashAlgorithm: 'SHA256', encodingType: PKCS_7_ASN_ENCODING });
//console.log(msg.toString('hex'));
//var pkcs7 = require('pkcs7');
//var result = pkcs7.getSignedDataBlock(msg);
//console.log(result.data, result.signingCertificate.publicKeyHash, result.signingCertificate.fingerprint);
//var decoded = cng.verifyMessage(msg, { encodingType: PKCS_7_ASN_ENCODING });
//console.log(decoded.toString());