Skip to content

Commit

Permalink
Merge pull request #333 from gnarea/EnvelopedData-RecipientKeyIdentifier
Browse files Browse the repository at this point in the history
Support RecipientKeyIdentifier in EnvelopedData KeyAgreeRecipientInfo
  • Loading branch information
microshine authored Oct 28, 2021
2 parents 8904acd + 2908fbb commit 657124b
Show file tree
Hide file tree
Showing 9 changed files with 899 additions and 156 deletions.
19 changes: 19 additions & 0 deletions examples/HowToEncryptCMSviaKey/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compact": false,
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": [ // Based on data from https://caniuse.com/#feat=cryptography
"Chrome >= 53",
"Firefox >= 47",
//"Edge >= 12", // Edge has incorrect or not complete WebCrypto API implementation (at least in v17)
"Safari >= 11",
"iOS >= 11.2"
]
}
}
]
]
}
193 changes: 193 additions & 0 deletions examples/HowToEncryptCMSviaKey/CMSEnvelopedExample.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>CMS Enveloped Example</title>

<script type="application/javascript" src="../../node_modules/babel-polyfill/dist/polyfill.js"></script>
<script type="text/javascript" src="bundle.js"></script>

<style type="text/css">
body{background:#EFEFEF;font:normal 14px/16px Helvetica, Arial, sans-serif;}
.wrapper{
width:600px;
margin:50px auto;
padding:50px;
border:solid 2px #CCC;
border-radius:10px;
-webkit-border-radius:10px;
box-shadow:0 0 12px 3px #CDCDCD;
-webkit-box-shadow:0 0 12px 3px #CDCDCD;
background:#FFF;
}
label{
font:bold 16px/20px Helvetica, Arial, sans-serif;
margin:0 0 8px;
}
textarea{
width:500px;
border:solid 1px #999;
border-radius:5px;
-webkit-border-radius:5px;
height:340px;
font:normal 12px/15px monospace;
display:block;
margin:0 0 12px;
box-shadow:0 0 5px 5px #EFEFEF inset;
-webkit-box-shadow:0 0 5px 5px #EFEFEF inset;
padding:20px;
resize: none;
}
a{
display:inline-block;
padding:5px 15px;
background:#ACD0EC;
border:solid 1px #4C6181;
color:#000;
font:normal 14px/16px Helvetica, Arial, sans-serif;
}
a:hover{
background:#DAEBF8;
cursor:pointer;
}
.header-block {
margin-top:30px;
font:bold 16px/20px Helvetica, Arial, sans-serif;
}
.border-block{
border:solid 2px #999;
border-radius:5px;
-webkit-border-radius:5px;
margin:10px 0 0;
padding:20px 30px;
background:#F0F4FF;
}
.border-block h2{
margin:0 0 16px;
font:bold 22px/24px Helvetica, Arial, sans-serif;
}
.border-block p{
margin:0 0 12px;
}
.border-block p .type{
font-weight:bold;
display:inline-block;
width:176px;
}
.border-block .two-col{
overflow:hidden;
margin:0 0 16px;
}
.border-block .two-col .subject{
width:180px;
font-weight:bold;
margin:0 0 12px;
float:left;
}
.border-block .two-col #cert-attributes{
margin:0;
padding:0;
float:left;
list-style:none;
}
.border-block .two-col #cert-attributes li p{
margin:0;
}
.border-block .two-col #cert-attributes li p span{
width:40px;
display:inline-block;
margin:0 0 5px;
}
.border-block .two-col #cert-exten{
overflow:hidden;
padding:0 0 0 17px;
margin:0;
list-style-type:square;
}
table {
border:solid;
border-collapse:collapse;
border-color:black;
}
th {
text-align:center;
background: #ccc;
padding: 5px;
border: 1px solid black;
}
td {
padding: 5px;
border: 1px solid black;
}
</style>
</head>

