diff --git a/examples/cryptopro-cades/package.json b/examples/cryptopro-cades/package.json index 7bed9e9..91a0259 100644 --- a/examples/cryptopro-cades/package.json +++ b/examples/cryptopro-cades/package.json @@ -34,6 +34,6 @@ }, "scripts": { "build:prod": "webpack --mode=production --config webpack.config.js", - "dev": "webpack serve --mode=development" + "dev": "webpack serve --mode=development --port 8081" } } diff --git a/examples/cryptopro-cades/public/index.html b/examples/cryptopro-cades/public/index.html index 86683d7..d832c18 100644 --- a/examples/cryptopro-cades/public/index.html +++ b/examples/cryptopro-cades/public/index.html @@ -8,7 +8,7 @@ name="description" content="Web site created using create-react-app" /> - React App + @astral/cryptopro-cades diff --git a/examples/cryptopro-cades/src/App.tsx b/examples/cryptopro-cades/src/App.tsx index d148d34..5d27d2d 100644 --- a/examples/cryptopro-cades/src/App.tsx +++ b/examples/cryptopro-cades/src/App.tsx @@ -48,24 +48,35 @@ const CryptoApp = () => { const systemInfo = await getSystemInfo(); setVersionInfo(systemInfo); } catch (error) { - window.alert(error); + outputError(error); + window.alert(error?.toString()); } } async function fetchCertificates() { - const fetchedCertificates = await getCertificates(STORE_TYPE.ALL); + try { + const fetchedCertificates = await getCertificates(STORE_TYPE.ALL); - setCertificates(fetchedCertificates); + setCertificates(fetchedCertificates); - // автоматически берем первый валидный серт если еще выбран - if (!selectedCertificate) { - setSelectedCertificate( - fetchedCertificates.find((c) => c.isGost && c.hasPrivateKey) - ); + // автоматически берем первый валидный серт если еще выбран + if (!selectedCertificate) { + setSelectedCertificate( + fetchedCertificates.find((c) => c.isGost && c.hasPrivateKey) + ); + } + } catch (error) { + outputError(error); + window.alert(error?.toString()); } } async function fetchCryptoProviders() { - const cryptoProviders = await getCryptoProviders(); - setCryptoProviders(cryptoProviders); + try { + const cryptoProviders = await getCryptoProviders(); + setCryptoProviders(cryptoProviders); + } catch (error) { + outputError(error); + window.alert(error?.toString()); + } } if (showCryptoProviders) { diff --git a/package-lock.json b/package-lock.json index f89993c..38e907a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3650,10 +3650,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz", - "integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==", - "license": "MIT" + "version": "17.0.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.36.tgz", + "integrity": "sha512-V3orv+ggDsWVHP99K3JlwtH20R7J4IhI1Kksgc+64q5VxgfRkQG8Ws3MFm/FZOKDYGy9feGFlZ70/HpCNe9QaA==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -3668,16 +3667,6 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "license": "MIT" }, - "node_modules/@types/pkijs": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/@types/pkijs/-/pkijs-0.0.14.tgz", - "integrity": "sha512-i2T9mjyNjWLqpv1Zg5Odo54VOv191EoahE41E2IKwOcZjQqCGVySpouAB3Ty5GXa8lnHHH0/DvbqzfGOl6GK7w==", - "dev": true, - "dependencies": { - "@types/pvutils": "*", - "asn1js": "^3.0.2" - } - }, "node_modules/@types/prettier": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.1.tgz", @@ -3691,12 +3680,6 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", "license": "MIT" }, - "node_modules/@types/pvutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/pvutils/-/pvutils-1.0.1.tgz", - "integrity": "sha512-OwGyloDb4Gz7cKzFt1ZfHkNXbh1gxd5706hPF5ot4cvsa2EfbqY1tz2nWtu6xWDccNSZ16p/pi027gloQu4hsQ==", - "dev": true - }, "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -5062,10 +5045,9 @@ "license": "MIT" }, "node_modules/asn1js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.4.tgz", - "integrity": "sha512-ZibuNYyfODvHiVyRFs80xLAUjCwBSkLbE+r1TasjlRKwdodENGT4AlLdaN12Pl/EcK3lFMDYXU6lE2g7Sq9VVQ==", - "license": "BSD-3-Clause", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", "dependencies": { "pvtsutils": "^1.3.2", "pvutils": "^1.1.3", @@ -6026,10 +6008,9 @@ } }, "node_modules/bytestreamjs": { - "version": "1.0.29", - "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-1.0.29.tgz", - "integrity": "sha512-Mri3yqoo9YvdaSvD5OYl4Rdu9zCBJInW/Ez31sdlNY4ikMy//EvTTmidfLcs0e+NBvKVEpPzYvJAesjgMdjnZg==", - "license": "BSD-3-Clause", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.0.tgz", + "integrity": "sha512-TyOlxeS92FcMOaJwAVq5gwqW0vfkWUv5W+ErwdbBzolcUN/9XYpCKWvCV21jjzXU550D9Wt4GgE8Pr1vVbR+wQ==", "engines": { "node": ">=6.0.0" } @@ -13780,17 +13761,18 @@ } }, "node_modules/pkijs": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-2.4.0.tgz", - "integrity": "sha512-cjJP/mYuGyMrjJ49jI04khId5Oufd3nFTUYBzQTIIVNI7/oAWdwXEfpwTF8HELFV/gz+WGYUBHCe3KHWD8rYvg==", - "license": "BSD-3-Clause", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.0.3.tgz", + "integrity": "sha512-36EZzEAZj/Wi53qM9GQNPWaQ2YX6P+xbkTwlkXNwoMkRetJafGpxxZpi41hCoLVS3bqzw7/Ws9g1pzkOILos7Q==", "dependencies": { - "asn1js": "^3.0.3", - "bytestreamjs": "^1.0.29", - "pvutils": "^1.1.3" + "asn1js": "^3.0.5", + "bytestreamjs": "^2.0.0", + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=12.0.0" } }, "node_modules/please-upgrade-node": { @@ -17863,16 +17845,14 @@ }, "packages/cryptopro-cades": { "name": "@astral/cryptopro-cades", - "version": "1.0.3", "dependencies": { - "asn1js": "^3.0.2", - "buffer": "^6.0.3", - "pkijs": "^2.3.1" + "asn1js": "3.0.5", + "buffer": "6.0.3", + "pkijs": "3.0.3" }, "devDependencies": { - "@types/node": "^17.0.33", - "@types/pkijs": "0.0.14", - "copyfiles": "^2.4.1" + "@types/node": "17.0.36", + "copyfiles": "2.4.1" } } }, @@ -18236,12 +18216,11 @@ "@astral/cryptopro-cades": { "version": "file:packages/cryptopro-cades", "requires": { - "@types/node": "^17.0.33", - "@types/pkijs": "0.0.14", - "asn1js": "^3.0.2", - "buffer": "^6.0.3", - "copyfiles": "^2.4.1", - "pkijs": "^2.3.1" + "@types/node": "17.0.36", + "asn1js": "3.0.5", + "buffer": "6.0.3", + "copyfiles": "2.4.1", + "pkijs": "3.0.3" } }, "@babel/code-frame": { @@ -20282,9 +20261,9 @@ "dev": true }, "@types/node": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz", - "integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==" + "version": "17.0.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.36.tgz", + "integrity": "sha512-V3orv+ggDsWVHP99K3JlwtH20R7J4IhI1Kksgc+64q5VxgfRkQG8Ws3MFm/FZOKDYGy9feGFlZ70/HpCNe9QaA==" }, "@types/normalize-package-data": { "version": "2.4.1", @@ -20297,16 +20276,6 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, - "@types/pkijs": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/@types/pkijs/-/pkijs-0.0.14.tgz", - "integrity": "sha512-i2T9mjyNjWLqpv1Zg5Odo54VOv191EoahE41E2IKwOcZjQqCGVySpouAB3Ty5GXa8lnHHH0/DvbqzfGOl6GK7w==", - "dev": true, - "requires": { - "@types/pvutils": "*", - "asn1js": "^3.0.2" - } - }, "@types/prettier": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.1.tgz", @@ -20318,12 +20287,6 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, - "@types/pvutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/pvutils/-/pvutils-1.0.1.tgz", - "integrity": "sha512-OwGyloDb4Gz7cKzFt1ZfHkNXbh1gxd5706hPF5ot4cvsa2EfbqY1tz2nWtu6xWDccNSZ16p/pi027gloQu4hsQ==", - "dev": true - }, "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -21237,9 +21200,9 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "asn1js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.4.tgz", - "integrity": "sha512-ZibuNYyfODvHiVyRFs80xLAUjCwBSkLbE+r1TasjlRKwdodENGT4AlLdaN12Pl/EcK3lFMDYXU6lE2g7Sq9VVQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", "requires": { "pvtsutils": "^1.3.2", "pvutils": "^1.1.3", @@ -21937,9 +21900,9 @@ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "bytestreamjs": { - "version": "1.0.29", - "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-1.0.29.tgz", - "integrity": "sha512-Mri3yqoo9YvdaSvD5OYl4Rdu9zCBJInW/Ez31sdlNY4ikMy//EvTTmidfLcs0e+NBvKVEpPzYvJAesjgMdjnZg==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.0.tgz", + "integrity": "sha512-TyOlxeS92FcMOaJwAVq5gwqW0vfkWUv5W+ErwdbBzolcUN/9XYpCKWvCV21jjzXU550D9Wt4GgE8Pr1vVbR+wQ==" }, "cacheable-request": { "version": "2.1.4", @@ -27254,13 +27217,15 @@ } }, "pkijs": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-2.4.0.tgz", - "integrity": "sha512-cjJP/mYuGyMrjJ49jI04khId5Oufd3nFTUYBzQTIIVNI7/oAWdwXEfpwTF8HELFV/gz+WGYUBHCe3KHWD8rYvg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.0.3.tgz", + "integrity": "sha512-36EZzEAZj/Wi53qM9GQNPWaQ2YX6P+xbkTwlkXNwoMkRetJafGpxxZpi41hCoLVS3bqzw7/Ws9g1pzkOILos7Q==", "requires": { - "asn1js": "^3.0.3", - "bytestreamjs": "^1.0.29", - "pvutils": "^1.1.3" + "asn1js": "^3.0.5", + "bytestreamjs": "^2.0.0", + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" } }, "please-upgrade-node": { diff --git a/packages/cryptopro-cades/package.json b/packages/cryptopro-cades/package.json index cbd01ec..65e6a4a 100644 --- a/packages/cryptopro-cades/package.json +++ b/packages/cryptopro-cades/package.json @@ -13,13 +13,13 @@ "lint": "eslint ./src --fix --quiet" }, "dependencies": { - "buffer": "^6.0.3", - "pkijs": "^2.3.1", - "asn1js": "^3.0.2" + "buffer": "6.0.3", + "pkijs": "3.0.3", + "asn1js": "3.0.5" }, "devDependencies": { - "@types/node": "^17.0.33", - "copyfiles": "^2.4.1" + "@types/node": "17.0.36", + "copyfiles": "2.4.1" }, "version": "1.2.0" } \ No newline at end of file diff --git a/packages/cryptopro-cades/src/Certificate.ts b/packages/cryptopro-cades/src/Certificate.ts index 1c36715..ccca57d 100644 --- a/packages/cryptopro-cades/src/Certificate.ts +++ b/packages/cryptopro-cades/src/Certificate.ts @@ -1,3 +1,4 @@ +import { unwrap } from './api/internal/unwrap'; import { CAPICOM_ENCODING_TYPE } from './constants'; import { CryptoError } from './errors'; import { ICertificate } from './types'; @@ -157,49 +158,25 @@ export class Certificate { } const certificate = new Certificate(cert); - if (cert.SubjectName instanceof Promise) { - certificate.subjectName = await cert.SubjectName; - certificate.thumbprint = await cert.Thumbprint; - certificate.notAfter = await cert.ValidToDate; - certificate.notBefore = await cert.ValidFromDate; - certificate.certificateBase64Data = await cert.Export( - CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64 + certificate.subjectName = await unwrap(cert.SubjectName); + certificate.thumbprint = await unwrap(cert.Thumbprint); + certificate.notAfter = await unwrap(cert.ValidToDate); + certificate.notBefore = await unwrap(cert.ValidFromDate); + certificate.certificateBase64Data = await unwrap( + cert.Export(CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64) + ); + try { + certificate.hasPrivateKey = await unwrap(cert.HasPrivateKey()); + const oPrivateKey = await unwrap(cert.PrivateKey); + certificate.providerName = await unwrap(oPrivateKey.ProviderName); + certificate.providerType = await unwrap(oPrivateKey.ProviderType); + } catch (error) { + // ошибка не критична, просто создаем ошибку (в дебаге оно залогируется само) + CryptoError.createCadesError( + error, + `Ошибка получения информации о приватном ключе сертификата ${certificate.thumbprint}.` ); - try { - certificate.hasPrivateKey = await cert.HasPrivateKey(); - const oPrivateKey = await cert.PrivateKey; - certificate.providerName = await oPrivateKey.ProviderName; - certificate.providerType = await oPrivateKey.ProviderType; - } catch (error) { - // ошибка не критична, просто создаем ошибку (в дебаге оно залогируется само) - CryptoError.createCadesError( - error, - `Ошибка получения информации о приватном ключе сертификата ${certificate.thumbprint}.` - ); - certificate.hasPrivateKey = false; - } - } else { - certificate.subjectName = cert.SubjectName; - certificate.thumbprint = cert.Thumbprint as string; - certificate.notAfter = cert.ValidToDate as Date; - certificate.notBefore = cert.ValidFromDate as Date; - certificate.certificateBase64Data = cert.Export( - CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64 - ); - - try { - certificate.hasPrivateKey = cert.HasPrivateKey(); - const oPrivateKey = cert.PrivateKey; - certificate.providerName = oPrivateKey.ProviderName; - certificate.providerType = oPrivateKey.ProviderType; - } catch (error) { - // ошибка не критична, просто создаем ошибку (в дебаге оно залогируется само) - CryptoError.createCadesError( - error, - `Ошибка получения информации о приватном ключе сертификата ${certificate.thumbprint}.` - ); - certificate.hasPrivateKey = false; - } + certificate.hasPrivateKey = false; } parseCertificate(certificate); diff --git a/packages/cryptopro-cades/src/api/createObject.ts b/packages/cryptopro-cades/src/api/createObject.ts index d1a4d20..7cb17f8 100644 --- a/packages/cryptopro-cades/src/api/createObject.ts +++ b/packages/cryptopro-cades/src/api/createObject.ts @@ -1,8 +1,8 @@ import { CryptoError } from '../errors'; -import { deasync } from '../utils'; import { afterPluginLoaded } from './internal/afterPluginLoaded'; import { canAsync } from './internal/canAsync'; +import { unwrap } from './internal/unwrap'; /** * Создание криптографического объекта. @@ -12,7 +12,7 @@ import { canAsync } from './internal/canAsync'; * @returns {Promise} Созданный объект. */ export function createObject(objectIdentifier: string): Promise { - return afterPluginLoaded(() => { + return afterPluginLoaded(async () => { if (!objectIdentifier) { throw CryptoError.create( 'CBP-7', @@ -25,7 +25,7 @@ export function createObject(objectIdentifier: string): Promise { ? window.cadesplugin.CreateObjectAsync(objectIdentifier) : window.cadesplugin.CreateObject(objectIdentifier); - return deasync(object); + return await unwrap(object); } catch (error) { throw CryptoError.createCadesError( error, diff --git a/packages/cryptopro-cades/src/api/decrypt.ts b/packages/cryptopro-cades/src/api/decrypt.ts index 42cec0c..dd22ec5 100644 --- a/packages/cryptopro-cades/src/api/decrypt.ts +++ b/packages/cryptopro-cades/src/api/decrypt.ts @@ -8,6 +8,7 @@ import { outputDebug } from '../utils'; import { createObject } from './createObject'; import { afterPluginLoaded } from './internal/afterPluginLoaded'; import { setCryptoProperty } from './internal/setCryptoProperty'; +import { unwrap } from './internal/unwrap'; /** * Расшифровать данные. @@ -39,7 +40,7 @@ export function decrypt(encryptedData: ArrayBuffer | string): Promise { try { // в криптопро браузер плагине не поддерживается подпись/расшифровка бинарных данных, // поэтому подписываем предварительно конвертированный в Base64 - setCryptoProperty( + await setCryptoProperty( envelopedData, 'ContentEncoding', CADESCOM_BASE64_TO_BINARY @@ -54,15 +55,9 @@ export function decrypt(encryptedData: ArrayBuffer | string): Promise { try { // в криптопро браузер плагине не поддерживается подпись/расшифровка бинарных данных, // поэтому расшифровываем предварительно конвертированный в Base64 - const decryptResult = envelopedData.Decrypt(base64String); - if (decryptResult instanceof Promise) { - await decryptResult; - } + await unwrap(envelopedData.Decrypt(base64String)); - const decryptedData = - envelopedData.Content instanceof Promise - ? await envelopedData.Content - : envelopedData.Content; + const decryptedData = await unwrap(envelopedData.Content); logData.push({ decryptedData }); diff --git a/packages/cryptopro-cades/src/api/encrypt.ts b/packages/cryptopro-cades/src/api/encrypt.ts index ecb6834..c5bcb80 100644 --- a/packages/cryptopro-cades/src/api/encrypt.ts +++ b/packages/cryptopro-cades/src/api/encrypt.ts @@ -12,6 +12,7 @@ import { outputDebug } from '../utils'; import { createObject } from './createObject'; import { afterPluginLoaded } from './internal/afterPluginLoaded'; import { setCryptoProperty } from './internal/setCryptoProperty'; +import { unwrap } from './internal/unwrap'; /** * Зашировать данные на указанные сертификаты. @@ -53,12 +54,12 @@ export function encrypt( try { // в криптопро браузер плагине не поддерживается подпись/расшифровка бинарных данных, // поэтому подписываем предварительно конвертированный в Base64 - setCryptoProperty( + await setCryptoProperty( envelopedData, 'ContentEncoding', CADESCOM_BASE64_TO_BINARY ); - setCryptoProperty(envelopedData, 'Content', base64String); + await setCryptoProperty(envelopedData, 'Content', base64String); } catch (err) { throw CryptoError.createCadesError( err, @@ -67,10 +68,10 @@ export function encrypt( } try { - const recipients: IRecipients = await envelopedData.Recipients; + const recipients: IRecipients = await unwrap(envelopedData.Recipients); for (const recipientCertificate of recipientCertificates) { - await recipients.Add(recipientCertificate); + await unwrap(recipients.Add(recipientCertificate)); } } catch (error) { throw CryptoError.createCadesError( @@ -83,18 +84,13 @@ export function encrypt( // в криптопро браузер плагине не поддерживается подпись/расшифровка бинарных данных, // поэтому расшифровываем предварительно конвертированный в Base64 - const encryptResult = envelopedData.Encrypt( - CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64 + const encryptResult = await unwrap( + envelopedData.Encrypt(CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64) ); - const encryptedData = - encryptResult instanceof Promise - ? await encryptResult - : encryptResult; + logData.push({ encryptResult }); - logData.push({ encryptedData }); - - return encryptedData; + return encryptResult; } catch (error) { throw CryptoError.createCadesError( error, diff --git a/packages/cryptopro-cades/src/api/findCertificateByThumbprint.ts b/packages/cryptopro-cades/src/api/findCertificateByThumbprint.ts index 5e28d5b..23dabc3 100644 --- a/packages/cryptopro-cades/src/api/findCertificateByThumbprint.ts +++ b/packages/cryptopro-cades/src/api/findCertificateByThumbprint.ts @@ -1,8 +1,10 @@ import { CAPICOM_CERTIFICATE_FIND_TYPE } from '../constants'; import { Certificate } from '../Certificate'; import { CryptoError } from '../errors'; +import { IStore } from '../types'; import { openStore } from './openStore'; +import { unwrap } from './internal/unwrap'; /** * Поиск в хранилищах сертификата. @@ -17,20 +19,22 @@ export async function findCertificateByThumbprint( const errorMessage = 'Не указан отпечаток искомого сертификата.'; throw CryptoError.create('CBP-7', errorMessage, null, errorMessage); } + let store: IStore | null = null; try { - const cert = await ( - await ( - await ( - await openStore() - ).Certificates - ).Find( + store = await openStore(); + const certificates = await unwrap(store.Certificates); + const certFind = await unwrap( + certificates.Find( CAPICOM_CERTIFICATE_FIND_TYPE.CAPICOM_CERTIFICATE_FIND_SHA1_HASH, thumbprint ) - ).Item(1); + ); + const cert = await unwrap(certFind.Item(1)); return cert ? await Certificate.CreateFrom(cert) : undefined; } catch (err) { throw CryptoError.createCadesError(err, 'Ошибка получения сертификата.'); + } finally { + await unwrap(store?.Close()); } } diff --git a/packages/cryptopro-cades/src/api/getCertInfo.ts b/packages/cryptopro-cades/src/api/getCertInfo.ts index 8abe0b9..e085f43 100644 --- a/packages/cryptopro-cades/src/api/getCertInfo.ts +++ b/packages/cryptopro-cades/src/api/getCertInfo.ts @@ -1,7 +1,7 @@ import { ICertificate } from '../types'; import { CAPICOM_CERT_INFO_TYPE } from '../constants'; -import { canAsync } from './internal/canAsync'; +import { unwrap } from './internal/unwrap'; /** * Получение информации из Сертификата. @@ -14,14 +14,9 @@ import { canAsync } from './internal/canAsync'; * @throws {CryptoError} в случае ошибки. * @returns {string} запрошенная информация из сертификата. */ -export async function getCertInfo( +export function getCertInfo( cert: ICertificate, what: CAPICOM_CERT_INFO_TYPE ): Promise { - const result = cert.GetInfo(what); - - if (canAsync() && result instanceof Promise) { - return result; - } - return result; + return unwrap(cert.GetInfo(what)); } diff --git a/packages/cryptopro-cades/src/api/getCertificates.ts b/packages/cryptopro-cades/src/api/getCertificates.ts index d71e3c8..8e034a7 100644 --- a/packages/cryptopro-cades/src/api/getCertificates.ts +++ b/packages/cryptopro-cades/src/api/getCertificates.ts @@ -11,6 +11,7 @@ import { CryptoError } from '../errors'; import { afterPluginLoaded } from './internal/afterPluginLoaded'; import { openStore } from './openStore'; +import { unwrap } from './internal/unwrap'; /** * Кэш из запрошенных сертификатов. @@ -24,12 +25,16 @@ const certificatesCache = {}; * @returns {Promise} .Список сертификатов. */ async function getCertificatesFromStore(store: IStore): Promise { + if (!store) { + const errorMessage = 'Не задано хранилище сертификатов.'; + throw CryptoError.create('CBP-7', errorMessage, null, errorMessage); + } const result: Certificate[] = []; let certificates: ICertificates; let certificatesCount = 0; try { - certificates = await store.Certificates; - certificatesCount = await certificates.Count; + certificates = await unwrap(store.Certificates); + certificatesCount = await unwrap(certificates.Count); } catch (err) { throw CryptoError.createCadesError( err, @@ -40,8 +45,8 @@ async function getCertificatesFromStore(store: IStore): Promise { // проверяем пригодность и превращаем сертификаты в наш внутренний тип while (certificatesCount) { try { - const certBin: ICertificate = await certificates.Item( - certificatesCount-- + const certBin: ICertificate = await unwrap( + certificates.Item(certificatesCount--) ); const cert: Certificate = await Certificate.CreateFrom(certBin); @@ -60,7 +65,6 @@ async function getCertificatesFromStore(store: IStore): Promise { /** * Получить сертификаты из USB токенов. - * @param {STORE_TYPE} storeType Тип хранилища ключей. * @throws {CryptoError} в случае ошибки. * @returns {Promise} .Список сертификатов из USB токенов. */ @@ -76,7 +80,6 @@ async function ReadCertificatesFromUsbToken(): Promise { /** * Получить сертификаты из реестра. - * @param {STORE_TYPE} storeType Тип хранилища ключей. * @throws {CryptoError} в случае ошибки. * @returns {Promise} .Список сертификатов из реестра. */ diff --git a/packages/cryptopro-cades/src/api/getCryptoProviders.ts b/packages/cryptopro-cades/src/api/getCryptoProviders.ts index 25feab1..bf8bbc2 100644 --- a/packages/cryptopro-cades/src/api/getCryptoProviders.ts +++ b/packages/cryptopro-cades/src/api/getCryptoProviders.ts @@ -5,6 +5,7 @@ import pluginConfig from '../PluginConfig'; import { afterPluginLoaded } from './internal/afterPluginLoaded'; import { createObject } from './createObject'; +import { unwrap } from './internal/unwrap'; /** * Кэш из запрошенных сертификатов. @@ -36,16 +37,15 @@ export function getCryptoProviders( } of pluginConfig.CheckCryptoProviders) { try { const cadesAbout: IAbout = await createObject(CRYPTO_OBJECTS.about); - const cspVersion: IVersion = await cadesAbout.CSPVersion( - ProviderName, - ProviderType + const cspVersion: IVersion = await unwrap( + cadesAbout.CSPVersion(ProviderName, ProviderType) ); availableCryptoProviders.push({ ProviderName: ProviderName, ProviderType: ProviderType, - BuildVersion: await cspVersion.BuildVersion, - MajorVersion: await cspVersion.MajorVersion, - MinorVersion: await cspVersion.MinorVersion, + BuildVersion: await unwrap(cspVersion.BuildVersion), + MajorVersion: await unwrap(cspVersion.MajorVersion), + MinorVersion: await unwrap(cspVersion.MinorVersion), }); } catch (error) { logData.push({ diff --git a/packages/cryptopro-cades/src/api/getSystemInfo.ts b/packages/cryptopro-cades/src/api/getSystemInfo.ts index c0a1de6..633d97a 100644 --- a/packages/cryptopro-cades/src/api/getSystemInfo.ts +++ b/packages/cryptopro-cades/src/api/getSystemInfo.ts @@ -11,6 +11,7 @@ import { IAbout, ISystemInfo } from '../types'; import { afterPluginLoaded } from './internal/afterPluginLoaded'; import { createObject } from './createObject'; import { getCryptoProviders } from './getCryptoProviders'; +import { unwrap } from './internal/unwrap'; /** * Кэш информации о системе. @@ -68,13 +69,13 @@ export const getSystemInfo = (): Promise => { } try { - const pluginVersion = await cadesAbout.PluginVersion; + const pluginVersion = await unwrap(cadesAbout.PluginVersion); if (pluginVersion) { - sysInfo.cadesVersion = await pluginVersion.toString(); + sysInfo.cadesVersion = await unwrap(pluginVersion.toString()); } if (!sysInfo.cadesVersion) { - sysInfo.cadesVersion = await cadesAbout.Version; + sysInfo.cadesVersion = await unwrap(cadesAbout.Version); } } catch (error) { throw CryptoError.createCadesError( diff --git a/packages/cryptopro-cades/src/api/internal/setCryptoProperty.ts b/packages/cryptopro-cades/src/api/internal/setCryptoProperty.ts index 3d3415b..51250ab 100644 --- a/packages/cryptopro-cades/src/api/internal/setCryptoProperty.ts +++ b/packages/cryptopro-cades/src/api/internal/setCryptoProperty.ts @@ -1,5 +1,4 @@ import { CryptoError } from '../../errors'; -import { deasync } from '../../utils/deasync'; import { canAsync } from './canAsync'; @@ -18,14 +17,18 @@ interface ICryptoObject { * @param {*} value - значение для свойства. * @returns {void} . */ -export function setCryptoProperty( +export async function setCryptoProperty( obj: ICryptoObject, key: string, value: any -): void { +): Promise { try { if (canAsync()) { - deasync(obj[`propset_${key}`](value)); + const res = obj[`propset_${key}`](value); + + if (res instanceof Promise) { + await res; + } } else { // eslint-disable-next-line no-param-reassign obj[key] = value; diff --git a/packages/cryptopro-cades/src/api/internal/unwrap.ts b/packages/cryptopro-cades/src/api/internal/unwrap.ts new file mode 100644 index 0000000..d8cda77 --- /dev/null +++ b/packages/cryptopro-cades/src/api/internal/unwrap.ts @@ -0,0 +1,16 @@ +import { WithOptionalPromise } from '../../types'; + +import { canAsync } from './canAsync'; + +/** + * Функция убирающая обёртку WithOptionalPromise при необходимости. + * @param optionalPromise Тип с опциональным промисом. + * @returns {Promise} Промис возвращающий реальное значение. + */ +export function unwrap(optionalPromise: WithOptionalPromise): Promise { + if (canAsync() && optionalPromise instanceof Promise) { + return optionalPromise; + } else { + return Promise.resolve(optionalPromise); + } +} diff --git a/packages/cryptopro-cades/src/api/openStore.ts b/packages/cryptopro-cades/src/api/openStore.ts index ce51dc2..c52a5a3 100644 --- a/packages/cryptopro-cades/src/api/openStore.ts +++ b/packages/cryptopro-cades/src/api/openStore.ts @@ -9,6 +9,7 @@ import { import { createObject } from './createObject'; import { afterPluginLoaded } from './internal/afterPluginLoaded'; +import { unwrap } from './internal/unwrap'; /** * Открывает хранилище с сертификатами. @@ -25,19 +26,18 @@ export function openStore( openMode: CAPICOM_STORE_OPEN_MODE = CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_EXISTING_ONLY ): Promise { return afterPluginLoaded(async () => { - const store: IStore = await createObject(CRYPTO_OBJECTS.store); + let store: IStore = await createObject(CRYPTO_OBJECTS.store); try { - const res = store.Open(storeLocation, storeName, openMode); + await unwrap(store.Open(storeLocation, storeName, openMode)); - await res; + return store; } catch (err) { + await unwrap(store?.Close()); throw CryptoError.createCadesError( err, 'Ошибка открытия хранилища сертификатов.' ); } - - return store; })(); } diff --git a/packages/cryptopro-cades/src/api/sign.ts b/packages/cryptopro-cades/src/api/sign.ts index c59fb94..391ed86 100644 --- a/packages/cryptopro-cades/src/api/sign.ts +++ b/packages/cryptopro-cades/src/api/sign.ts @@ -15,6 +15,7 @@ import { afterPluginLoaded } from './internal/afterPluginLoaded'; import { createObject } from './createObject'; import { setCryptoProperty } from './internal/setCryptoProperty'; import { validateCertificate } from './validateCertificate'; +import { unwrap } from './internal/unwrap'; /** * Подписать входные данные указанным сертификатом в формате CMS. @@ -87,23 +88,23 @@ export function sign( // заполнение параметров для подписи try { - setCryptoProperty(signer, 'Certificate', cert); + await setCryptoProperty(signer, 'Certificate', cert); if (includeCertChain) { - setCryptoProperty( + await setCryptoProperty( signer, 'Options', CAPICOM_CERTIFICATE_INCLUDE_OPTION.CAPICOM_CERTIFICATE_INCLUDE_WHOLE_CHAIN ); } - setCryptoProperty( + await setCryptoProperty( signedData, 'ContentEncoding', CADESCOM_BASE64_TO_BINARY ); // в криптопро браузер плагине не поддерживается подпись/расшифровка бинарных данных, // поэтому подписываем предварительно конвертированный в Base64 - setCryptoProperty(signedData, 'Content', base64String); + await setCryptoProperty(signedData, 'Content', base64String); } catch (error) { throw CryptoError.createCadesError( error, @@ -112,18 +113,17 @@ export function sign( } try { - const signResult = signedData.SignCades( - signer, - CADESCOM_CADES_TYPE.CADESCOM_CADES_BES, - detach + const signResult = await unwrap( + signedData.SignCades( + signer, + CADESCOM_CADES_TYPE.CADESCOM_CADES_BES, + detach + ) ); - const sig = - signResult instanceof Promise ? await signResult : signResult; - - logData.push({ sig }); + logData.push({ signResult }); - return sig; + return signResult; } catch (error) { throw CryptoError.createCadesError( error, diff --git a/packages/cryptopro-cades/src/api/signXml.ts b/packages/cryptopro-cades/src/api/signXml.ts index fcc11f0..4c224ef 100644 --- a/packages/cryptopro-cades/src/api/signXml.ts +++ b/packages/cryptopro-cades/src/api/signXml.ts @@ -15,6 +15,7 @@ import { afterPluginLoaded } from './internal/afterPluginLoaded'; import { createObject } from './createObject'; import { setCryptoProperty } from './internal/setCryptoProperty'; import { validateCertificate } from './validateCertificate'; +import { unwrap } from './internal/unwrap'; /** * Получить алгоритм вычисления подписи для Xml. @@ -124,24 +125,24 @@ export const signXml = ( // заполнение параметров для подписи try { - setCryptoProperty(signer, 'Certificate', cert.certificateBin); + await setCryptoProperty(signer, 'Certificate', cert.certificateBin); // в криптопро браузер плагине не поддерживается подпись/расшифровка бинарных данных, // поэтому подписываем предварительно конвертированный в Base64 - setCryptoProperty(signedData, 'Content', base64String); + await setCryptoProperty(signedData, 'Content', base64String); // указываем тип подписи - setCryptoProperty(signedData, 'SignatureType', xmlSignatureType); + await setCryptoProperty(signedData, 'SignatureType', xmlSignatureType); // указываем алгоритм подписи - setCryptoProperty( + await setCryptoProperty( signedData, 'SignatureMethod', getXmlSignAlgorithmType(cert) ); // указываем алгоритм хэширования - setCryptoProperty( + await setCryptoProperty( signedData, 'DigestMethod', getXmlHashAlgorithmType(cert) @@ -154,14 +155,11 @@ export const signXml = ( } try { - const signResult = signedData.Sign(signer); + const signResult = await unwrap(signedData.Sign(signer)); - const sig = - signResult instanceof Promise ? await signResult : signResult; + logData.push({ signResult }); - logData.push({ sig }); - - return sig; + return signResult; } catch (error) { throw CryptoError.createCadesError( error, diff --git a/packages/cryptopro-cades/src/errors/CryptoError.ts b/packages/cryptopro-cades/src/errors/CryptoError.ts index 791b1be..b46926f 100644 --- a/packages/cryptopro-cades/src/errors/CryptoError.ts +++ b/packages/cryptopro-cades/src/errors/CryptoError.ts @@ -1,7 +1,11 @@ import { getLastError } from '../api/getLastError'; import { IAnyError, ICryptoError, IErrorObject } from '../types'; -import { CRYPTO_PRO_ERRORS, PLUGIN_ERRORS } from './errorCodes'; +import { + CRYPTO_PRO_ERRORS, + ERRORS_WITHOUT_CODE, + PLUGIN_ERRORS, +} from './errorCodes'; import PluginConfig from './../PluginConfig'; /** @@ -61,14 +65,10 @@ export class CryptoError extends Error implements ICryptoError { * @param err Объект ошибки. */ private constructor(err: IErrorObject | null) { - super(); + super(err?.message); this.InnerError = err; this.message = err?.message; - this.stack = err?.stack; - - if (Error.captureStackTrace) { - Error.captureStackTrace(this, CryptoError); - } + Object.setPrototypeOf(this, CryptoError.prototype); } /** @@ -92,12 +92,19 @@ export class CryptoError extends Error implements ICryptoError { const cryptoError = new CryptoError(err); err = err as IAnyError; - cryptoError.code = err.code || CryptoError._extractCode(err); + const errCode = ERRORS_WITHOUT_CODE[err.message]; + + if (errCode) { + err.code = errCode; + } + + cryptoError.code = err.code ?? CryptoError._extractCode(err); if (typeof cryptoError.code === 'string' && cryptoError.code.length > 16) { cryptoError.code = ''; } + let extractedMsg = ''; - if (!err.message) { + if (err.message) { extractedMsg = cryptoError._extractMessage(err); } cryptoError.title = title ?? err.message ?? extractedMsg; @@ -105,8 +112,9 @@ export class CryptoError extends Error implements ICryptoError { cryptoError.type += ' < @astral/cades-plugin'; cryptoError.message = CRYPTO_PRO_ERRORS.find((res) => res.code == cryptoError.code)?.message ?? - err.message ?? - extractedMsg; + PLUGIN_ERRORS[cryptoError.code] ?? + extractedMsg ?? + err.message; PluginConfig.notifyError(cryptoError); @@ -150,7 +158,7 @@ export class CryptoError extends Error implements ICryptoError { (err.message?.match(/\(?0x.{2,8}\)?/) || err.message?.match(CryptoError._RULE_MATCHING_CODE) || [])[0] || ''; - return result.replace(/[()]/g, ''); + return result.replace(/[()]/g, '').trim(); } /** @@ -161,8 +169,9 @@ export class CryptoError extends Error implements ICryptoError { */ private _extractMessage(err: IErrorObject): string | any { const fullErrorData = getLastError(err); - const msg = typeof err === 'string' ? err : err.message; - return (fullErrorData?.message || msg || '').replace(` (${this.code})`, ''); + return (fullErrorData?.message || err.message || '') + .replace(`(${this.code})`, '') + .trim(); } } diff --git a/packages/cryptopro-cades/src/errors/errorCodes.ts b/packages/cryptopro-cades/src/errors/errorCodes.ts index 681f182..773a95e 100644 --- a/packages/cryptopro-cades/src/errors/errorCodes.ts +++ b/packages/cryptopro-cades/src/errors/errorCodes.ts @@ -106,11 +106,6 @@ export const CRYPTO_PRO_ERRORS = Object.freeze([ code: '0xC2110126', message: 'Сертификат отозван.', }, - { - code: '0x80090010', - message: - 'Отказано в доступе. Убедитесь, что подпись производится действующим сертификатом.', - }, { code: '0x000004C7', message: @@ -129,6 +124,22 @@ export const CRYPTO_PRO_ERRORS = Object.freeze([ message: 'Ошибка разбора XML файла. Документ имеет некорректный формат.', // original: '0x800705B9: Windows was unable to parse the requested XML data. (0x800705B9)' }, + { + code: '0x80090010', + message: 'Срок действия требуемого сертификата истёк или ещё не наступил.', + // original: 'Отказано в доступе. (0x80090010)' + }, + { + code: '0x800B0101', + message: 'Срок действия требуемого сертификата истёк или ещё не наступил.', + // original: 'Истек/не наступил срок действия требуемого сертификата при проверке по системным часам или по отметке времени в подписанном файле. (0x800B0101)' + }, + { + code: '0x8010006C', + message: + 'Доступ к сертификату заблокирован, так как исчерпано число попыток ввести правильный PIN-код.', + // original: 'The card cannot be accessed because the maximum number of PIN entry attempts has been reached. (0x8010006C)' + }, ]); /** @@ -149,4 +160,12 @@ export const PLUGIN_ERRORS = Object.freeze({ 'CBP-8': 'Не установлен ни один криптопровайдер.', 'CBP-9': 'Неизвестный алгоритм ключа.', 'CBP-10': 'Не удалось прочитать данные сертификата.', + 'CBP-11': 'Потеряно соединение с КриптоПро ЭЦП Browser plug-in.', +}); + +/** + * Список текстов ошибок у котороых нет кода ошибок. + */ +export const ERRORS_WITHOUT_CODE = Object.freeze({ + 'Lost connection to extension': 'CBP-11', }); diff --git a/packages/cryptopro-cades/src/utils/certificateParser.ts b/packages/cryptopro-cades/src/utils/certificateParser.ts index e8d5298..b8f088f 100644 --- a/packages/cryptopro-cades/src/utils/certificateParser.ts +++ b/packages/cryptopro-cades/src/utils/certificateParser.ts @@ -1,6 +1,5 @@ import { Buffer } from 'buffer'; -// @ts-ignore // TOOD: разобраться почему не находит модуль. import { Certificate as x509Certificate } from 'pkijs'; import { fromBER } from 'asn1js'; @@ -63,6 +62,7 @@ export function parseCertificate(certificate: Certificate) { new Uint8Array(Buffer.from(certificate.certificateBase64Data, 'base64')) .buffer ); + const parsedCert = new x509Certificate({ schema: asn1.result }); const publishKeyAlgorithm = @@ -70,7 +70,7 @@ export function parseCertificate(certificate: Certificate) { certificate.algorithm = publishKeyAlgorithm; certificate.isGost = GOST_KEY_ALGORITHM_OIDS.includes(publishKeyAlgorithm); - const subjectKeyIdentifierExtension = parsedCert.extensions.find( + const subjectKeyIdentifierExtension = parsedCert.extensions?.find( ({ extnID }: { extnID: string }): boolean => extnID === subjectKeyIdExtensionOid ); diff --git a/packages/cryptopro-cades/src/utils/deasync.ts b/packages/cryptopro-cades/src/utils/deasync.ts deleted file mode 100644 index f9ad7cf..0000000 --- a/packages/cryptopro-cades/src/utils/deasync.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { canAsync } from '../api/internal/canAsync'; -import { CryptoError } from '../errors'; - -/** - * Обёртка для вызова асинхронной функции в синхронном контексте. - * @param {Promise<*>} fn - функция или иное свойство от Promise. - * @returns {*|Promise<*>} . - */ -export function deasync(fn: Promise): any { - let result = fn; - if (canAsync()) { - (async () => { - try { - result = await fn; - } catch (err) { - throw CryptoError.create('CBP-0', 'deasync', err); - } - })(); - } - return result; -} diff --git a/packages/cryptopro-cades/src/utils/index.ts b/packages/cryptopro-cades/src/utils/index.ts index fa50ee0..f260466 100644 --- a/packages/cryptopro-cades/src/utils/index.ts +++ b/packages/cryptopro-cades/src/utils/index.ts @@ -1,4 +1,3 @@ export * from './bufferToHex'; export * from './certificateParser'; -export * from './deasync'; export * from './output'; diff --git a/packages/cryptopro-cades/src/utils/output.ts b/packages/cryptopro-cades/src/utils/output.ts index 2754431..2049800 100644 --- a/packages/cryptopro-cades/src/utils/output.ts +++ b/packages/cryptopro-cades/src/utils/output.ts @@ -5,7 +5,7 @@ import PluginConfig from '../PluginConfig'; * @param args данные. * @returns Объект готовый для логирования. */ -const prepareArgs = (args: (string | any)[]): any => { +const prepareArgs = (args: (string | any)[]): any[] => { const context = '[cryptopro-cades]: '; if (typeof args[0] == 'string') { @@ -32,5 +32,10 @@ export const outputDebug = (...args: (string | any)[]): void => { * @param args параметры. */ export const outputError = (...args: (string | any)[]): void => { - console.error(...prepareArgs(args)); + const data = prepareArgs(args); + + // первый элемент- контекст, второй и далее - объекты ошибки + const err = data.length === 2 ? data[1] : data.slice(1, data.length); + + console.error({ err }, ...data); // логируем текст ошибки и прикрепляем саму ошибку. };