mirror of
https://github.com/crypto-pro-web/crypto-pro-js.git
synced 2024-11-24 00:55:00 +03:00
add attached and detached signature
This commit is contained in:
parent
c0566b7654
commit
a81294a60a
88
src/api/addAttachedSignature.test.ts
Normal file
88
src/api/addAttachedSignature.test.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import 'cadesplugin';
|
||||||
|
import { rawCertificates, parsedCertificates } from '../__mocks__/certificates';
|
||||||
|
import { createAttachedSignature } from './createAttachedSignature';
|
||||||
|
import { _getCadesCert } from '../helpers/_getCadesCert';
|
||||||
|
import { addAttachedSignature } from './addAttachedSignature';
|
||||||
|
|
||||||
|
const [rawCertificateMock] = rawCertificates;
|
||||||
|
const [parsedCertificateMock] = parsedCertificates;
|
||||||
|
|
||||||
|
jest.mock('../helpers/_getCadesCert', () => ({ _getCadesCert: jest.fn(() => rawCertificateMock) }));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(_getCadesCert as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
const executionSteps = [
|
||||||
|
Symbol('step 0'),
|
||||||
|
Symbol('step 1'),
|
||||||
|
Symbol('step 2'),
|
||||||
|
Symbol('step 3'),
|
||||||
|
Symbol('step 4'),
|
||||||
|
Symbol('step 5'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const executionFlow = {
|
||||||
|
[executionSteps[0]]: {
|
||||||
|
propset_Name: jest.fn(),
|
||||||
|
propset_Value: jest.fn(),
|
||||||
|
},
|
||||||
|
[executionSteps[1]]: {
|
||||||
|
propset_ContentEncoding: jest.fn(),
|
||||||
|
propset_Content: jest.fn(),
|
||||||
|
SignCades: jest.fn(() => executionSteps[4]),
|
||||||
|
CoSignCades: jest.fn(() => executionSteps[5]),
|
||||||
|
},
|
||||||
|
[executionSteps[2]]: {
|
||||||
|
propset_Certificate: jest.fn(),
|
||||||
|
AuthenticatedAttributes2: executionSteps[3],
|
||||||
|
propset_Options: jest.fn(),
|
||||||
|
},
|
||||||
|
[executionSteps[3]]: {
|
||||||
|
Add: jest.fn(),
|
||||||
|
},
|
||||||
|
[executionSteps[4]]: 'signature',
|
||||||
|
[executionSteps[5]]: 'newSignature',
|
||||||
|
};
|
||||||
|
|
||||||
|
window.cadesplugin.__defineExecutionFlow(executionFlow);
|
||||||
|
window.cadesplugin.CreateObjectAsync.mockImplementation((object) => {
|
||||||
|
switch (object) {
|
||||||
|
case 'CADESCOM.CPAttribute':
|
||||||
|
return executionSteps[0];
|
||||||
|
case 'CAdESCOM.CadesSignedData':
|
||||||
|
return executionSteps[1];
|
||||||
|
case 'CAdESCOM.CPSigner':
|
||||||
|
return executionSteps[2];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addAttachedSignature', () => {
|
||||||
|
test('uses Buffer to encrypt the message', async () => {
|
||||||
|
const originalBufferFrom = global.Buffer.from;
|
||||||
|
|
||||||
|
(global.Buffer.from as jest.Mock) = jest.fn(() => ({
|
||||||
|
toString: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
await createAttachedSignature(parsedCertificateMock.thumbprint, 'message');
|
||||||
|
await addAttachedSignature(parsedCertificateMock.thumbprint, 'message');
|
||||||
|
|
||||||
|
expect(global.Buffer.from).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
global.Buffer.from = originalBufferFrom;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('uses specified certificate', async () => {
|
||||||
|
await addAttachedSignature(parsedCertificateMock.thumbprint, 'message');
|
||||||
|
|
||||||
|
expect(_getCadesCert).toHaveBeenCalledWith(parsedCertificateMock.thumbprint);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns new signature', async () => {
|
||||||
|
await createAttachedSignature(parsedCertificateMock.thumbprint, 'message');
|
||||||
|
const signature = await addAttachedSignature(parsedCertificateMock.thumbprint, 'message');
|
||||||
|
|
||||||
|
expect(signature).toEqual('newSignature');
|
||||||
|
});
|
||||||
|
});
|
86
src/api/addAttachedSignature.ts
Normal file
86
src/api/addAttachedSignature.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { CADESCOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME } from '../constants';
|
||||||
|
import { _afterPluginsLoaded } from '../helpers/_afterPluginsLoaded';
|
||||||
|
import { _extractMeaningfulErrorMessage } from '../helpers/_extractMeaningfulErrorMessage';
|
||||||
|
import { __cadesAsyncToken__, __createCadesPluginObject__, _generateCadesFn } from '../helpers/_generateCadesFn';
|
||||||
|
import { _getCadesCert } from '../helpers/_getCadesCert';
|
||||||
|
import { _getDateObj } from '../helpers/_getDateObj';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создает присоединенную подпись сообщения по отпечатку сертификата
|
||||||
|
*
|
||||||
|
* @param thumbprint - отпечаток сертификата
|
||||||
|
* @param message - подписываемое сообщение
|
||||||
|
* @returns подпись в формате PKCS#7
|
||||||
|
*/
|
||||||
|
export const addAttachedSignature = _afterPluginsLoaded(
|
||||||
|
async (thumbprint: string, unencryptedMessage: string | ArrayBuffer): Promise<string> => {
|
||||||
|
const { cadesplugin } = window;
|
||||||
|
const cadesCertificate = await _getCadesCert(thumbprint);
|
||||||
|
|
||||||
|
return eval(
|
||||||
|
_generateCadesFn(function addAttachedSignature(): string {
|
||||||
|
let cadesAttrs;
|
||||||
|
let cadesSignedData;
|
||||||
|
let cadesSigner;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cadesAttrs = __cadesAsyncToken__ + __createCadesPluginObject__('CADESCOM.CPAttribute');
|
||||||
|
cadesSignedData = __cadesAsyncToken__ + __createCadesPluginObject__('CAdESCOM.CadesSignedData');
|
||||||
|
cadesSigner = __cadesAsyncToken__ + __createCadesPluginObject__('CAdESCOM.CPSigner');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при инициализации подписи');
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime = _getDateObj(new Date());
|
||||||
|
|
||||||
|
try {
|
||||||
|
void (__cadesAsyncToken__ + cadesAttrs.propset_Name(CADESCOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME));
|
||||||
|
void (__cadesAsyncToken__ + cadesAttrs.propset_Value(currentTime));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при установке времени подписи');
|
||||||
|
}
|
||||||
|
|
||||||
|
let messageBase64;
|
||||||
|
|
||||||
|
try {
|
||||||
|
messageBase64 = Buffer.from(unencryptedMessage).toString('base64');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error('Ошибка при преобразовании сообщения в Base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
let cadesAuthAttrs;
|
||||||
|
|
||||||
|
try {
|
||||||
|
void (__cadesAsyncToken__ + cadesSigner.propset_Certificate(cadesCertificate));
|
||||||
|
cadesAuthAttrs = __cadesAsyncToken__ + cadesSigner.AuthenticatedAttributes2;
|
||||||
|
void (__cadesAsyncToken__ + cadesAuthAttrs.Add(cadesAttrs));
|
||||||
|
void (__cadesAsyncToken__ + cadesSignedData.propset_ContentEncoding(cadesplugin.CADESCOM_BASE64_TO_BINARY));
|
||||||
|
void (__cadesAsyncToken__ + cadesSignedData.propset_Content(messageBase64));
|
||||||
|
void (__cadesAsyncToken__ + cadesSigner.propset_Options(cadesplugin.CAPICOM_CERTIFICATE_INCLUDE_WHOLE_CHAIN));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при указании данных для подписи');
|
||||||
|
}
|
||||||
|
|
||||||
|
let signature: string;
|
||||||
|
|
||||||
|
try {
|
||||||
|
signature = __cadesAsyncToken__ + cadesSignedData.CoSignCades(cadesSigner, cadesplugin.CADESCOM_PKCS7_TYPE);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при подписании данных');
|
||||||
|
}
|
||||||
|
|
||||||
|
return signature;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
81
src/api/addDetachedSignature.test.ts
Normal file
81
src/api/addDetachedSignature.test.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import 'cadesplugin';
|
||||||
|
import { rawCertificates, parsedCertificates } from '../__mocks__/certificates';
|
||||||
|
import { createDetachedSignature } from './createDetachedSignature';
|
||||||
|
import { _getCadesCert } from '../helpers/_getCadesCert';
|
||||||
|
import { addDetachedSignature } from "./addDetachedSignature";
|
||||||
|
|
||||||
|
const [rawCertificateMock] = rawCertificates;
|
||||||
|
const [parsedCertificateMock] = parsedCertificates;
|
||||||
|
|
||||||
|
jest.mock('../helpers/_getCadesCert', () => ({ _getCadesCert: jest.fn(() => rawCertificateMock) }));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(_getCadesCert as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
const executionSteps = [
|
||||||
|
Symbol('step 0'),
|
||||||
|
Symbol('step 1'),
|
||||||
|
Symbol('step 2'),
|
||||||
|
Symbol('step 3'),
|
||||||
|
Symbol('step 4'),
|
||||||
|
Symbol('step 5'),
|
||||||
|
Symbol('step 6'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const executionFlow = {
|
||||||
|
[executionSteps[0]]: {
|
||||||
|
propset_Name: jest.fn(),
|
||||||
|
propset_Value: jest.fn(),
|
||||||
|
},
|
||||||
|
[executionSteps[1]]: {
|
||||||
|
propset_ContentEncoding: jest.fn(),
|
||||||
|
propset_Content: jest.fn(),
|
||||||
|
SignHash: jest.fn(() => executionSteps[4]),
|
||||||
|
CoSignHash: jest.fn(() => executionSteps[6]),
|
||||||
|
},
|
||||||
|
[executionSteps[2]]: {
|
||||||
|
propset_Certificate: jest.fn(),
|
||||||
|
AuthenticatedAttributes2: executionSteps[3],
|
||||||
|
propset_Options: jest.fn(),
|
||||||
|
},
|
||||||
|
[executionSteps[3]]: {
|
||||||
|
Add: jest.fn(),
|
||||||
|
},
|
||||||
|
[executionSteps[4]]: 'signature',
|
||||||
|
[executionSteps[5]]: {
|
||||||
|
propset_Algorithm: jest.fn(),
|
||||||
|
SetHashValue: jest.fn(),
|
||||||
|
},
|
||||||
|
[executionSteps[6]]: 'newSignature',
|
||||||
|
};
|
||||||
|
|
||||||
|
window.cadesplugin.__defineExecutionFlow(executionFlow);
|
||||||
|
window.cadesplugin.CreateObjectAsync.mockImplementation((object) => {
|
||||||
|
switch (object) {
|
||||||
|
case 'CADESCOM.CPAttribute':
|
||||||
|
return executionSteps[0];
|
||||||
|
case 'CAdESCOM.CadesSignedData':
|
||||||
|
return executionSteps[1];
|
||||||
|
case 'CAdESCOM.CPSigner':
|
||||||
|
return executionSteps[2];
|
||||||
|
case 'CAdESCOM.HashedData':
|
||||||
|
return executionSteps[5];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addDetachedSignature', () => {
|
||||||
|
test('uses specified certificate', async () => {
|
||||||
|
const signature = await createDetachedSignature(parsedCertificateMock.thumbprint, 'message');
|
||||||
|
await addDetachedSignature(parsedCertificateMock.thumbprint, signature);
|
||||||
|
|
||||||
|
expect(_getCadesCert).toHaveBeenCalledWith(parsedCertificateMock.thumbprint);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns new signature', async () => {
|
||||||
|
let signature = await createDetachedSignature(parsedCertificateMock.thumbprint, 'message');
|
||||||
|
signature = await addDetachedSignature(parsedCertificateMock.thumbprint, signature);
|
||||||
|
|
||||||
|
expect(signature).toEqual('newSignature');
|
||||||
|
});
|
||||||
|
});
|
90
src/api/addDetachedSignature.ts
Normal file
90
src/api/addDetachedSignature.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { CADESCOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME } from '../constants';
|
||||||
|
import { _afterPluginsLoaded } from '../helpers/_afterPluginsLoaded';
|
||||||
|
import { _extractMeaningfulErrorMessage } from '../helpers/_extractMeaningfulErrorMessage';
|
||||||
|
import { __cadesAsyncToken__, __createCadesPluginObject__, _generateCadesFn } from '../helpers/_generateCadesFn';
|
||||||
|
import { _getCadesCert } from '../helpers/_getCadesCert';
|
||||||
|
import { _getDateObj } from '../helpers/_getDateObj';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создает отсоединенную подпись хеша по отпечатку сертификата
|
||||||
|
*
|
||||||
|
* @param thumbprint - отпечаток сертификата
|
||||||
|
* @param messageHash - хеш подписываемого сообщения, сгенерированный по ГОСТ Р 34.11-2012 256 бит
|
||||||
|
* @returns подпись в формате PKCS#7
|
||||||
|
*/
|
||||||
|
export const addDetachedSignature = _afterPluginsLoaded(
|
||||||
|
async (thumbprint: string, messageHash: string): Promise<string> => {
|
||||||
|
const { cadesplugin } = window;
|
||||||
|
const cadesCertificate = await _getCadesCert(thumbprint);
|
||||||
|
|
||||||
|
return eval(
|
||||||
|
_generateCadesFn(function addDetachedSignature(): string {
|
||||||
|
let cadesAttrs;
|
||||||
|
let cadesHashedData;
|
||||||
|
let cadesSignedData;
|
||||||
|
let cadesSigner;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cadesAttrs = __cadesAsyncToken__ + __createCadesPluginObject__('CADESCOM.CPAttribute');
|
||||||
|
cadesHashedData = __cadesAsyncToken__ + __createCadesPluginObject__('CAdESCOM.HashedData');
|
||||||
|
cadesSignedData = __cadesAsyncToken__ + __createCadesPluginObject__('CAdESCOM.CadesSignedData');
|
||||||
|
cadesSigner = __cadesAsyncToken__ + __createCadesPluginObject__('CAdESCOM.CPSigner');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при инициализации подписи');
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime = _getDateObj(new Date());
|
||||||
|
|
||||||
|
try {
|
||||||
|
void (__cadesAsyncToken__ + cadesAttrs.propset_Name(CADESCOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME));
|
||||||
|
void (__cadesAsyncToken__ + cadesAttrs.propset_Value(currentTime));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при установке времени подписи');
|
||||||
|
}
|
||||||
|
|
||||||
|
let cadesAuthAttrs;
|
||||||
|
|
||||||
|
try {
|
||||||
|
void (__cadesAsyncToken__ + cadesSigner.propset_Certificate(cadesCertificate));
|
||||||
|
cadesAuthAttrs = __cadesAsyncToken__ + cadesSigner.AuthenticatedAttributes2;
|
||||||
|
void (__cadesAsyncToken__ + cadesAuthAttrs.Add(cadesAttrs));
|
||||||
|
void (__cadesAsyncToken__ + cadesSigner.propset_Options(cadesplugin.CAPICOM_CERTIFICATE_INCLUDE_WHOLE_CHAIN));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при установке сертификата');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
void (
|
||||||
|
__cadesAsyncToken__ +
|
||||||
|
cadesHashedData.propset_Algorithm(cadesplugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256)
|
||||||
|
);
|
||||||
|
void (__cadesAsyncToken__ + cadesHashedData.SetHashValue(messageHash));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при установке хеша');
|
||||||
|
}
|
||||||
|
|
||||||
|
let signature: string;
|
||||||
|
|
||||||
|
try {
|
||||||
|
signature =
|
||||||
|
__cadesAsyncToken__ +
|
||||||
|
cadesSignedData.CoSignHash(cadesHashedData, cadesSigner, cadesplugin.CADESCOM_PKCS7_TYPE);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при подписании данных');
|
||||||
|
}
|
||||||
|
|
||||||
|
return signature;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user