Norvell's Public Machinations

    @qiwitech/cryptopro

    0.1.2 • Public • Published

    Web Cryptography and Key Discovery API wrapper for Crypto-Pro CAdES browser plugin

    This package exports the following.

    1. High-level Web Cryptography and WebCrypto Key Discovery API with essential methods.
    2. Mid-level wrapper classes for essential Crypto-Pro CAdES API objects.
    3. Low-level API for communicating with the Crypto-Pro CAdES browser plugin.

    To use plugin, you should load its vendor script cadesplugin_api.js which isn't included to this package.

    <script type="text/javascript" src="https://www.cryptopro.ru/sites/default/files/products/cades/cadesplugin_api.js"></script>

    Differences from W3 specification

    Please note that Crypto-Pro client software relies on X.509 certificates rather than PKI cryptography keys. At the same time, Web Cryptography API does not define any methods to manipulate X.509 certificates. So it's impossible to conform the current W3 specification by means of Crypto-Pro CAdES browser plugin.

    The NamedCryptoKey and CryptoKey instances represent X.509 certificates and reference internal plugin objects.

    The SubtleCrypto.generateKey and SubtleCrypto.exportKey may be used to generate HMAC keys and PKCS#10 Certificate Signing Requests, while SubtleCrypto.importKey is able to import X.509 certificates.

    The SubtleCrypto.verify resolves to true for successfully verified signatures and to false otherwise. But it may reject in case of any other error, e.g. corrupted CAdES message or any issues with certificate chain.

    Web Cryptography API

    import { createSubtleCrypto, createCryptoKeys, createHelper, getPlugin } form '@qiwitech/cryptopro';
    
    const plugin = await getPlugin(window),
    	helper = await createHelper(plugin),
    	subtle = await createSubtleCrypto(helper),
    	cryptokeys = await createCryptoKeys(helper);

    digest

    digest(algo: AlgorithmIdentifier, buffer: ArrayBuffer): Promise<ArrayBuffer>

    exportKey

    exportKey(format: KeyFormat, key: CryptoKey | NamedCryptoKey): Promise<ArrayBuffer>

    generateKey

    generateKey(algorithm: AlgorithmIdentifier, extractable: bool, keyUsages: Array<KeyUsage>): Promise<ArrayBuffer>

    importKey

    importKey(format: KeyFormat, keyData: ArrayBuffer, algorithm: AlgorithmIdentifier, extractable: bool, keyUsages: Array<KeyUsage>): Promise<CryptoKey>

    sign

    sign(algo: AlgorithmIdentifier, key: CryptoKey | NamedCryptoKey, buffer: ArrayBuffer): Promise<ArrayBuffer>

    verify

    verify(algo: AlgorithmIdentifier, key: CryptoKey | NamedCryptoKey, signature: ArrayBuffer, buffer: ArrayBuffer): Promise<void>

    WebCrypto Key Discovery API

    getKeyById

    getKeyById(id: string): Promise<?NamedCryptoKey>

    getKeyByName

    getKeyByName(name: string): Promise<?NamedCryptoKey>

    getKeyByExtendedKeyUsage

    getKeyByExtendedKeyUsage(oid: string): Promise<?NamedCryptoKey>

    getKeysByExtendedKeyUsages

    getKeysByExtendedKeyUsages(oids: Array<string>): Promise<Array<NamedCryptoKey>>

    API helper

    createHelper

    createHelper(plugin: Plugin): Promise<Helper>

    Note that cadesplugin is actually an instance of Promise with injected methods and constants, which resolves to undefined instead of actual API object. So it's recommended to use getPlugin helper function.

    import { createHelper, getPlugin } form '@qiwitech/cryptopro';
    
    const helper = await createHelper(
    	await getPlugin(window)
    );

    It's possible to directly pass a cadesplugin reference to createHelper.

    import { createHelper } form '@qiwitech/cryptopro';
    
    window.cadesplugin.then(
    	() => createHelper(window.cadesplugin)
    ).then(
    	(helper) => ...
    );

    createSubtleCrypto

    createSubtleCrypto(helper: Helper): Promise<SubtleCrypto>

    createCryptoKeys

    createCryptoKeys(helper: Helper): Promise<CryptoKeys>

    Wrapper classes

    Wrapper classes utilize API similar to Node.js Crypto module. It's generally not recommended to use them, but they could be useful for special cases not covered by high-level Web Cryptography API.

    There are helper functions available to create instance of any class.

    const hash = await helper.createHash(helper.plugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256);
    await hash.update('cWl3aXRlY2g=', helper.plugin.CADESCOM_BASE64_TO_BINARY);
    const hex = await hash.digest();

    It's not recommended, but is possible to use actual classes with the new keyword. Note they always take Helper and Handle instances as first and second arguments respectively. Some classes have an async populate method that must be called before any other one.

    const hash = new cryptopro.Hash(
    	helper,
    	await helper.createHandle('CAdESCOM.HashedData'),
    	helper.plugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256
    );
    await hash.populate();

    Plugin API

    getPlugin

    getPlugin(scope: Object, options?: PluginOptions): Promise<Plugin>

    The getPlugin function accepts the scope (generally, window) and returns the reference to the Crypto-Pro CAdES browser plugin.

    import { getPlugin } form '@qiwitech/cryptopro';
    
    const plugin = await getPlugin(window);

    Examples

    Get started

    import { createSubtleCrypto, createCryptoKeys, createHelper, getPlugin } form '@qiwitech/cryptopro';
    
    const plugin = await getPlugin(window),
    	helper = await createHelper(plugin),
    	crypto = {
    		subtle: await createSubtleCrypto(helper)
    	},
    	cryptokeys = await createCryptoKeys(helper);

    Sign

    const digestAlgo = 'GOST R 34.11-2012-256',
    	signAlgo = 'GOST R 34.10-2012-256',
    	text = 'Hello World',
    	data = new TextEncoder().encode(text).buffer;
    
    const namedKey = await cryptokeys.getKeyByName('...'),
    	hash = await crypto.subtle.digest(digestAlgo, data);
    	signature = await crypto.subtle.sign(signAlgo, namedKey, hash),
    	certificate = await crypto.subtle.exportKey('x509', namedKey),
    	valid = await crypto.subtle.verify(signAlgo, namedKey, siganture, hash);
    await sendToOtherParty(hash, signature, certificate);
    
    // NOTE this also works: both sign() and verify() can pre-hash data for you.
    const signature = await crypto.subtle.sign(digestAlgo, namedKey, data),
    	valid = await crypto.subtle.verify(digestAlgo, namedKey, signature, data);
    await sendToOtherParty(data, signature, certificate);

    Verify

    const digestAlgo = 'GOST R 34.11-2012-256',
    	signAlgo = 'GOST R 34.10-2012';
    
    const { data, signature, certificate } = await getFromOtherParty(),
    	publicKey = await crypto.subtle.importKey('x509', certificate, ['verify']),
    	hash = await crypto.subtle.digest(digestAlgo, data);
    	valid = await crypto.subtle.verify(signAlgo, publicKey, siganture, hash);
    
    // NOTE this also works: verify() can pre-hash data for you.
    const valid = await crypto.subtle.verify(digestAlgo, publicKey, signature, data);

    Generate PKCS#10 CSR and install response

    The only way to create new keypair is to call SubtleCrypto.generateKey method to generate a keypair, then use SubtleCrypto.exportKey to format a PKCS#10 Certificate Signing Request, send it to the trusted Certificate Authority, then receive a newly issued X.509 Certificate and finally import it into available certificate storage using the SubtleCrypto.importKey method.

    const signAlgo = 'GOST R 34.10-2012-256';
    
    const { publicKey, privateKey } = await crypto.subtle.generateKey(signAlgo, false, ['sign', 'verify']);
    
    // NOTE publicKey.name may be used to define certificate Common name (CN).
    publicKey.name = 'New certificate';
    
    const csr = await crypto.subtle.exportKey('pkcs10', publicKey),
    	certificate = await sendToCertificateAutority(csr),
    	{ publicKey, privateKey } = await crypto.subtle.importKey('x509', certificate, signAlgo, false, ['sign', 'verify']);

    Install

    npm i @qiwitech/cryptopro

    DownloadsWeekly Downloads

    4

    Version

    0.1.2

    License

    MIT

    Unpacked Size

    370 kB

    Total Files

    32

    Last publish

    Collaborators

    • qiwitech