@bnnsoftvn/mobile-sic-sdk
Version:
mobilesdk to sign csr
600 lines (589 loc) • 22.4 kB
JavaScript
/*!
* MIT License
*
* Copyright (c) Peculiar Ventures. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
;
Object.defineProperty(exports, '__esModule', { value: true });
require('reflect-metadata');
var ReactNativeBiometrics = require('@bnnsoftvn/react-native-biometrics-sic');
var asn1Csr = require('@peculiar/asn1-csr');
var x509 = require('@peculiar/x509');
var asn1X509 = require('@peculiar/asn1-x509');
var asn1Schema = require('@peculiar/asn1-schema');
var DeviceInfo = require('react-native-device-info');
var xmldom = require('@xmldom/xmldom');
var asn1Rsa = require('@peculiar/asn1-rsa');
var pvtsutils = require('pvtsutils');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var ReactNativeBiometrics__default = /*#__PURE__*/_interopDefaultLegacy(ReactNativeBiometrics);
var DeviceInfo__default = /*#__PURE__*/_interopDefaultLegacy(DeviceInfo);
var xmldom__namespace = /*#__PURE__*/_interopNamespace(xmldom);
class SignResult {
constructor() {
this.status = -1;
this.data = "";
this.message = "";
}
}
const keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const Base64Utils = {
encode: (input) => {
let output = [];
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
}
else if (isNaN(chr3)) {
enc4 = 64;
}
output.push(keyStr.charAt(enc1) +
keyStr.charAt(enc2) +
keyStr.charAt(enc3) +
keyStr.charAt(enc4));
}
return output.join('');
},
encodeFromByteArray: (input) => {
let output = [];
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
while (i < input.length) {
chr1 = input[i++];
chr2 = input[i++];
chr3 = input[i++];
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
}
else if (isNaN(chr3)) {
enc4 = 64;
}
output.push(keyStr.charAt(enc1) +
keyStr.charAt(enc2) +
keyStr.charAt(enc3) +
keyStr.charAt(enc4));
}
return output.join('');
},
decode: (input) => {
let output = '';
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
const base64test = /[^A-Za-z0-9+/=]/g;
if (base64test.test(input)) {
throw new Error("Invalid base64 characters in input. Allowed: A-Z, a-z, 0-9, '+', '/', '='.");
}
input = input.replace(/[^A-Za-z0-9+/=]/g, '');
while (i < input.length) {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output += String.fromCharCode(chr1);
if (enc3 !== 64) {
output += String.fromCharCode(chr2);
}
if (enc4 !== 64) {
output += String.fromCharCode(chr3);
}
}
return output;
}
};
const appVersion = "2.11.3";
const SAD_FORMAT = {
SAD_XML: 0,
SAD_JWK: 1,
SAD_BIN: 2
};
const ERROR_CODES = {
NguoiDungHuy: {
code: -2,
message: "Người dùng hủy.",
},
KyLoi: {
code: -3,
message: "Quá nhiều lần thử. Hãy thử lại sau.",
},
KhongHoTro: {
code: -4,
message: "Thiết bị không hỗ trợ.",
},
ChuaDangKy: {
code: -5,
message: "Thiết bị chưa đăng ký.",
},
KhongXacDinh: {
code: -9,
message: "Có lỗi xảy ra. Vui lòng thử lại sau.",
},
KeyKhongTonTai: {
code: -6,
message: "Không tìm thấy key.",
},
LoiTrongQuaTrinhDongGoiChuKy: {
code: -7,
message: "Lỗi trong quá trình đóng gói chữ ký.",
},
LoiPhienBanSDK: {
code: -8,
message: "Phiên bản SDK cần update.",
},
LoiChuKy: {
code: -10,
message: "Ký thành công nhưng chữ ký bị sai format hoặc trống.",
},
};
const compareVersions = (v1, v2) => {
const v1Parts = v1.split(".").map(Number);
const v2Parts = v2.split(".").map(Number);
for (let i = 0; i < v1Parts.length; i++) {
if ((v1Parts[i] || 0) > (v2Parts[i] || 0))
return true;
if ((v1Parts[i] || 0) < (v2Parts[i] || 0))
return false;
}
return false;
};
function escapeFilterValue(input) {
let escapedResult = "";
for (const inputChar of input) {
switch (inputChar) {
case "*":
escapedResult += "\\2a";
break;
case "(":
escapedResult += "\\28";
break;
case ")":
escapedResult += "\\29";
break;
case "\\":
escapedResult += "\\5c";
break;
case "\0":
escapedResult += "\\00";
break;
default:
escapedResult += inputChar;
break;
}
}
return escapedResult;
}
const isNullOrEmpty = (value) => {
return value === null || value === undefined || value === "";
};
async function GenerateCsr_V2(data) {
var res = new SignResult();
const rnBiometrics = new ReactNativeBiometrics__default["default"]();
const { available, biometryType } = await rnBiometrics.isSensorAvailable();
if (available && biometryType === ReactNativeBiometrics.BiometryTypes.TouchID) {
console.log('TouchID is supported');
}
else if (available && biometryType === ReactNativeBiometrics.BiometryTypes.FaceID) {
console.log('FaceID is supported');
}
else if (available && biometryType === ReactNativeBiometrics.BiometryTypes.Biometrics) {
console.log('Biometrics is supported');
}
else {
console.log('Biometrics not supported');
res.message = ERROR_CODES.KhongHoTro.message;
res.status = ERROR_CODES.KhongHoTro.code;
return res;
}
let promptMessage = "Sinh khoá thiết bị";
let keytype = 0;
let keylength = 2048;
console.log("data input", data);
if (!isNullOrEmpty(data.version)) {
console.log("data.version", data.version);
if (compareVersions(appVersion, data.version)) {
console.log("appVersion");
res.message = ERROR_CODES.LoiPhienBanSDK.message;
res.status = ERROR_CODES.LoiPhienBanSDK.code;
return res;
}
}
if (!isNullOrEmpty(data.algorithm)) {
console.log("data.algorithm", data.algorithm);
data.algorithm.name;
data.algorithm.hash;
}
if (!isNullOrEmpty(data.keytype)) {
console.log("data.keytype", data.keytype);
keytype = data.keytype;
}
if (!isNullOrEmpty(data.keylength)) {
console.log("data.keylength", data.keylength);
keylength = data.keylength;
}
if (!isNullOrEmpty(data.promptMessage)) {
console.log("data.promptMessage", data.promptMessage);
promptMessage = data.promptMessage;
}
if (data.orgcode != null && (data.orgcode.includes("VNPT-SMARTCA") || data.orgcode.includes("VNPT") || data.orgcode.includes("vnpt"))) {
let publicKey = "";
let checkkey = await rnBiometrics.biometricKeysExist(data.userid);
if (checkkey.keysExist) {
console.log("get publickey begin");
let keys = await rnBiometrics.getPublicKey(data.userid);
console.log("get publickey done");
publicKey = keys.publicKey;
}
else {
console.log("create publickey", {
keytag: data.userid,
keytype: keytype,
keysize: keylength
});
const keys = await rnBiometrics.createKeys({
keytag: data.userid,
keytype: keytype,
keysize: keylength
});
console.log("create publickey done");
publicKey = keys.publicKey;
}
var obj = {
subject: escapeFilterValue("CN=" + data.userid),
subjectPublicKeyInfo: publicKey
};
console.log("publicKey", publicKey);
const key = new x509.PublicKey(publicKey);
const asnSpki = asn1Schema.AsnConvert.parse(key.rawData, asn1X509.SubjectPublicKeyInfo);
console.log(asnSpki);
const rsapublicKey = asn1Schema.AsnConvert.parse(asnSpki.subjectPublicKey, asn1Rsa.RSAPublicKey);
console.log(rsapublicKey);
console.log(rsapublicKey.modulus, rsapublicKey.publicExponent);
let modulus = rsapublicKey.modulus;
let exponent = rsapublicKey.publicExponent;
var challenge = new Uint8Array(modulus.byteLength + exponent.byteLength);
challenge.set(new Uint8Array(modulus), 0);
challenge.set(new Uint8Array(exponent), modulus.byteLength);
var device = DeviceInfo__default["default"].getUniqueId();
let deviceuniqdn = "O=" + device;
let devicesyndn = "OU=" + await DeviceInfo__default["default"].syncUniqueId();
let devicesysname = "L=" + DeviceInfo__default["default"].getSystemName();
let devicesysver = "ST=" + DeviceInfo__default["default"].getSystemVersion();
obj.subject += "," + escapeFilterValue(deviceuniqdn)
+ "," + escapeFilterValue(devicesyndn)
+ "," + escapeFilterValue(devicesysname)
+ "," + escapeFilterValue(devicesysver);
const asnReq = new asn1Csr.CertificationRequest({
certificationRequestInfo: new asn1Csr.CertificationRequestInfo({
subjectPKInfo: asnSpki,
version: 0,
subject: asn1Schema.AsnConvert.parse(new x509.Name(obj.subject).toArrayBuffer(), asn1X509.Name)
}),
});
const tbs = asn1Schema.AsnConvert.serialize(asnReq.certificationRequestInfo);
const { success, signature } = await rnBiometrics.createSignature({
promptMessage: promptMessage,
payload: pvtsutils.Convert.ToBase64(challenge),
keytag: data.userid,
type: 1
});
if (success) {
let result = {
csr: pvtsutils.Convert.ToBase64(tbs),
subjectDN: obj.subject,
signature: signature
};
res.data = Base64Utils.encode(JSON.stringify(result)),
res.status = 0;
}
else {
res.status = ERROR_CODES.NguoiDungHuy.code;
res.message = ERROR_CODES.NguoiDungHuy.message;
}
return res;
}
var checkkey = await rnBiometrics.biometricKeysExist(data.userid);
if (checkkey.keysExist) {
console.log("publickey true");
}
else {
console.log("create publickey", {
keytag: data.userid,
keytype: keytype,
keysize: keylength
});
await rnBiometrics.createKeys({
keytag: data.userid,
keytype: keytype,
keysize: keylength
});
console.log("create publickey done");
}
var device = DeviceInfo__default["default"].getUniqueId();
let devicesyndn = await DeviceInfo__default["default"].syncUniqueId();
let devicesysname = DeviceInfo__default["default"].getSystemName();
let devicesysver = DeviceInfo__default["default"].getSystemVersion();
var csrResult = await rnBiometrics.createCsr({
keytag: data.userid,
promptMessage: promptMessage,
commonName: data.userid,
organization: device,
organizationalUnit: devicesyndn,
locality: devicesysname,
state: devicesysver
});
if (csrResult.success && csrResult.csr) {
res.status = 0;
res.data = csrResult.csr;
}
else {
res.status = ERROR_CODES.KhongXacDinh.code;
res.message = csrResult.error ? csrResult.error : ERROR_CODES.KhongXacDinh.message;
}
return res;
}
async function GenerateSad_V2(data) {
const rnBiometrics = new ReactNativeBiometrics__default["default"]();
var res = new SignResult();
if (!isNullOrEmpty(data.version)) {
if (!compareVersions(appVersion, data.version)) {
res.message = ERROR_CODES.LoiPhienBanSDK.message;
res.status = ERROR_CODES.LoiPhienBanSDK.code;
return res;
}
}
var uniqueId = DeviceInfo__default["default"].getUniqueId();
if (data.deviceid != uniqueId) {
res.status = ERROR_CODES.ChuaDangKy.code;
res.message = ERROR_CODES.ChuaDangKy.message;
return res;
}
const { available, biometryType } = await rnBiometrics.isSensorAvailable();
if (available && biometryType === ReactNativeBiometrics.BiometryTypes.TouchID) {
console.log('TouchID is supported');
}
else if (available && biometryType === ReactNativeBiometrics.BiometryTypes.FaceID) {
console.log('FaceID is supported');
}
else if (available && biometryType === ReactNativeBiometrics.BiometryTypes.Biometrics) {
console.log('Biometrics is supported');
}
else {
console.log('Biometrics not supported');
res.message = ERROR_CODES.KhongHoTro.message;
res.status = ERROR_CODES.KhongHoTro.code;
return res;
}
if (data.format == SAD_FORMAT.SAD_BIN) {
let sign = await signbinary(rnBiometrics, data);
res.status = sign.status;
res.data = sign.data;
res.message = sign.message;
}
else if (data.format == SAD_FORMAT.SAD_JWK) {
let sign = await signjson(rnBiometrics, data);
res.status = sign.status;
res.data = sign.data;
res.message = sign.message;
}
else if (data.format == SAD_FORMAT.SAD_XML) {
let sign = await signxml(rnBiometrics, data);
res.status = sign.status;
res.data = sign.data;
res.message = sign.message;
}
return res;
}
async function signbinary(rnBiometrics, data) {
var res = new SignResult();
console.log("signbinary0");
try {
let keytag = data.userid;
let keylength = 2048;
var checkkey = await rnBiometrics.biometricKeysExist(keytag);
if (checkkey.keysExist != true) {
res.status = ERROR_CODES.KeyKhongTonTai.code;
res.message = ERROR_CODES.KeyKhongTonTai.message;
return res;
}
console.log("signbinary1");
try {
const { success, signature } = await rnBiometrics.createSignature({
promptMessage: data.promptMessage,
payload: data.sad,
keytag: keytag,
type: 1
});
if (success && (signature == null || signature == undefined || signature.length == 0)) {
res.status = ERROR_CODES.NguoiDungHuy.code;
res.message = ERROR_CODES.NguoiDungHuy.message;
}
else if (success && signature) {
res.data = signature;
res.status = 0;
}
else {
res.status = ERROR_CODES.NguoiDungHuy.code;
res.message = ERROR_CODES.NguoiDungHuy.message;
}
}
catch (err) {
res.status = ERROR_CODES.KyLoi.code;
res.message = ERROR_CODES.KyLoi.message;
}
}
catch (err) {
res.status = ERROR_CODES.KhongXacDinh.code;
res.message = ERROR_CODES.KhongXacDinh.message;
}
return res;
}
async function signjson(rnBiometrics, data) {
var res = new SignResult();
try {
let keytag = data.userid;
let keylength = 2048;
var checkkey = await rnBiometrics.biometricKeysExist(keytag);
if (checkkey.keysExist != true) {
res.status = ERROR_CODES.KeyKhongTonTai.code;
res.message = ERROR_CODES.KeyKhongTonTai.message;
return res;
}
console.log("checkkey b", checkkey);
try {
const { success, signature } = await rnBiometrics.createSignature({
promptMessage: data.promptMessage,
payload: data.sad,
keytag: keytag,
type: 0
});
console.log("return b", signature);
if (success && signature) {
var tokensign = data.sad + "." + signature.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
console.log("jwt ", tokensign);
res.data = tokensign;
res.status = 0;
}
else {
res.status = ERROR_CODES.NguoiDungHuy.code;
res.message = ERROR_CODES.NguoiDungHuy.message;
}
}
catch (err) {
res.status = ERROR_CODES.KyLoi.code;
res.message = ERROR_CODES.KyLoi.message + "Chi tiết: " + err;
}
}
catch (err) {
res.status = ERROR_CODES.KhongXacDinh.code;
res.message = ERROR_CODES.KhongXacDinh.message;
}
return res;
}
async function signxml(rnBiometrics, data) {
var res = new SignResult();
console.log("signxml0");
try {
let keytag = data.userid;
let keylength = 2048;
var checkkey = await rnBiometrics.biometricKeysExist(keytag);
if (checkkey.keysExist != true) {
res.status = ERROR_CODES.KeyKhongTonTai.code;
res.message = ERROR_CODES.KeyKhongTonTai.message;
return res;
}
console.log("signxml2");
try {
const { success, signature } = await rnBiometrics.createSignature({
promptMessage: data.promptMessage,
payload: data.sad,
keytag: keytag,
type: 1
});
if (success) {
const doc = new xmldom__namespace.DOMParser().parseFromString(Base64Utils.decode(data.sad), "application/xml");
let signatureXml = "<Signature><DigestMethod>SHA256</DigestMethod><SignatureValue>" + signature + "</SignatureValue></Signature>";
const docsig = new xmldom__namespace.DOMParser().parseFromString(signatureXml, "application/xml");
if (doc.documentElement == null || docsig.documentElement == null) {
res.status = ERROR_CODES.LoiTrongQuaTrinhDongGoiChuKy.code;
res.message = ERROR_CODES.LoiTrongQuaTrinhDongGoiChuKy.message;
return res;
}
else {
doc.documentElement.appendChild(docsig.documentElement);
res.data = Base64Utils.encode(doc.toString());
res.status = 0;
return res;
}
}
else {
res.status = ERROR_CODES.NguoiDungHuy.code;
res.message = ERROR_CODES.NguoiDungHuy.message;
}
}
catch (err) {
res.status = ERROR_CODES.KyLoi.code;
res.message = ERROR_CODES.KyLoi.message;
}
}
catch (err) {
res.status = ERROR_CODES.KhongXacDinh.code;
res.message = ERROR_CODES.KhongXacDinh.message;
}
return res;
}
exports.GenerateCsr_V2 = GenerateCsr_V2;
exports.GenerateSad_V2 = GenerateSad_V2;