<body>
<div class="wrapper">
<p class="header-block">Enter existing private key or generate one</p>
<div id="output_div" class="border-block">
<p>
<label for="curve_name" style="font-weight:bold">Key agreement algorithm:</label>
<select id="curve_name" onchange="handleKeyAgreeAlgorithmOnChange()">
<option value="ecdh_p256">ECDH P-256</option>
<option value="ecdh_p384">ECDH P-384</option>
<option value="ecdh_p521">ECDH P-521</option>
</select>
</p>
<p>
<a onclick="createKeyPair();">Create</a>
</p>
<label for="pkcs8_key" style="font-weight:bold;float:left;">BASE-64 encoded PKCS#8 private key:</label>
<textarea id="pkcs8_key">&lt; PKCS#8 private key must be put here &gt;</textarea>
<label for="pkcs8_key_pub" style="font-weight:bold;float:left;">BASE-64 encoded PKCS#8 public key:</label>
<textarea id="pkcs8_key_pub">&lt; PKCS#8 public key must be put here &gt;</textarea>
<label for="pkcs8_key_id" style="font-weight:bold;float:left;">BASE-64 encoded key pair id:</label>
<textarea id="pkcs8_key_id">&lt; Encoded key id must be put here &gt;</textarea>
</div>
</div>
<div class="wrapper">
<p class="header-block">Work with CMS Enveloped data</p>
<div class="border-block">
<label for="content" style="font-weight:bold;float:left;">Content to encrypt:</label>
<textarea id="content">&lt; Put input data here &gt;</textarea>
<p>
<label for="content_enc_alg" style="font-weight:bold">Content encryption algorithm:</label>
<select id="content_enc_alg" onchange="handleEncAlgOnChange()">
<option value="alg_CBC">AES-CBC</option>
<option value="alg_GCM">AES-GCM</option>
</select>
</p>
<p>
<label for="content_enc_alg_len" style="font-weight:bold">Content encryption algorithm length:</label>
<select id="content_enc_alg_len" onchange="handleEncLenOnChange()">
<option value="len_128">128</option>
<!-- Currently in Google Chrome AES 192 bit keys are not supported
<option value="len_192">192</option>
-->
<option value="len_256">256</option>
</select>
</p>
<p>
<label for="oaep_hash_alg" style="font-weight:bold">KDF Hashing Algorithm:</label>
<select id="oaep_hash_alg" onchange="handleOAEPHashAlgOnChange()">
<option value="alg_SHA1">SHA-1</option>
<option value="alg_SHA256">SHA-256</option>
<option value="alg_SHA384">SHA-384</option>
<option value="alg_SHA512">SHA-512</option>
</select>
</p>
<p>
<a onclick="envelopedEncrypt();">Encrypt</a>
</p>
<label for="encrypted_content" style="font-weight:bold;float:left;">BASE-64 encoded encrypted content:</label>
<textarea id="encrypted_content">&lt; Put encrypted content here &gt;</textarea>
<p>
<a onclick="envelopedDecrypt();">Decrypt</a>
</p>
<label for="decrypted_content" style="font-weight:bold;float:left;">Decrypted content:</label>
<textarea id="decrypted_content">&lt; Decrypted content will be put here &gt;</textarea>
</div>
</div>
</body>
</html>
62 changes: 62 additions & 0 deletions examples/HowToEncryptCMSviaKey/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Working with CMS EnvelopedData

Working with with CMS Enveloped Data in PKIjs is done with the **org.pkijs.simpl.CMS_ENVELOPED_DATA** object. Utilizing this object you can encode/encrypt a new CMS Enveloped Data message, decode/decrypt an existing one or add recipients to it. Additionally to make it easier for web developers to work with these messages it is also possible to translate the message to JSON.

## Encrypting
To create an encrypted message with PKIjs the first thing you must do is choose who you want to encrypt the message to. [PKIjs][] supports encrypting to recipients using the public keys in X.509 certificates as well as using preshared keys (KEK) or passwords.

To add a recipient to a message you use one of these methods:
* addRecipientByCertificate;
* addRecipientByPreDefinedData;

**addRecipientByCertificate** is used when encrypting a message to a the subject of an X.509 certificate; S/MIME encryption uses this approach.

>**NOTE**: CMS EnvelopedData supports a concept of recipient types when encrypting to a subject of an certificate. At this time you **can not** specify the "type" of recipient. Certificates with RSA signatures the recipient's type will be **KeyTransRecipientInfo** and certificates with ECC signatures it will be type **KeyAgreeRecipientInfo**. It is not possible to support this case with WebCrypto at this time because there are no good support for the **DH** algorithm in all browsers ([FireFox has problems with exporting/importing keys][] and [Google Chrome has no implementation for **DH** at all][]). When this changes it will be possible to add support for **KeyAgreeRecipientInfo** for both RSA and ECC certificates.
Parameters for **addRecipientByCertificate**:
* **certificate** - *org.pkijs.simpl.CERT* - The recipients certificate (a PKIjs parsed object);
* **parameters** - *Object* - The options to be used when encrypting the message; options include:
* **oaepHashAlgorithm** - *String* - The hash algorithm to be used with with RSASSA-OAEP. The default value is - SHA-512;
* **kdfAlgorithm** - *String* - The hash algorithm to be used when deriving keys for use with ECDH (when using ECC-based certificates). The default value is - SHA-512;
* **kekEncryptionLength** - *Number* - The length of the key to be use with AES-KW. The default value is - 256;
* **variant** - *Number* - A reserved value. The intention is to use this in the future to support specifying *“recipient type”*; for example *"variant = 1"* for *KeyTransRecipientInfo* type and *"variant = 2"* for **KeyAgreeRecipientInfo** type;

