UNPKG

21.7 kBMarkdownView Raw
1# Tutorial JavaScript Crypto Library [![Build Status](https://travis-ci.org/VirgilSecurity/virgil-crypto-javascript.svg?branch=master)](https://travis-ci.org/VirgilSecurity/virgil-crypto-javascript) [![npm](https://img.shields.io/npm/v/virgil-crypto.svg)](https://www.npmjs.com/package/virgil-crypto)
2
3- [Install](#install)
4- [Generate Keys](#generate-keys)
5- [Obtaining an Access Token](#obtaining-an-access-token)
6- [Use case](#use-case)
7 - [Step 0. Initialization](#step-0-initialization)
8 - [Step 1. Generate and Publish the Keys](#step-1-generate-and-publish-the-keys)
9 - [Step 2. Encrypt and Sign](#step-2-encrypt-and-sign)
10 - [Step 3. Send a Message](#step-3-send-a-message)
11 - [Step 4. Receive a Message](#step-4-receive-a-message)
12 - [Step 5. Verify and Decrypt](#step-5-verify-and-decrypt)
13- [Encrypt/Decrypt Data](#encryptdecrypt-data)
14 - [Using Password](#using-password)
15 - [Async (using web workers) Using Password](#async-using-web-workers-using-password)
16 - [Using Key](#using-key)
17 - [Using Key with Password](#using-key-with-password)
18 - [Using Key with Password for Multiple Recipients](#using-key-with-password-for-multiple-recipients)
19 - [Async (using web workers) Using Key with Password](#async-using-web-workers-using-key-with-password)
20 - [Async (using web workers) Using Key with Password for Multiple Recipients](#async-using-web-workers-using-key-with-password-for-multiple-recipients)
21 - [Using Key without Password](#using-key-without-password)
22 - [Async (using web workers) Using Key without Password](#async-using-web-workers-using-key-without-password)
23- [Sign and Verify Data Using Key](#sign-and-verify-data-using-key)
24 - [With Password](#with-password)
25 - [Async (using web workers) with Password](#async-using-web-workers-with-password)
26- [Source code](#source-code)
27- [Resources](#resources)
28- [License](#license)
29- [Contacts](#contacts)
30
31## Install
32
33### NPM
34
35```sh
36npm install virgil-crypto
37```
38Or install Virgil SDK with Virgil Crypto (recommended):
39
40```sh
41npm install virgil-sdk
42```
43
44### Bower
45```sh
46bower install virgil-crypto
47```
48Or install Virgil SDK with Virgil Crypto (recommended):
49
50```sh
51bower install virgil-sdk
52```
53
54### CDN
55```html
56<script
57src="https://cdn.virgilsecurity.com/packages/javascript/crypto/1.5.5/virgil-crypto.min.js"
58integrity="sha256-3W5xboDke1qIoYdoIGh3alQWUBMElS+lIyGL2JAjYhE="
59crossorigin="anonymous"></script>
60```
61Or install Virgil SDK with Virgil Crypto (recommended):
62
63```html
64<script
65src="https://cdn.virgilsecurity.com/packages/javascript/sdk/1.4.1/virgil-sdk.min.js"
66integrity="sha256-oa5PdJUfmpmSk0q08WejIusp7epaht49i8NKSf6uoJo="
67crossorigin="anonymous"></script>
68```
69
70### Demos
71[Virgil & Twilio IP Messaging Demo Code](https://github.com/VirgilSecurity/virgil-demo-twilio) and check out working demo:
72[End to End Encrypted IP Messaging with Twilio API + Virgil](http://virgil-twilio-demo.azurewebsites.net/)
73
74Quickstart guide for making your own E2E encrypted IP Messaging is: [here](https://github.com/VirgilSecurity/virgil-demo-twilio/blob/master/Quick%20start%20guide.md)
75
76## Generate Keys
77
78The following code example creates a new public/private key pair.
79
80```javascript
81var keyPair = virgilCrypto.generateKeyPair();
82console.log('Key pair without password: ', keyPair);
83```
84
85You can also generate a key pair with an encrypted private key just using one of the overloaded constructors.
86
87In the table below you can see all types.
88
89| Key Type | Description |
90|-------------------|--------------------------------|
91| Default | recommended safest type |
92| RSA_2048 | RSA 2048 bit (not recommended) |
93| RSA_3072 | RSA 3072 bit |
94| RSA_4096 | RSA 4096 bit |
95| RSA_8192 | RSA 8192 bit |
96| EC_SECP256R1 | 256-bits NIST curve |
97| EC_SECP384R1 | 384-bits NIST curve |
98| EC_SECP521R1 | 521-bits NIST curve |
99| EC_BP256R1 | 256-bits Brainpool curve |
100| EC_BP384R1 | 384-bits Brainpool curve |
101| EC_BP512R1 | 512-bits Brainpool curve |
102| EC_M221 | (not implemented yet) |
103| EC_M255 | Curve25519 |
104| EC_M383 | (not implemented yet) |
105| EC_M511 | (not implemented yet) |
106| EC_SECP192K1 | 192-bits "Koblitz" curve |
107| EC_SECP224K1 | 224-bits "Koblitz" curve |
108| EC_SECP256K1 | 256-bits "Koblitz" curve |
109
110
111```javascript
112var keyPairRsa2048 = virgilCrypto.generateKeyPair(virgilCrypto.KeysTypesEnum.RSA_2048);
113console.log('Key pair RSA_2048 without password: ', keyPairRsa2048);
114
115var KEY_PASSWORD = 'password';
116var keyPairWithPassword = virgilCrypto.generateKeyPair(KEY_PASSWORD);
117console.log('Key pair with password: ', keyPairWithPassword);
118
119
120var KEY_PASSWORD = 'password';
121var keyPairWithPasswordAndSpecificType = virgilCrypto.generateKeyPair(KEY_PASSWORD, virgilCrypto.KeysTypesEnum.RSA_2048);
122console.log('Key pair RSA_2048 with password: ', keyPairWithPasswordAndSpecificType);
123```
124
125In the example below you can see a simply generated public/private keypair without a password.
126
127```
128-----BEGIN PUBLIC KEY-----
129MIGbMBQGByqGSM49AgEGCSskAwMCCAEBDQOBggAEWIH2SohavmLdRwEJ/VWbFcWr
130rU+g7Z/BkI+E1L5JF7Jlvi1T1ed5P0/JCs+K0ZBM/0hip5ThhUBKK2IMbeFjS3Oz
131zEsWKgDn8j3WqTb8uaKIFWWG2jEEnU/8S81Bgpw6CyxbCTWoB+0+eDYO1pZesaIS
132Tv6dTclx3GljHpFRdZQ=
133-----END PUBLIC KEY-----
134
135-----BEGIN EC PRIVATE KEY-----
136MIHaAgEBBEAaKrInIcjTeBI6B0mX+W4gMpu84iJtlPxksCQ1Dv+8iM/lEwx3nWWf
137ol6OvLkmG/qP9RqyXkTSCW+QONiN9JCEoAsGCSskAwMCCAEBDaGBhQOBggAEWIH2
138SohavmLdRwEJ/VWbFcWrrU+g7Z/BkI+E1L5JF7Jlvi1T1ed5P0/JCs+K0ZBM/0hi
139p5ThhUBKK2IMbeFjS3OzzEsWKgDn8j3WqTb8uaKIFWWG2jEEnU/8S81Bgpw6Cyxb
140CTWoB+0+eDYO1pZesaISTv6dTclx3GljHpFRdZQ=
141-----END EC PRIVATE KEY-----
142```
143
144Here is what an encrypted private key looks like:
145
146```
147-----BEGIN ENCRYPTED PRIVATE KEY-----
148MIIBKTA0BgoqhkiG9w0BDAEDMCYEIJjDIF2KRj7u86Up1ZB4yHHKhqMg5C/OW2+F
149mG5gpI+3AgIgAASB8F39JXRBTK5hyqEHCLcCTbtLKijdNH3t+gtCrLyMlfSfK49N
150UTREjF/CcojkyDVs9M0y5K2rTKP0S/LwUWeNoO0zCT6L/zp/qIVy9wCSAr+Ptenz
151MR6TLtglpGqpG4bhjqLNR2I96IufFmK+ZrJvJeZkRiMXQSWbPavepnYRUAbXHXGB
152a8HWkrjKPHW6KQxKkotGRLcThbi9cDtH+Cc7FvwT80O7qMyIFQvk8OUJdY3sXWH4
1535tol7pMolbalqtaUc6dGOsw6a4UAIDaZhT6Pt+v65LQqA34PhgiCxQvJt2UOiPdi
154SFMQ8705Y2W1uTexqw==
155-----END ENCRYPTED PRIVATE KEY-----
156```
157
158## Obtaining an Access Token
159
160First you must create a free Virgil Security developer's account by signing up [here](https://developer.virgilsecurity.com/account/signup). Once you have your account you can [sign in](https://developer.virgilsecurity.com/account/signin) and generate an access token for your application.
161
162The access token provides authenticated secure access to Virgil Keys Services and is passed with each API call. The access token also allows the API to associate your app’s requests with your Virgil Security developer's account.
163
164Use this token to initialize the SDK client [here](#step-0-initialization).
165
166## Use Case
167**Secure any data end to end**: users need to securely exchange information (text messages, files, audio, video etc) while enabling both in transit and at rest protection.
168
169- Application generates public and private key pairs using Virgil Crypto library and uses Virgil Keys service to enable secure end to end communications:
170 - public key on Virgil Public Keys Service;
171 - private key on Virgil Private Keys Service or locally.
172- Sender’s information is encrypted in Virgil Crypto Library with the recipient’s public key.
173- Sender’s encrypted information is signed with his private key in Virgil Crypto Library.
174- Application securely transfers the encrypted data, sender’s digital signature and UDID to the recipient without any risk to be revealed.
175- Application on the recipient’s side verifies that the signature of transferred data is valid using the signature and sender’s public key in Virgil Crypto Library.
176- The received information is decrypted with the recipient’s private key using Virgil Crypto Library.
177- Decrypted data is provided to the recipient.
178
179## Step 0. Initialization
180
181### Node
182
183```javascript
184var VirgilSDK = require('virgil-sdk');
185var virgil = new VirgilSDK("%ACCESS_TOKEN%");
186```
187
188### Browsers
189
190```javascript
191var VirgilSDK = window.VirgilSDK;
192var virgil = new VirgilSDK("%ACCESS_TOKEN%");
193```
194
195## Step 1. Generate and Publish the Keys
196First a mail exchange application is generating the keys and publishing them to the Public Keys Service where they are available in an open access for other users (e.g. recipient) to verify and encrypt the data for the key owner.
197
198The following code example creates a new public/private key pair.
199
200```javascript
201var password = "jUfreBR7";
202// the private key's password is optional
203var keyPair = virgil.crypto.generateKeyPair(password);
204```
205- [virgil.crypto.generateKeyPair](https://github.com/VirgilSecurity/virgil-crypto-javascript/#generate-keys)
206
207The app is registering a Virgil Card which includes a public key and an email address identifier. The card will be used for the public key identification and searching for it in the Public Keys Service. You can create a Virgil Card with or without identity verification, see both examples [here...](https://github.com/VirgilSecurity/virgil/tree/master/javascript/keys-sdk#publish-a-virgil-card)
208
209```javascript
210virgil.cards.create({
211 public_key: keyPair.publicKey,
212 private_key: keyPair.privateKey,
213 private_key_password: 'YOUR_PRIVATE_KEY_PASSWORD',
214 identity: {
215 type: VirgilSDK.IdentityTypes.email,
216 value: 'user@virgilsecurity.com'
217 }
218}).then(function (myCard) {a
219
220});
221```
222
223- [virgil.cards.create](https://github.com/VirgilSecurity/virgil/tree/master/javascript/keys-sdk#publish-a-virgil-card)
224
225## Step 2. Encrypt and Sign
226
227The app is searching for the recipient’s public key on the Public Keys Service to encrypt a message for him. The app is signing the encrypted message with sender’s private key so that the recipient can make sure the message had been sent from the declared sender.
228
229```javascript
230getChannelRecipients()
231 .then(function encryptMessageForAllMembersAndSend (recipients) {
232 const encryptedMessage = virgil.crypto.encrypt(message, recipients);
233 const sign = virgil.crypto.sign(encryptedMessage, privateKey);
234 //...
235 })
236
237```
238
239- [virgil.crypto.encrypt](https://github.com/VirgilSecurity/virgil-crypto-javascript/#encryptdecrypt-data)
240- [virgil.crypto.sign](https://github.com/VirgilSecurity/virgil-crypto-javascript#sign-and-verify-data-using-key)
241
242## Step 3. Send a Message
243The app is merging the message text and the signature into one structure and sending the message to the recipient using a simple IP messaging client.
244
245```javascript
246messagingService.sendMessageToChannel({
247 channel_name: 'some channel name',
248 identity_token: 'messaging service user identity token',
249 message: JSON.stringify({
250 message: encryptedMessage.toString('base64'),
251 sign: sign.toString('base64')
252 })
253})
254```
255
256## Step 4. Receive a Message
257
258An encrypted message is received on the recipient’s side using an IP messaging client. In order to decrypt and verify the received data, the app on recipient’s side needs to get sender’s Virgil Card from the Keys Service.
259
260```javascript
261messagingService.getChannelMessages({ channel_name: 'some channel name' })
262 .map(function (messagePayload) {
263 return virgil.cards.search({
264 value: messagePayload.sender_identifier,
265 type: VirgilSDK.IdentityTypes.email
266 }).then(function (cards) {
267 var senderCard = cards[0];
268 // ...
269 });
270 })
271```
272
273- [virgil.cards.search](https://github.com/VirgilSecurity/virgil/tree/master/javascript/keys-sdk#search-for-cards)
274
275## Step 5. Verify and Decrypt
276
277The application is making sure the message came from the declared sender by getting his card on Virgil Public Keys Service. In case of success, the message is decrypted using the recipient's private key.
278
279```javascript
280var payload = JSON.parse(message.message);
281var encryptedMessage = new virgil.crypto.Buffer(payload.message, 'base64');
282var sign = new virgil.crypto.Buffer(payload.sign, 'base64');
283
284var isVerified = virgil.crypto.verify(encryptedMessage,
285 senderCard.public_key.public_key, sign);
286
287 if (!isVerified) {
288 throw new Error('The message signature is not valid');
289 }
290
291var decryptedMessage = virgil.crypto.decrypt(encryptedMessage,
292 recipientCard.id, privateKey);
293// Decrypt returns decrypted content as buffer in order to get
294// original text content
295// toString method should be used
296var originalMessage = decryptedMessage.toString('utf8');
297```
298
299- [virgil.crypto.verify](https://github.com/VirgilSecurity/virgil-crypto-javascript#sign-and-verify-data-using-key)
300- [virgil.crypto.decrypt](https://github.com/VirgilSecurity/virgil-crypto-javascript#using-key-with-password-for-multiple-recipients)
301
302
303## Encrypt/Decrypt data
304
305The procedure for encrypting and decrypting the data is simple. For example:
306
307If you want to encrypt the data to Bob, you encrypt it using Bob's public key (which you can get from the Public Keys Service), and Bob decrypts it with his private key. If Bob wants to encrypt some data to you, he encrypts it using your public key, and you decrypt it with your private key.
308
309Crypto Library allows to encrypt the data for several types of recipient's user data like public key and password. This means that you can encrypt the data with some password or with a public key generated with the Crypto Library.
310
311### Using Password
312
313> Initial data must be passed as a String or [Buffer](https://github.com/feross/buffer).
314
315> Encrypted data will be returned as a [Buffer](https://github.com/feross/buffer).
316
317> The [Buffer](https://github.com/feross/buffer) constructor is available by ```virgilCrypto.Buffer```.
318
319```javascript
320var INITIAL_DATA = 'data to be encrypted';
321var PASSWORD = 'password';
322
323var encryptedData = virgilCrypto.encrypt(INITIAL_DATA, PASSWORD);
324var decryptedData = virgilCrypto.decrypt(encryptedData, PASSWORD);
325
326console.log('Encrypted data: ' + encryptedData);
327console.log('Decrypted data: ' + decryptedData.toString());
328```
329
330### Async (using web workers) Using Password
331
332> Only for browsers.
333
334```javascript
335var INITIAL_DATA = 'data to be encrypted';
336var PASSWORD = 'password';
337
338virgilCrypto.encryptAsync(INITIAL_DATA, PASSWORD)
339 .then(function(encryptedData) {
340 console.log('Encrypted data: ' + encryptedData);
341
342 virgilCrypto.decryptAsync(encryptedData, PASSWORD)
343 .then(function(decryptedData) {
344 console.log('Decrypted data: ' + decryptedData.toString());
345 });
346 });
347```
348
349### Using Key
350
351> Initial data must be passed as a String or [Buffer](https://github.com/feross/buffer).
352
353> Encrypted data will be returned as a [Buffer](https://github.com/feross/buffer).
354
355> The [Buffer](https://github.com/feross/buffer) constructor is available by ```virgilCrypto.Buffer```.
356
357### Using Key with Password
358
359```javascript
360var KEY_PASSWORD = 'password';
361var INITIAL_DATA = 'data to be encrypted';
362var RECIPIENT_ID = '<SOME_RECIPIENT_ID>';
363
364var keyPair = virgilCrypto.generateKeyPair(KEY_PASSWORD);
365var encryptedData = virgilCrypto.encrypt(INITIAL_DATA, RECIPIENT_ID, keyPair.publicKey);
366var decryptedData = virgilCrypto.decrypt(encryptedData, RECIPIENT_ID, keyPair.privateKey, KEY_PASSWORD);
367
368console.log('Encrypted data: ' + encryptedData);
369console.log('Decrypted data: ' + decryptedData.toString());
370```
371
372### Using Key with Password for Multiple Recipients
373
374```javascript
375var KEY_PASSWORD = 'password';
376var INITIAL_DATA = 'data to be encrypted';
377var RECIPIENT_ID = '<SOME_RECIPIENT_ID>';
378
379var keyPair = virgilCrypto.generateKeyPair(KEY_PASSWORD);
380var recipientsList = [{ recipientId: RECIPIENT_ID, publicKey: keyPair.publicKey }];
381var encryptedData = virgilCrypto.encrypt(INITIAL_DATA, recipientsList);
382var decryptedData = virgilCrypto.decrypt(encryptedData, RECIPIENT_ID, keyPair.privateKey, KEY_PASSWORD);
383
384console.log('Encrypted data: ' + encryptedData);
385console.log('Decrypted data: ' + decryptedData.toString());
386```
387
388### Async (using web workers) Using Key with Password
389
390> Only for browsers.
391
392```javascript
393var KEY_PASSWORD = 'password';
394var INITIAL_DATA = 'data to be encrypted';
395var RECIPIENT_ID = '<SOME_RECIPIENT_ID>';
396
397virgilCrypto.generateKeyPairAsync(KEY_PASSWORD)
398 .then(function(keyPair) {
399 virgilCrypto.encryptAsync(INITIAL_DATA, RECIPIENT_ID, keyPair.publicKey)
400 .then(function(encryptedData) {
401 console.log('Encrypted data: ' + encryptedData);
402
403 virgilCrypto.decryptAsync(encryptedData, RECIPIENT_ID, keyPair.privateKey, KEY_PASSWORD)
404 .then(function(decryptedData) {
405 console.log('Decrypted data: ' + decryptedData.toString());
406 });
407 });
408 });
409```
410
411### Async (using web workers) Using Key with Password for Multiple Recipients
412
413> Only for browsers.
414
415```javascript
416var KEY_PASSWORD = 'password';
417var INITIAL_DATA = 'data to be encrypted';
418var RECIPIENT_ID = '<SOME_RECIPIENT_ID>';
419
420virgilCrypto.generateKeyPairAsync(KEY_PASSWORD)
421 .then(function(keyPair) {
422 var recipientsList = [{ recipientId: RECIPIENT_ID, publicKey: keyPair.publicKey }];
423
424 virgilCrypto.encryptAsync(INITIAL_DATA, recipientsList)
425 .then(function(encryptedData) {
426 console.log('Encrypted data: ' + encryptedData);
427
428 virgilCrypto.decryptAsync(encryptedData, RECIPIENT_ID, keyPair.privateKey, KEY_PASSWORD)
429 .then(function(decryptedData) {
430 console.log('Decrypted data: ' + decryptedData.toString());
431 });
432 });
433 });
434```
435
436### Using Key without Password
437
438```javascript
439var INITIAL_DATA = 'data to be encrypted';
440var RECIPIENT_ID = '<SOME_RECIPIENT_ID>';
441
442var keyPair = virgilCrypto.generateKeyPair();
443var encryptedData = virgilCrypto.encrypt(INITIAL_DATA, RECIPIENT_ID, keyPair.publicKey);
444var decryptedData = virgilCrypto.decrypt(encryptedData, RECIPIENT_ID, keyPair.privateKey);
445
446console.log('Encrypted data: ' + encryptedData);
447console.log('Decrypted data: ' + decryptedData.toString());
448```
449
450### Async (using web workers) Using Key without Password
451
452> Only for browsers.
453
454```javascript
455var INITIAL_DATA = 'data to be encrypted';
456var RECIPIENT_ID = '<SOME_RECIPIENT_ID>';
457
458virgilCrypto.generateKeyPairAsync()
459 .then(function(keyPair) {
460 virgilCrypto.encryptAsync(INITIAL_DATA, RECIPIENT_ID, keyPair.publicKey)
461 .then(function(encryptedData) {
462 console.log('Encrypted data: ' + encryptedData);
463
464 virgilCrypto.decryptAsync(encryptedData, RECIPIENT_ID, keyPair.privateKey)
465 .then(function(decryptedData) {
466 console.log('Decrypted data: ' + decryptedData.toString());
467 });
468 });
469 });
470```
471
472## Sign and Verify Data Using Key
473
474Cryptographic digital signatures use public key algorithms to provide data integrity. When you sign the data with a digital signature, someone else can verify the signature and can prove that the data originated from you and was not altered after you had signed it.
475
476The following example applies a digital signature to a public key identifier.
477
478> Initial data must be passed as a String or [Buffer](https://github.com/feross/buffer).
479
480> Encrypted data will be returned as a [Buffer](https://github.com/feross/buffer).
481
482> The [Buffer](https://github.com/feross/buffer) constructor is available by ```virgilCrypto.Buffer```.
483
484### With Password
485
486```javascript
487var KEY_PASSWORD = 'password';
488var INITIAL_DATA = 'data to be encrypted';
489var RECIPIENT_ID = '<SOME_RECIPIENT_ID>';
490
491var keyPair = virgilCrypto.generateKeyPair(KEY_PASSWORD);
492var encryptedData = virgilCrypto.encrypt(INITIAL_DATA, RECIPIENT_ID, keyPair.publicKey);
493var sign = virgilCrypto.sign(encryptedData, keyPair.privateKey, KEY_PASSWORD);
494```
495
496To verify that the data was signed by a particular party, you need the following information:
497
498* the public key of the party that signed the data;
499* the digital signature;
500* the data that was signed.
501
502The following example verifies a digital signature which was signed by the sender.
503
504```javascript
505var isDataVerified = virgilCrypto.verify(encryptedData, keyPair.publicKey, sign);
506
507console.log('Encrypted data: ' + encryptedData);
508console.log('Sign: ' + sign.toString('base64'));
509console.log('Is data verified: ' + isDataVerified);
510```
511
512### Async (using web workers) With Password
513
514> Only for browsers.
515
516```javascript
517var KEY_PASSWORD = 'password';
518var INITIAL_DATA = 'data to be encrypted';
519var RECIPIENT_ID = '<SOME_RECIPIENT_ID>';
520
521virgilCrypto.generateKeyPairAsync(KEY_PASSWORD)
522 .then(function(keyPair) {
523 virgilCrypto.encryptAsync(INITIAL_DATA, RECIPIENT_ID, keyPair.publicKey)
524 .then(function(encryptedData) {
525 console.log('Encrypted data: ' + encryptedData);
526
527 virgilCrypto.signAsync(encryptedData, keyPair.privateKey, KEY_PASSWORD)
528 .then(function(sign) {
529 console.log('Sign: ' + sign.toString('base64'));
530
531 virgilCrypto.verifyAsync(encryptedData, keyPair.publicKey, sign)
532 .then(function(isDataVerified) {
533 console.log('Is data verified: ' + isDataVerified);
534 });
535 });
536 });
537 });
538```
539
540## Source code
541
542* [Use Case Example](https://github.com/VirgilSecurity/virgil-sdk-javascript/tree/master/examples/ip-messaging/client)
543* [IP-Messaging Simple Server](https://github.com/VirgilSecurity/virgil-sdk-javascript/tree/master/examples/ip-messaging/server)
544
545## Resources
546
547* [Crypto Library](https://github.com/VirgilSecurity/virgil/blob/master/javascript/crypto-library/readme.md)
548* [SDK](https://github.com/VirgilSecurity/virgil/blob/master/javascript/keys-sdk/readme.md)
549
550## License
551BSD 3-Clause. See [LICENSE](https://github.com/VirgilSecurity/virgil/blob/master/LICENSE) for details.
552
553## Contacts
554Email: <support@virgilsecurity.com>