**addRecipientByPreDefinedData** is used when encrypting to pre-defined keys (**KEKRecipientInfo**) and passwords (**PasswordRecipientInfo**).

>**WARNING** Working with **PasswordRecipientInfo** currently (as for April 2015) possible only from [latest development versions of Google Chrome](https://www.google.com/intl/en/chrome/browser/desktop/index.html?extra=devchannel#eula)
Parameters for **addRecipientByPreDefinedData**:
* **preDefinedData**. Type - **ArrayBuffer**. The key or password to be used to encrypt the message.
>**NOTE**: At this time (April 2015) Google Chrome supports only AES-KW-128 and AES-KW-256 algorithms. Thus to use the **KEKRecipientInfo** type you must provide 16 or 32 bytes in this field to use that option;
* **parameters** - *Object* - The options to be used when encrypting the message; options include:
* **keyIdentifier** - *ArrayBuffer* - Used for **KEKRecipientInfo**. Default value - A random initialized ArrayBuffer 16 bytes length;
* **hmacHashAlgorithm** - *String* - The hash algorithm to be used in PBKDF2 when producing the HMAC. Default value - SHA-512;
* **iterationCount** - *Number* - The iteration count used in PBKDF2. The default value is - 2048;
* **keyEncryptionAlgorithm** - *WebCrypto algorithm* - The key encryption algorithm to be used when encrypting the message. The default value is - { name: *"AES-KW"*, length: *256* };
* **keyEncryptionAlgorithmParams** - *ASN1js parsed object* - Additional parameters used during key encryption. For example, for AES-CBC here we would put an "initialization vector". Default value - **new org.pkijs.asn1.NULL()**;
* **variant** - *Number* - Possible values: *variant = 1* is for **KEKRecipientInfo** and *variant = 2* is for **PasswordRecipientInfo**;

Once all recipients have been added the message you call the **encrypt** method on **org.pkijs.simpl.CMS_ENVELOPED_DATA** object.

The **encrypt** method has following parameters:
* **contentEncryptionAlgorithm** - *WebCrypto algorithm* - There is no default value for this parameter - At this time [PKIjs][] supports both AES-CBC and AES-GCM.
> **NOTE:** It is also technically possible to use AES-CTR, but seems that AES-CTR does not have an OID enabling it to be used in an interoperable with CMS Enveloped data messages;
* **contentToEncrypt** - *ArrayBuffer* - The data to encrypt;

## Decrypting
To decrypt a message with PKIjs you use the **decrypt** method of **org.pkijs.simpl.CMS_ENVELOPED_DATA** object.

The **decrypt** method has following parameters:
* **recipientIndex** - *Number* - The index (starting from 0) of the recipient that you want to decrypt the session key for.
> **NOTE:** In **KeyAgreeRecipientInfo** there is possibility to use one ephemeral ECDH key for many different recipients, inside one CMS Enveloped message. This case is not supported by [PKIjs][] - for each new recipient we use new ECDH ephemeral key. That is why [PKIjs][] has no "recipient's sub-indexes";
* **parameters** - *Object* - The options to be used when decrypting the message; options include:
* **recipientCertificate** - *org.pkijs.simpl.CERT* - This field is mandatory for recipient's type **KeyAgreeRecipientInfo**;
* **recipientPrivateKey** - *ArrayBuffer* - The encoded PKCS#8 structure for recipient's private key. This field is mandatory for recipient's types **KeyTransRecipientInfo** and **KeyAgreeRecipientInfo**;
* **preDefinedData** - *ArrayBuffer* - Early used pre-shared data (pre-shared *"key encryption key"* or password).

> **NOTE:** [PKIjs][] supports combinations of options not yet (as of April 2015) supported by OpenSSL and Microsoft CryptoAPI (and CNG). For example OpenSSL does not support PBKDF2 with AES-KW algorithms. And Microsoft CryptoAPI does not support **dhSinglePass-stdDH-sha1kdf-scheme** and **dhSinglePass-stdDH-sha512kdf-scheme** or any forms of CMS using pre-shared data.

[PKIjs]: http://pkijs.org/
[FireFox has problems with exporting/importing keys]: https://docs.google.com/spreadsheet/ccc?key=0AiAcidBZRLxndE9LWEs2R1oxZ0xidUVoU3FQbFFobkE#gid=1
[Google Chrome has no implementation for **DH** at all]: https://sites.google.com/a/chromium.org/dev/blink/webcrypto
Loading

0 comments on commit 657124b

Please sign in to comment.