Getting Started with IPWorks Encrypt

Requirements: IPWorks Encrypt

Introduction

IPWorks Encrypt is a comprehensive suite of components for encrypting, signing, decrypting, and verifying across a multitude of standard symmetric and asymmetric algorithms.

This guide will cover the basics of each component provided in IPWorks Encrypt. In addition, a few basic concepts involved in symmetric and asymmetric cryptography are included at the bottom of this article.

Before continuing, it is recommended to download IPWorks Encrypt in order to follow along with this tutorial.

Contents

Symmetric Algorithms (AES, CAST, DES, 3DES, etc.)

IPWorks Encrypt supports a variety of symmetric encryption algorithms. The simplest way to use any symmetric algorithm is through the EzCrypt component. This component provides a standard interface for all symmetric algorithms. Alternatively the toolkit contains individual components for each of the supported algorithms. The list of components supporting symmetric encryption is:

EzCrypt

The EzCrypt component provides a simple interface for using various symmetric algorithms to encrypt and decrypt. The encryption/decryption algorithm can be specified by setting the Algorithm property. By default, EzCrypt will use AES cryptography, which is functionally identical to using the AES component.

Encrypting and decrypting with EzCrypt requires specifying an input source, an output destination, and a KeyPassword.

Input

EzCrypt supports inputs from a file and from a string in memory. Setting the InputFile property to a path on disk will cause the component to read the contents of that file as input. If InputFile is not set, the component will expect the InputMessage property to contain the input data in string format.

In the .NET and Java Editions, EzCrypt also supports reading input from a stream. Calling SetInputStream will override both InputFile and InputMessage as the input source.

Output

Similar to input, EzCrypt can output to a file or to a string in memory. If the OutputFile property is non-empty, the output from encryption/decryption will be written to that file. Otherwise, the output will be availble in the OutputMessage string property. If a file already exists at the path indicated by OutputFile, you can specify whether to overwrite the existing file with the Overwrite property.

The .NET and Java Editions also support outputting to a stream via the SetOutputStream method.

KeyPassword (or Key and IV)

In order to encrypt or decrypt data the component must know the key. The easiest way to manage the key is by setting the KeyPassword property. This automatically calculates the Key and IV values using the PKCS5 password digest algorithm.

Alternativelly Key and IV may be set directly if desired. If IV is left empty, one will be automatically created when Encrypt or Decrypt is called.

Encrypting/Decrypting

Once these properties are set, simply call the Encrypt or Decrypt method.

Below is an example of encrypting and decrypting a file in C#: Ezcrypt ezcrypt = new Ezcrypt(); ezcrypt.UseHex = true; ezcrypt.Algorithm = EzcryptAlgorithms.ezAES; //default //encrypt a file ezcrypt.InputFile = workingDirectory + "input.txt"; ezcrypt.OutputFile = workingDirectory + "encrypted.txt"; ezcrypt.Overwrite = true; ezcrypt.KeyPassword = "encryption password"; ezcrypt.Encrypt(); //decrypt a file ezcrypt.Reset(); ezcrypt.UseHex = true; ezcrypt.InputFile = workingDirectory + "encrypted.txt"; ezcrypt.OutputFile = workingDirectory + "output.txt"; ezcrypt.Overwrite = true; ezcrypt.KeyPassword = "encryption password"; ezcrypt.Decrypt();

AES

The AES component supports symmetric encryption and decryption through Advanced Encryption Standard cryptography.

Like all symmetric encryption components, the AES component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Aes aes = new Aes(); aes.UseHex = true; //encrypt a string aes.InputMessage = "Hello World!"; aes.KeyPassword = "encryption password"; aes.Encrypt(); string encrypted = aes.OutputMessage; //decrypt the same string aes.Reset(); aes.UseHex = true; aes.InputMessage = encrypted; aes.KeyPassword = "encryption password"; aes.Decrypt(); //Hello World! Console.WriteLine(aes.OutputMessage);

Blowfish

The Blowfish component supports symmetric encryption and decryption with the Blowfish block cipher.

Like all symmetric encryption components, the Blowfish component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Blowfish blowfish = new Blowfish(); blowfish.UseHex = true; //encrypt a string blowfish.InputMessage = "Hello World!"; blowfish.KeyPassword = "encryption password"; blowfish.Encrypt(); string encrypted = blowfish.OutputMessage; //decrypt the string blowfish.Reset(); blowfish.UseHex = true; blowfish.InputMessage = encrypted; blowfish.KeyPassword = "encryption password"; blowfish.Decrypt(); //Hello World! Console.WriteLine(blowfish.OutputMessage);

CAST

The CAST component supports symmetric encryption and decryption with the CAST-128/CAST5 block cipher.

Like all symmetric encryption components, the CAST component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Cast cast = new Cast(); cast.UseHex = true; //encrypt a string cast.InputMessage = "Hello World!"; cast.KeyPassword = "encryption password"; cast.Encrypt(); string encrypted = cast.OutputMessage; //decrypt the string cast.Reset(); cast.UseHex = true; cast.InputMessage = encrypted; cast.KeyPassword = "encryption password"; cast.Decrypt(); //Hello World! Console.WriteLine(cast.OutputMessage);

ChaCha

The ChaCha component supports symmetric encryption and decryption through the ChaCha20 encryption algorithm. It also is capable of performing ChaCha20-Poly1305 AEAD for IETF as defined in RFC 7539.

Like all symmetric encryption components, the ChaCha component requires an input source, an output destination, and a Key and IV. When performing AEAD the UseAEAD, AuthTag, and AdditionalAuthData properties are also applicable.

The following is an example of encrypting and then decrypting a string in C#: Chacha chacha = new Chacha(); chacha.UseHex = true; //encrypt a string chacha.KeyB = new byte[]{ 0xBB, 0x76, 0x17, 0xC9, 0x05, 0x73, 0x4A, 0x8D, 0x59, 0x9D, 0x7B, 0x0D, 0x86, 0x2A, 0x03, 0x82, 0x50, 0x6A, 0x70, 0xFB, 0xA8, 0x56, 0x47, 0x1B, 0x1E, 0x68, 0x0B, 0x2B, 0x34, 0x18, 0x0F, 0xE2 }; chacha.IVB = new byte[] { 0x0D, 0xE4, 0x43, 0x40, 0x29, 0xAD, 0x70, 0x7D, 0x7B, 0x32, 0xB5, 0xC7 }; chacha.InputMessage = "hello chacha!"; chacha.Encrypt(); //decrypt the encrypted string chacha.InputMessage = chacha.OutputMessage; chacha.Decrypt(); Console.WriteLine(chacha.OutputMessage); //outputs "hello chacha!"

To use ChaCha to perform ChaCha20-Poly1305 AEAD start by setting UseAEAD to true.

The following is an example of encrypting and decrypting an AEAD in C#: Chacha chacha = new Chacha(); // Let's set the key, IV, and additional authentication data byte[] aad = Encoding.UTF8.GetBytes("Some data."); byte[] iv = new byte[] { 0x0D, 0xE4, 0x43, 0x40, 0x29, 0xAD, 0x70, 0x7D, 0x7B, 0x32, 0xB5, 0xC7 }; byte[] key = new byte[] { 0xBB, 0x76, 0x17, 0xC9, 0x05, 0x73, 0x4A, 0x8D, 0x59, 0x9D, 0x7B, 0x0D, 0x86, 0x2A, 0x03, 0x82, 0x50, 0x6A, 0x70, 0xFB, 0xA8, 0x56, 0x47, 0x1B, 0x1E, 0x68, 0x0B, 0x2B, 0x34, 0x18, 0x0F, 0xE2 }; // Encrypt with AEAD chacha.UseAEAD = true; chacha.AdditionalAuthDataB = aad; chacha.KeyB = key; chacha.IVB = iv; chacha.InputMessage = "Hello chacha! Let's make an AEAD"; chacha.Encrypt(); // After encrypting we have two values, the Cipher Text and the AuthTag // Some protocols require the AuthTag to be appended to the Cipher Text, // in which case set the IncludeAuthTag config to true. It is false for // our example. byte[] authTag = chacha.AuthTagB; byte[] cipherText = chacha.OutputMessageB; // Decrypt with AEAD chacha.Reset(); chacha.UseAEAD = true; chacha.AdditionalAuthDataB = aad; chacha.KeyB = key; chacha.IVB = iv; chacha.AuthTagB = authTag; chacha.InputMessageB = cipherText; chacha.Decrypt(); Console.WriteLine(chacha.OutputMessage); // Prints the original Plain Text.

DES

The DES component supports symmetric encryption and decryption through Data Encryption Standard cryptography.

Like all symmetric encryption components, the DES component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Des des = new Des(); des.UseHex = true; //encrypt a string des.InputMessage = "Hello World!"; des.KeyPassword = "encryption password"; des.Encrypt(); string encrypted = des.OutputMessage; //decrypt the string des.Reset(); des.UseHex = true; des.InputMessage = encrypted; des.KeyPassword = "encryption password"; des.Decrypt(); //Hello World! Console.WriteLine(des.OutputMessage);

IDEA

The IDEA component supports symmetric encryption and decryption with the International Data Encryption Algorithm block cipher.

Like all symmetric encryption components, the IDEA component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Idea idea = new Idea(); idea.UseHex = true; //encrypt a string idea.InputMessage = "Hello World!"; idea.KeyPassword = "encryption password"; idea.Encrypt(); string encrypted = idea.OutputMessage; //decrypt the string idea.Reset(); idea.UseHex = true; idea.InputMessage = encrypted; idea.KeyPassword = "encryption password"; idea.Decrypt(); //Hello World! Console.WriteLine(idea.OutputMessage);

RC2

The RC2 component supports symmetric encryption and decryption with the RC2 (ARC2) 64-bit block cipher.

Like all symmetric encryption components, the RC2 component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Rc2 rc2 = new Rc2(); rc2.UseHex = true; //encrypt a string rc2.InputMessage = "Hello World!"; rc2.KeyPassword = "encryption password"; rc2.Encrypt(); string encrypted = rc2.OutputMessage; //decrypt the string rc2.Reset(); rc2.UseHex = true; rc2.InputMessage = encrypted; rc2.KeyPassword = "encryption password"; rc2.Decrypt(); //Hello World! Console.WriteLine(rc2.OutputMessage);

RC4

The RC4 component supports symmetric encryption and decryption with the RC4 (ARC4) stream cipher.

Like all symmetric encryption components, the RC4 component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Rc4 rc4 = new Rc4(); rc4.UseHex = true; //encrypt a string rc4.InputMessage = "Hello World!"; rc4.KeyPassword = "encryption password"; rc4.Encrypt(); string encrypted = rc4.OutputMessage; //decrypt the string rc4.Reset(); rc4.UseHex = true; rc4.InputMessage = encrypted; rc4.KeyPassword = "encryption password"; rc4.Decrypt(); //Hello World! Console.WriteLine(rc4.OutputMessage);

Rijndael

The Rijndael component supports encryption and decryption through the Rijndael symmetric algorithm. AES uses the Rijndael algorithm underneath, so the Rijndael and AES components are very similar. The difference is that AES has a fixed block size of 128 bits, while Rijndael can support 128, 192, and 256 bit block sizes.

Like all symmetric encryption components, the Rijndael component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Rijndael rijndael = new Rijndael(); rijndael.UseHex = true; //encrypt a string rijndael.InputMessage = "Hello World!"; rijndael.KeyPassword = "encryption password"; rijndael.Encrypt(); string encrypted = rijndael.OutputMessage; //decrypt the same string rijndael.Reset(); rijndael.UseHex = true; rijndael.InputMessage = encrypted; rijndael.KeyPassword = "encryption password"; rijndael.Decrypt(); //Hello World! Console.WriteLine(rijndael.OutputMessage);

Salsa20

The Salsa20 component supports encryption and decryption through the Salsa20 and XSalsa20 symmetric algorithms.

Like all symmetric encryption components, the Salsa20 component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a file in C#: byte[] key = GetRandomBytes(32); byte[] iv = GetRandomBytes(24); Salsa20 salsa = new Salsa20(); salsa.Algorithm = Salsa20Algorithms.saXSALSA20; // Default value salsa.IVB = iv; salsa.KeyB = key; salsa.InputFile = @"C:\temp\hideme.txt"; salsa.OutputFile = @"C:\temp\hideme.txt.hidden"; salsa.Encrypt(); salsa.Reset(); salsa.IVB = iv; salsa.KeyB = key; salsa.InputFile = @"C:\temp\hideme.txt.hidden"; salsa.OutputFile = @"C:\temp\hideme.txt.hidden.revealed"; salsa.Decrypt();

TEA

The TEA component supports symmetric encryption and decryption with the Tiny Encryption Algorithm block cipher.

The Algorithm property specifies which TEA algorithm the component will use. Currently the TEA component supports the following algorithms:

  • TEA
  • XTEA
  • XXTEA

Like all symmetric encryption components, the TEA component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Tea tea = new Tea(); rijndael.UseHex = true; //encrypt a string tea.InputMessage = "Hello World!"; tea.KeyPassword = "encryption password"; tea.Encrypt(); string encrypted = tea.OutputMessage; //decrypt the string tea.Reset(); rijndael.UseHex = true; tea.InputMessage = encrypted; tea.KeyPassword = "encryption password"; tea.Decrypt(); //Hello World! Console.WriteLine(tea.OutputMessage);

TripleDES

The TripleDES component supports symmetric encryption and decryption with TripleDES, successor to the DES encryption algorithm.

Like all symmetric encryption components, the TripleDES component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Tripledes tripledes = new Tripledes(); tripledes.UseHex = true; //encrypt a string tripledes.InputMessage = "Hello World!"; tripledes.KeyPassword = "encryption password"; tripledes.Encrypt(); string encrypted = tripledes.OutputMessage; //decrypt the string tripledes.Reset(); tripledes.UseHex = true; tripledes.InputMessage = encrypted; tripledes.KeyPassword = "encryption password"; tripledes.Decrypt(); //Hello World! Console.WriteLine(tripledes.OutputMessage);

Twofish

The Twofish component supports symmetric encryption and decryption with Twofish, successor to the Blowfish block cipher.

Like all symmetric encryption components, the Twofish component requires an input source, an output destination, and a Key and IV. For details, please refer to the EzCrypt section.

The following is an example encrypting and then decrypting a string in C#: Twofish twofish = new Twofish(); twofish.UseHex = true; //encrypt a string twofish.InputMessage = "Hello World!"; twofish.KeyPassword = "encryption password"; twofish.Encrypt(); string encrypted = twofish.OutputMessage; //decrypt the string twofish.Reset(); twofish.UseHex = true; twofish.InputMessage = encrypted; twofish.KeyPassword = "encryption password"; twofish.Decrypt(); //Hello World! Console.WriteLine(twofish.OutputMessage);

Asymmetric Algorithms

IPWorks Encrypt includes components for a variety of asymmetric algorithms. Each component is specifically designed with a particular asymmetric algorithm in mind. The available properties, methods, and events differ based on the applicability to the specific algorithm. Please see details about each component listed below:

RSA

The RSA component implements RSA public-key cryptography to encrypt, decrypt, sign and verify messages.

Encryption

Encryption requires an input source, an output destination, and a public key.

For input, simply set the InputFile property to encrypt the contents of a file, or the InputMessage property to encrypt a string. For output, set the OutputFile property to output to a file, otherwise the output will be available as a string via OutputMessage. The .NET and Java editions also support inputting from and outputting to a stream; simply call SetInputStream and/or SetOutputStream with the appropriate stream. The RecipientCert property should be set to a certificate containing the appropriate public key.

Once the input, output, and public key are set, simply call the Encrypt method.

The following is an example of encrypting a string in C#: Rsa rsa = new Rsa(); rsa.UseHex = true; Certificate cert = new Certificate(CertStoreTypes.cstPFXFile, certificateFilePath, "test", "*"); rsa.RecipientCert = cert; rsa.InputMessage = "Encrypt me please!"; rsa.Encrypt(); String encryptedMessage = rsa.OutputMessage;

Decryption

Similar to encryption, decryption requires an input source and an output destination; however, while encryption requires a public key, decryption requires the corresponding private key.

The Certificate property should be set to a certificate with the appropriate private key for decryption. Input and output work identically to encryption; please see the encyption section for details.

Once the input, output, and private key are set, simply call the Decrypt method.

The following is an example of decrypt a string in C#: Rsa rsa = new Rsa(); rsa.UseHex = true; Certificate cert = new Certificate(CertStoreTypes.cstPFXFile, certificateFilePath, "test", "*"); rsa.Certificate = cert; rsa.InputMessage = encryptedMessage; rsa.Decrypt(); Console.WriteLine(rsa.OutputMessage);

Signing

Signing requires an input source and a private key. The InputFile or InputMessage properties should be set to the file or string containing the message. The Certificate property should be set to a certificate with the desired private key.

After specifying the input source and private key, simply call the Sign method. The HashSignature property is automatically populated with the result.

The following is an example of signing a string in C#: Rsa rsa = new Rsa(); rsa.UseHex = true; Certificate cert = new Certificate(CertStoreTypes.cstPFXFile, certificateFilePath, "test", "*"); rsa.Certificate = cert; rsa.InputMessage = "sign me please!"; rsa.Sign(); string signature = rsa.HashSignature;

Verifying

Verifying a signature requires the message that was signed, the signature, and the public key that corresponds to the private key used to create the signature. The InputFile or InputMessage properties should be set to the file or string containing the signed message. The HashSignature property should be set to the signature. Then, the SignerCert property should be set to the certificate containing the appropriate public key.

After setting the input, signature, and public key, simply call the VerifySignature method. This method will return true if the verification succeeds.

The following is an example of verifying a signature in C#: rsa.Reset(); rsa.UseHex = true; rsa.SignerCert = cert; rsa.InputMessage = dataToSign; rsa.HashSignature = signature; bool validSignature = rsa.VerifySignature();

A Note About Message Length

RSA has an upper limit to the amount of data that can be encrypted or decrypted, also known as message length. This length can typically be calculated as the size of the key minus the size of the RSA header and padding.

When not using OAEP, the following formula and table can be referenced. (RSA Key Bytes) - (Header Bytes) = Length of data, where Header Bytes is always 11.

RSA Key Length (bits)Length (bits)Length (bytes)
1024 936 117
2048 1960 245
3072 2984 373
4096 4008 501

When using OAEP, the following formula and table can be referenced. (RSA Key Bytes) - (2 * Hash Length Bytes) - 2 = Length of data. The table below assumes SHA-256 for the hash, so Hash Length Bytes is 32.

RSA Key Length (bits)Length (bits)Length (bytes)
1024 496 62
2048 1520 190
3072 2544 318
4096 3568 446

DSA

The DSA component implements DSA public-key cryptography to encrypt, decrypt, sign and verify messages.

Signing

Signing requires an input source and a private key. The InputFile and InputMessage properties should be used to set the input to a file or string respectively. The Certificate property should be set to a certificate with the desired private key. After calling Sign, the signature will be available through HashSignature

Verifying

Verifying a signature requires the message that was signed, the signature, and the public key that corresponds to the private key used to create the signature. The InputFile or InputMessage properties should be set to the file or string containing the message. The HashSignature property should be set to the signature. Then, the SignerCert property should be set to the certificate containing the appropriate public key. After this is set, the VerifySignature method will return true if the signature is successfully verified.

Example

The following is an example signing and then verifying a signature in C#: Dsa dsa = new Dsa(); dsa.UseHex = true; //sign a message Certificate cert = new Certificate(CertStoreTypes.cstPFXFile, privateCertificatePath, "test", "*"); dsa.Certificate = cert; string dataToSign = "sign me please!"; dsa.InputMessage = dataToSign; dsa.Sign(); string signature = dsa.HashSignature; //verify the signed message dsa.Reset(); dsa.UseHex = true; dsa.SignerCert = cert; dsa.InputMessage = dataToSign; dsa.HashSignature = signature; bool validSignature = dsa.VerifySignature(); Console.WriteLine(validSignature);

OpenPGP

The OpenPGP component provides an easy way to encrypt, decrypt, sign, and verify OpenPGP messages. Key management features are also supported through the use of the KeyMgr component.

Please see the Getting Started with OpenPGP guide for a complete walkthrough of OpenPGP.

SMIME

The SMIME component provides encryption, decryption, signing, and verifying functionality for MIME data.

Encrypting

Encryption requires a public key and MIME input data. The RecipientCerts property should be populated with the certificates containing the public key for each recipient. These can be easily added with the AddRecipientCert method. Then, the Message property should be set to the input data before calling Encrypt.

The following is an example of encrypting some MIME data in C#: Smime smime = new Smime(); Certificate cert = new Certificate(CertStoreTypes.cstPFXFile, certificateFilePath, "test", "*"); smime.RecipientCerts.Add(cert); smime.InputMessage = "Content-Type: text/plain \r\n here is some data \r\n --XXXXboundary text "; smime.Encrypt(); string encrypted = smime.OutputMessage;

Decrypting

Decryption requires the private key associated with the public key used to encrypt. The Certificate property should be set to the certificate containing the appropriate private key, and the Message property should be set to the data to decrypt. Then, simply call the Decrypt method.

The following is an example of encrypting some MIME data in C#: smime.Reset(); smime.InputMessage = encrypted; smime.Certificate = cert; smime.Decrypt(); Console.WriteLine(smime.OutputMessage);

Signing

Signing requires a valid private key. The Certificate property should be set to a certificate containing a private key, and the Message property should be set to the data to sign. The preferred method of building signed messages involves including a digital certificate in the message signature. This can be accomplished by setting the IncludeCertificate property to true.

The SMIME component can sign and encrypt a message in the same step. To do this, set all of the properties required for both signing and encrypting and then call SignAndEncrypt.

Verifying

To verify a message, simply set the Message property and call VerifySignature. If a certificate is attached to the message, the component will attempt to verify the signature using the attached certificate. Otherwise, the component will attempt to verify the signature with the certificate supplied in SignerCert. If the signature cannot be succesfully verified, VerifySignature will throw an exception. If VerifySignature returns succesfully, the signature was verified.

The SMIME component can decrypt and verify a message in the same step. To accomplish this, set all of the properties requried for both decrypting and verifying and call DecryptAndVerifySignature.

Elgamal

The Elgamal component provides functionality for encryption and decryption via the Elgamal asymmetric encryption algorithm. The behavior of this component closely resembles the encryption and decryption behavior of the RSA component. However, the Elgamal component does not support certificates through the Certificate and RecipientCert properties; instead, the Key and RecipientKey properties must be used.

You can load your own keys, or simply call CreateKey to generate a new key pair. Note that CreateKey will populate the Key property, and to encrypt the RecipientKey property will need to be set to the public key.

If the data is stored in a file, set InputFile to the appropriate file path. Otherwise, set the InputMessage property to the string containing the data. The .NET and Java editions also support inputting from and outputting to a stream; simply call SetInputStream with the appropriate stream.

The following is an example encrypting and then decrypting a string in C#: Elgamal elgamal = new Elgamal(); elgamal.UseHex = true; //encrypt a string elgamal.CreateKey(); elgamal.InputMessage = "Encrypt me please!"; string pubKey = elgamal.Key.PublicKey; elgamal.RecipientKey.PublicKey = pubKey; elgamal.Encrypt(); string encryptedMessage = elgamal.OutputMessage; string privKey = elgamal.Key.PrivateKey; //decrypt the string elgamal.Reset(); elgamal.UseHex = true; elgamal.InputMessage = encryptedMessage; elgamal.Key.PrivateKey = privKey; elgamal.Decrypt(); Console.WriteLine(elgamal.OutputMessage);

XML Signing and Encrypting

The toolkit includes two components for XML signing and encryption. XMLSig supports signing XML and verifying signatures. The XMLEncrypt component supports encrypting and decrypting XML. Both may be used together to encrypt and sign a document, or decrypt and verify a document. Please see the sections below for details:

XMLSig

The XMLSig component provides the ability to sign and verify XML.

Signing

Signing requires an input source, a set of XML elements to sign, and a certificate with a private key. If the data to sign is stored in a file, InputFile should be set to the file path. Otherwise, the InputXML property should be set to the XML data in string format. The References property should include a reference for each XML element to sign. The Certificate property should be set to a certificate with a private key.

Once these properties are set, simply call Sign. The output will be available in the file specified by OutputFile, or if no file is specified, in OutputXML.

Verifying

Verifying a signature requires an input source, calling VerifySignature, and handling the SignatureInfo event. If the data to sign is stored in a file, InputFile should be set to the file path. Otherwise, the InputXML property should be set to the XML data in string format.

Once VerifySignature is called, the SignatureInfo event will fire once for every signature that is found in the input XML, and the References property will automatically be populated. Inside the SignatureInfo event, the XMLElement property of the current reference should be set to the XML element to which the signature applies. The easiest way to do this is to use the name of the element. If there are multiple elements with the same name, you will need to use the XPath to identify the element.

The component will automatically parse the signer's certificate if it is included with the signed XML. Otherwise, the SignerCert property should be set to the certificate containing the signer's public key.

Example

The following is an example of signing and verifying a simple XML string in C#: Xmlsig xmlsig = new Xmlsig(); Certificate cert = new Certificate(CertStoreTypes.cstPFXFile, certificateFilePath, "test", "*"); xmlsig.Certificate = cert; xmlsig.InputXML = "data"; XMLSigReference reference = new XMLSigReference(); reference.XMLElement = "myTag"; xmlsig.References.Add(reference); xmlsig.Sign(); string signed = xmlsig.OutputXML; xmlsig.Reset(); xmlsig.InputXML = signed; xmlsig.OnSignatureInfo += (obj, ev) => { xmlsig.References[0].XMLElement = "myTag"; }; xmlsig.VerifySignature();

XMLEncrypt

The XMLEncrypt component provides the ability to encrypt and decrypt XML using symmetric encryption. The component also allows for asymmetric encryption to encrypt the shared symmetric key.

Encrypting

Encrypting requires an input source and a symmetric key. If the data to encrypt is stored in a file, InputFile should be set to the file path. Otherwise, the InputXML property should be set to the XML data in string format. The SymmetricKey property should be set to the symmetric key used for encryption (also known as a session key). Optionally, the EncryptingAlgorithm property can be set to specify which symmetric algorithm to use. Note that the SymmetricKey must be of appropriate length according to the chosen algorithm; 128 bits for AES and 3DES, and 64 bits for DES.

To encrypt the symmetric key and send it along with the encrypted data, set the RecipientCert property to the certificate containing the recipient's public key. If RecipientCert is not set, the recipient must have prior knowledge of the symmetric key in order to decrypt the data.

Once these properties are set, simply call Encrypt. The output will be available in the file specified by OutputFile, or if no file is specified, in OutputXML.

The following is an example of encrypting an XML tag in C#: Xmlencrypt xmlencrypt = new Xmlencrypt(); Certificate cert = new Certificate(CertStoreTypes.cstPFXFile, certificateFilePath, "test", "*"); xmlencrypt.RecipientCert = cert; xmlencrypt.InputXML = "data"; xmlencrypt.EncryptingAlgorithm = "AES128"; xmlencrypt.SymmetricKey = "1234567890123456"; xmlencrypt.Encrypt(); string encrypted = xmlencrypt.OutputXML;

Decrypting

Decrypting requires an input source and either a symmetric key or a certificate with a private key. If the data to decrypt is stored in a file, InputFile should be set to the file path. Otherwise, the InputXML property should be set to the XML data in string format. If the symmetric key (or session key) was encrypted and sent along with the data, the Certificate property should be set to the certificate with the appropriate private key. Otherwise, the SymmetricKey property should be set to the shared symmetric key.

Note that the SymmetricKey property can be set in the EncryptedDataInfo event as well as in the main flow of the program. If there are multiple encrypted elements that use different symmetric keys, the SymmetricKey property will need to be updated in the EncryptedDataInfo event, which fires once for each encrypted element.

Once these properties are set, simply call Decrypt. The output will be available in the file specified by OutputFile, or if no file is specified, in OutputXML.

The following is an example of encrypting an XML tag in C#: xmlencrypt.Reset(); xmlencrypt.InputXML = encrypted; xmlencrypt.Certificate = cert; xmlencrypt.Decrypt(); Console.WriteLine(xmlencrypt.OutputXML);

ECC

For details on the ECC (Elliptic Curve Cryptography) component, please see our dedicated ECC article here.

DPAPI

The DPAPI component provides an easy interface for protecting data using the Microsoft Windows Data Protection API. Protected data is secured by the operating system and may be unprotected at a later time.

The data to protect should be specified by InputFile if it is stored in a file, otherwise it should be passed as a string to InputMessage. Optionally, you can set a password with the Password property. Once the input is set, simply call Protect. Once the data no longer needs to be protected, simply call Unprotect.

EzRand

The EzRand component provides multiple algorithms by which to generate random numbers or bytes.

Random Numbers

To generate a random number, simply specify the Algorithm and Min and Max properties. Then, calling GetNextInt will populate RandInt with a random number.

Random Bytes

To generate a random sequence of bytes, simply set the RandBytesLength property to the desired length and call GetNextBytes. The result will be available in the RandBytes property.

Hash

The Hash component provides an easy way to compute a hash using various algorithms.

Computing a hash requires an input source and a hash algorithm. If the InputFile property is set, the component will read the input from that file. Otherwise, the InputMessage property should contain the input in string format. Once the input is set, simply specify an algorithm with the Algorithm property and call ComputeHash. The result is available in the HashValue property.

Some of the supported algorithms are:

  • SHA-256
  • SHA-1
  • SHA-224
  • SHA-384
  • SHA-512
  • MD-5
  • HMAC-SHA1
  • HMAC-SHA256
  • RIPEMD-160
  • SHA-3-256
  • And more!

Poly1305

The Poly1305 component provides an easy way to compute a MAC (Message Authentication Code) with the Poly1305 algorithm.

Computing a MAC requires an input source and a Key. The key must be 256 bits (32 bytes) and will produces a 128 bit (16 byte) MAC. Once the input is set, simply call ComputeMAC. The result is available in the MACValue property.

The following is an example of computing a MAC from a string in C#: Poly1305 poly1305 = new Poly1305(); poly1305.KeyB = new byte[] { 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0 }; poly1305.InputMessage = "Hey, let's compute a MAC for this message!"; poly1305.EncodeMAC = true; // When set to true, the MAC will be hex-encoded. poly1305.ComputeMAC(); Console.WriteLine(poly1305.MACValue);

PBKDF

The PBKDF component provides the ability to derive a key from a password using the PBDKF password digest algorithms.

To begin, set the Password and Salt properties as inputs for the digest. Optionally, specify the KeyLength and Iterations properties for finer control over security and performance. Then, simply call CreateKey and access the new key through the Key property.

Some of the supported algorithms are:
  • HMAC-SHA1
  • HMAC-SHA256
  • HMAC-SHA224
  • HMAC-SHA284
  • HMAC-SHA512
  • HMAC-MD5
  • HMAC-RIPEMD160
  • And more!

JWE Encrypting and Decrypting

The JWE component supports encrypting and decrypting JSON Web Encryption (JWE) messages.

Specify any payload via input properties and use Encrypt to create a JWE message using a variety of algorithms including ECDH, RSA, and AES. Use Decrypt to decrypt the payload of any received JWE message. The following algorithms are supported:

  • RSA1_5
  • RSA-OAEP
  • RSA-OAEP-256
  • A128KW
  • A192KW
  • A256KW
  • dir
  • ECDH-ES
  • ECDH-ES+A128KW
  • ECDH-ES+A192KW
  • ECDH-ES+A256KW
  • A128GCMKW
  • A192GCMKW
  • A256GCMKW
  • PBES2-HS256+A128KW
  • PBES2-HS384+A192KW
  • PBES2-HS512+A256KW

See the algorithm notes sections below for more details about specific algorithms.

Encrypting

The Encrypt method may be used to encrypt a payload with a variety of algorithms.

JSON Web Encryption (JWE) is performed by first generating a random key used to encrypt the content. The content encryption key is used to encrypt the content using the algorithm specified by ContentEncryptionAlgorithm. The content encryption key is then encrypted itself using the algorithm specified by EncryptionAlgorithm. The content encryption key is not directly exposed in the API as it is randomly generated.

After calling this method the compact serialized JWE string is written to the specified output location. For instance:

eyJhbGciOiJBMjU2R0NNS1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiaXYiOiJMa0tNeTZ5Qlpfbzh6QW92IiwidGFnIjoiSmpMTkRsV3l3bWt3V2pMa0NLU0xxQSJ9.wiwySYm6fXZre-3IdT1tb_02KMQDrMICwUawVf7Gjhc.k84s7ne8J41QnA5BQ31k_A.kjIveRjjNYV4x92CVE9Agw.uAygkyeO2KWeFQIy9JLU0A

The component is agnostic of the payload that is encrypted. Any value may be encrypted. KeyId may be set to include an identifier to help the receiving party identify the key or certificate used to encrypt the data. The following properties are applicable when calling this method:

  • EncryptionAlgorithm (required)
  • Key (conditional - required for AES)
  • KeyPassword (conditional - required for PBES)
  • Certificate (conditional - required for ECDH and RSA)
  • ContentEncryptionAlgorithm
  • CompressionAlgorithm
  • HeaderParams
  • Overwrite

Input and Output Properties

The component will determine the source and destination of the input and output based on which properties are set.

The order in which the input properties are checked is as follows:

  • SetInputStream
  • InputFile
  • InputMessage

When a valid source is found the search stops. The order in which the output properties are checked is as follows:

  • SetOutputStream
  • OutputFile
  • OutputMessage

Notes for AES Algorithms (A128KW, A192KW, A256KW, A128GCMKW, A192GCMKW, A256GCMKW)

When EncryptionAlgorithm is set to a AES algorithm, Key must be set to a key of appropriate length for the algorithm. For instance a 256 bit key would be used for A256KW.

The example below uses the EzRand component to generate a key, but the key may be created using any method. The key must be known by both parties in order for encryption and decryption to take place.

//Generate a 256 bit (32 byte) key Ezrand rand = new Ezrand(); rand.RandBytesLength = 32; rand.GetNextBytes(); byte[] key = rand.RandBytesB; //Encrypt the payload using A256KW Jwe jwe = new Jwe(); jwe.KeyB = key; jwe.InputMessage = "test data"; jwe.EncryptionAlgorithm = JweEncryptionAlgorithms.eaA256KW; jwe.Encrypt(); string encryptedData = jwe.OutputMessage;

To use an existing AES key provide the bytes to the Key property. For instance: byte[] key = new byte[] { 164, 60, 194, 0, 161, 189, 41, 38, 130, 89, 141, 164, 45, 170, 159, 209, 69, 137, 243, 216, 191, 131, 47, 250, 32, 107, 231, 117, 37, 158, 225, 234 }; //Encrypt the payload using A256KW Jwe jwe = new Jwe(); jwe.KeyB = key; jwe.InputMessage = "test data"; jwe.EncryptionAlgorithm = JweEncryptionAlgorithms.eaA256KW; jwe.Encrypt(); string encryptedData = jwe.OutputMessage;

Notes for RSA Algorithms (RSA1_5, RSA-OEAP, RSA-OAEP-256)

The RSA based algorithms use asymmetric encryption. Encrypting is done with a public key and decryption is done with a private key. The public certificate should be in PEM (base64) format.

The following is an example of encrypting using an RSA algorithm: Jwe jwe = new Jwe(); jwe.Certificate = new Certificate("..\\recipient.cer"); jwe.InputMessage = "test data"; jwe.EncryptionAlgorithm = JweEncryptionAlgorithms.eaRSA_OAEP; jwe.Encrypt(); string encryptedData = jwe.OutputMessage;

Notes for ECDH Algorithms (ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW)

ECDH algorithms require a valid ECC public key to encrypt the message. If the key was originally created with the ECC component the PEM encoded PublicKey may be used directly with the Certificate property. An example PEM encoded public certificate created by the ECC component:

-----BEGIN PUBLIC KEY-----
MIIBMjCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAA
AAD///////////////8wRAQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrG
NdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt
6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA
//////////+85vqtpxeehPO5ysL8YyVRAgEBA0EEIC5rbLp11Mnz6cBXLLriaDIov3rm8RAY
x/OR0bOKiff0cQy+sLVaxjseqFk/+Xvl4ORSv5Z6HdHv5GyEpA0UoA==
-----END PUBLIC KEY-----

The following is an example of encrypting using a public certificate created with the ECC component: Jwe jwe = new Jwe(); jwe.Certificate = new Certificate(CertStoreTypes.cstPublicKeyFile, pubKeyFile, "", "*"); jwe.InputMessage = "test data"; jwe.EncryptionAlgorithm = JweEncryptionAlgorithms.eaECDH_ES_A256KW; jwe.Encrypt(); string encryptedData = jwe.OutputMessage;

To use an ECC public key created by other means the ECC component may be used to import the key parameters. Populate the Rx and Ry properties of the ECC component first to obtain the PEM formatted public key.

The following is an example of encrypting using existing key parameters: byte[] x_bytes = new byte[] { 171, 170, 196, 151, 94, 196, 231, 12, 128, 232, 17, 61, 45, 105, 41, 209, 192, 187, 112, 242, 110, 178, 95, 240, 36, 55, 83, 171, 190, 176, 78, 13 }; byte[] y_bytes = new byte[] { 197, 75, 134, 245, 245, 28, 199, 9, 7, 117, 1, 54, 49, 178, 135, 252, 62, 89, 35, 180, 117, 80, 231, 23, 110, 250, 28, 124, 219, 253, 224, 156 }; Ecc ecc = new Ecc(); ecc.Key.RxB = x_bytes; ecc.Key.RyB = y_bytes; string pubKey = ecc.Key.PublicKey; Jwe jwe = new Jwe(); jwe.Certificate = new Certificate(CertStoreTypes.cstPublicKeyFile, pubKey, "", "*"); jwe.InputMessage = "test data"; jwe.EncryptionAlgorithm = JweEncryptionAlgorithms.eaECDH_ES_A256KW; jwe.Encrypt(); string encryptedData = jwe.OutputMessage;

Notes for PBES Algorithms (PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW

PBES algorithms derive a content encryption key from the KeyPassword property. Set KeyPassword to a shared secret, such as in the below example.

Jwe jwe = new Jwe(); jwe.KeyPassword = "secret"; jwe.InputMessage = "test data"; jwe.EncryptionAlgorithm = JweEncryptionAlgorithms.eaPBES2_HS512_A256KW; jwe.Encrypt(); string encryptedData = jwe.OutputMessage;

Notes for Direct Shared Keys

When EncryptionAlgorithm is set to Direct, the Key property must be set to a valid symmetric key that will be used directly by the ContentEncryptionAlgorithm. In this case a content encryption key is not generated randomly, the Key is used instead. The length of the specified Key must be valid for the selected ContentEncryptionAlgorithm.

The following is an example of encrypting using such a key: //Generate a 256 bit (32 byte) key Ezrand rand = new Ezrand(); rand.RandBytesLength = 32; rand.GetNextBytes(); byte[] key = rand.RandBytesB; Jwe jwe = new Jwe(); jwe.EncryptionAlgorithm = JweEncryptionAlgorithms.eaDir; jwe.ContentEncryptionAlgorithm = JweContentEncryptionAlgorithms.ceaA256GCM; jwe.KeyB = key; jwe.InputMessage = "test data"; jwe.Encrypt(); string encryptedData = jwe.OutputMessage;

Decrypting

The Decrypt method may be used to decrypt a received JWE message.

Before calling the Decrypt method set InputMessage or InputFile to a valid compact serialized JWE string. For instance:

eyJhbGciOiJBMjU2R0NNS1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiaXYiOiJMa0tNeTZ5Qlpfbzh6QW92IiwidGFnIjoiSmpMTkRsV3l3bWt3V2pMa0NLU0xxQSJ9.wiwySYm6fXZre-3IdT1tb_02KMQDrMICwUawVf7Gjhc.k84s7ne8J41QnA5BQ31k_A.kjIveRjjNYV4x92CVE9Agw.uAygkyeO2KWeFQIy9JLU0A

The type and format of the private key depends on the algorithm used to encrypt the data. The following table summarizes the relationship:

Algorithm Private Key Location
AES Key
RSA and ECDH Certificate
PBES KeyPassword

If the correct Key or Certificate is not known ahead of time the KeyId parameter of the RecipientInfo event may be used to identify the correct key.

If this method returns without error decryption was successful. If decryption fails then this method fails with an error. After calling this method the payload will be present in the OutputMessage or file specified by OutputFile, and HeaderParams will contain the headers. Headers of the parsed message are also available through the HeaderParam event.

The following properties are applicable when calling this method:

  • Certificate (conditional - required for RSA and ECDH)
  • Key (conditional - required for AES)
  • ContentEncryptionAlgorithm (only if StrictValidation is True)
  • EncryptionAlgorithm (only if StrictValidation is True)
  • HeaderParams
  • Overwrite
  • StrictValidation

Input and Output Properties

The component will determine the source and destination of the input and output based on which properties are set.

The order in which the input properties are checked is as follows:

  • SetInputStream
  • InputFile
  • InputMessage

When a valid source is found the search stops. The order in which the output properties are checked is as follows:

  • SetOutputStream
  • OutputFile
  • OutputMessage

Notes for AES Algorithms (A128KW, A192KW, A256KW, A128GCMKW, A192GCMKW, A256GCMKW)

To decrypt messages that use AES encryption Key must be set to a key of appropriate length for the algorithm. For instance a 256 bit key would be used for A256KW.

The key must be known by both parties in order for encryption and decryption to take place.

The following is an example of decrypting using an AES algorithm: byte[] key = new byte[] { 164, 60, 194, 0, 161, 189, 41, 38, 130, 89, 141, 164, 45, 170, 159, 209, 69, 137, 243, 216, 191, 131, 47, 250, 32, 107, 231, 117, 37, 158, 225, 234 }; Jwe jwe = new Jwe(); jwe.KeyB = key; jwe.InputMessage = encryptedData; jwe.Decrypt(); string decryptedData = jwe.OutputMessage;

Notes for RSA Algorithms (RSA1_5, RSA-OEAP, RSA-OAEP-256)

The RSA based algorithms use asymmetric encryption. Encrypting is done with a public key and decryption is done with a private key. The certificate with private key must be specified.

The following is an example of decrypting using an RSA algorithm: Jwe jwe = new Jwe(); jwe.Certificate = new Certificate(CertStoreTypes.cstPFXFile, "..\\jwt.pfx", "password", "*"); jwe.InputMessage = encryptedData; jwe.Decrypt(); string decryptedData = jwe.OutputMessage;

Notes for ECDH Algorithms (ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW)

ECDH algorithms require a valid ECC private key to decrypt the message. If the key was originally created with the ECC component the PEM encoded PrivateKey may be used directly with the Certificate property.

The following is an example of decrypting using a PEM key file: Jwe jwe = new Jwe(); jwe.Certificate = new Certificate(CertStoreTypes.cstPEMKeyFile, privKeyFile, "", "*"); jwe.InputMessage = encryptedData; jwe.Decrypt(); string decryptedData = jwe.OutputMessage;

To use an ECC private key created by other means the ECC component may be used to import the key parameters. Populate the Rx, Ry, and KB properties of the ECC component first to obtain the PEM formatted public key.

The following is an example of decrypting using existing key parameters: Ecc ecc = new Ecc(); byte[] x_bytes = new byte[] { 171, 170, 196, 151, 94, 196, 231, 12, 128, 232, 17, 61, 45, 105, 41, 209, 192, 187, 112, 242, 110, 178, 95, 240, 36, 55, 83, 171, 190, 176, 78, 13 }; byte[] y_bytes = new byte[] { 197, 75, 134, 245, 245, 28, 199, 9, 7, 117, 1, 54, 49, 178, 135, 252, 62, 89, 35, 180, 117, 80, 231, 23, 110, 250, 28, 124, 219, 253, 224, 156 }; byte[] k_bytes = new byte[] { 81, 65, 201, 24, 235, 249, 162, 148, 169, 150, 109, 181, 61, 238, 145, 122, 31, 30, 151, 94, 239, 90, 222, 217, 63, 103, 54, 2, 176, 232, 248, 168 }; ecc.Key.RxB = x_bytes; ecc.Key.RyB = y_bytes; ecc.Key.KB = k_bytes; string privKey = ecc.Key.PrivateKey; Jwe jwe = new Jwe(); jwe.Certificate = new Certificate(CertStoreTypes.cstPEMKeyBlob, privKey, "", "*"); jwe.InputMessage = encryptedData; jwe.Decrypt(); string decryptedData = jwe.OutputMessage;

Notes for PBES Algorithms (PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW

PBES algorithms derive a content encryption key from the KeyPassword property. Set KeyPassword to the shared secret, such as in the below example.

Jwe jwe = new Jwe(); jwe.KeyPassword = "secret"; jwe.InputMessage = encryptedData; jwe.Decrypt(); string decryptedData = jwe.OutputMessage;

Notes for Direct Shared Keys

When Direct encryption is used the Key property must be set to a valid symmetric key that will be used directly by the ContentEncryptionAlgorithm.

The following is an example of decrypting using Direct Shared Keys: byte[] key = new byte[] { 164, 60, 194, 0, 161, 189, 41, 38, 130, 89, 141, 164, 45, 170, 159, 209, 69, 137, 243, 216, 191, 131, 47, 250, 32, 107, 231, 117, 37, 158, 225, 234 }; Jwe jwe = new Jwe(); jwe.KeyB = key; jwe.InputMessage = encryptedData; jwe.Decrypt(); string decryptedData = jwe.OutputMessage;

Other Functionality

In addition to standard encrypting and decrypting the component also supports a variety of other features including:

  • Adding custom header parameters with AddHeaderParam
  • Enforcing algorithm restrictions when decrypting by setting StrictValidation
  • Inspect the JWE headers without decrypting by calling Parse

JWS Signing and Verifying

The JWS component supports signing and verifying JSON Web Signatures (JWS). Specify any payload via input properties and use Sign to create a JWS message using a variety of algorithms including HMAC, RSA, and ECDSA. Use Verify to verify the signature of any received JWS message. The following algorithms are supported:

  • HS256
  • HS384
  • HS512
  • RS256
  • RS384
  • RS512
  • PS256
  • PS384
  • PS512
  • ES256
  • ES384
  • ES512
  • None

See the algorithm notes sections below for more information on signing or verifying with specific algorithms.

Signing

The Sign method may be used to sign a payload with a variety of algorithms.

Before calling the Sign method set Algorithm to the algorithm which will be used to sign the message. The result of signing is a compact serialized JWS string. For instance:

eyJhbGciOiJIUzI1NiJ9.dGVzdA.o_JihJlCwvBO1AgY_Ao3_VBivdFmj3ufv3ZWAqYF4Ow

The component is agnostic of the payload that is signed. Any value may be signed. KeyId may be set to include an identifier to help the receiving party identify the key used to sign the message. The following properties are applicable when calling this method:

  • Algorithm (required)
  • Certificate (conditional - required for ECDSA and RSA)
  • Key (conditional - required for HMAC)
  • HeaderParams
  • KeyId
  • Overwrite

Input and Output Properties

The component will determine the source and destination of the input and output based on which properties are set.

The order in which the input properties are checked is as follows:

  • SetInputStream
  • InputFile
  • InputMessage

When a valid source is found the search stops. The order in which the output properties are checked is as follows:

  • SetOutputStream
  • OutputFile
  • OutputMessage

Notes for HMAC Algorithms (HS256, HS384, HS512)

When Algorithm is set to a HMAC algorithm, Key must be set to a key of appropriate length for the algorithm. The key should be the same number of bits as the algorithm being used. For instance a 256 bit key would be used for HS256.

The example code below uses the EzRand component to generate a key, but the key may be created using any means. The key must be known by both parties in order for signing and verification to take place.

//Generate a 256 bit (32 byte) key Ezrand ezrand = new Ezrand(); ezrand.RandBytesLength = 32; ezrand.GetNextBytes(); byte[] key = ezrand.RandBytesB; //Sign the payload using HS256 Jws jws = new Jws(); jws.Algorithm = JwsAlgorithms.jwsHS256; jws.InputMessage = "test data"; jws.KeyB = key; jws.Sign(); string signedData = jws.OutputMessage;

To use an existing HMAC key provide the bytes to the Key property. For instance: //HMAC SHA-256 Key byte[] key = new byte[] { 170, 171, 221, 209, 7, 181, 48, 178, 48, 118, 242, 132, 36, 218, 74, 140, 216, 165, 161, 70, 11, 42, 246, 205, 235, 231, 19, 48, 87, 141, 122, 10 }; //Sign the payload using HS256 Jws jws = new Jws(); jws.Algorithm = JwsAlgorithms.jwsHS256; jws.InputMessage = "test data"; jws.KeyB = key; jws.Sign(); string signedData = jws.OutputMessage;

Notes for RSA Algorithms (RS256, RS384, RS512, PS256, PS384, PS512)

The RSA based algorithms use asymmetric encryption. Signing is done with a private key and verification is done with a public key. The private key may be in PFX or PEM format.

The following is an example of signing with an RSA algorithm: Jws jws = new Jws(); jws.Algorithm = JwsAlgorithms.jwsRS256; jws.Certificate = new Certificate(CertStoreTypes.cstPFXFile, "..\\jwt.pfx", "test", "*"); jws.InputMessage = "test"; jws.Sign(); string signedMessage = jws.OutputMessage;

Notes for ECDSA Algorithms (ES256, ES384, ES512)

ECDSA algorithms require a valid ECC private key to sign. The ECC component can be used to create or import an ECC key into the Certificate format accepted by the JWS component.

The following is an example of signing with an ECDSA algorithm: //Create an ECC key with SHA-256 Ecc ecc = new Ecc(); ecc.HashAlgorithm = EccHashAlgorithms.ehaSHA256; ecc.CreateKey(); string privKey = ecc.Key.PrivateKey; //Sign the payload using ES256 Jws jws = new Jws(); jws.Algorithm = JwsAlgorithms.jwsES256; jws.Certificate = new Certificate(CertStoreTypes.cstPEMKeyBlob, privKey, "", "*"); jws.InputMessage = "test"; jws.Sign(); string signedMessage = jws.OutputMessage;

To use an existing ECC Key populate the Rx, Ry, and K values of the Key property in the ECC component first. For instance: //Import an existing ECC private key Ecc ecc = new Ecc(); byte[] x_bytes = new byte[] { 171, 170, 196, 151, 94, 196, 231, 12, 128, 232, 17, 61, 45, 105, 41, 209, 192, 187, 112, 242, 110, 178, 95, 240, 36, 55, 83, 171, 190, 176, 78, 13 }; byte[] y_bytes = new byte[] { 197, 75, 134, 245, 245, 28, 199, 9, 7, 117, 1, 54, 49, 178, 135, 252, 62, 89, 35, 180, 117, 80, 231, 23, 110, 250, 28, 124, 219, 253, 224, 156 }; byte[] k_bytes = new byte[] { 81, 65, 201, 24, 235, 249, 162, 148, 169, 150, 109, 181, 61, 238, 145, 122, 31, 30, 151, 94, 239, 90, 222, 217, 63, 103, 54, 2, 176, 232, 248, 168 }; ecc.Key.RxB = x_bytes; ecc.Key.RyB = y_bytes; ecc.Key.KB = k_bytes; string privKey = ecc.Key.PrivateKey; //Sign the payload using ES256 Jws jws = new Jws(); jws.Algorithm = JwsAlgorithms.jwsES256; jws.Certificate = new Certificate(CertStoreTypes.cstPEMKeyBlob, privKey, "", "*"); jws.InputMessage = "test"; jws.Sign(); string signedMessage = jws.OutputMessage;

Notes for Unsecured (none)

To create a JWS token without any security set Algorithm to jwsNone. For instance: Jws jws = new Jws(); jws.Algorithm = JwsAlgorithms.jwsNone; jws.InputMessage = "test"; jws.Sign(); string unsecuredMessage = jws.OutputMessage;

Verifying

The Verify method may be used to verify a received JWS message. Before calling the Verify method set InputMessage or InputFile to a valid compact serialized JWS string. For instance:

eyJhbGciOiJIUzI1NiJ9.dGVzdA.o_JihJlCwvBO1AgY_Ao3_VBivdFmj3ufv3ZWAqYF4Ow

Key or Certificate should be set to the HMAC key or public certificate respectively. If the correct Key or Certificate is not known ahead of time the KeyId parameter of the SignerInfo event may be used to identify the correct key.

If this method returns without error verification it was successful. If verification fails then this method fails with an error. After calling this method the payload will be present in the OutputMessage or file specified by OutputFile and the HeaderParams property will contain the headers. Headers of the parsed message are also available through the HeaderParam event.

The following properties are applicable when calling this method:

  • Key (conditional - required for HMAC)
  • Certificate (conditional - required for ECDSA and RSA)
  • Algorithm (only if StrictValidation is True)
  • Overtime
  • StrictValidation

Input and Output Properties

The component will determine the source and destination of the input and output based on which properties are set.

The order in which the input properties are checked is as follows:

  • SetInputStream
  • InputFile
  • InputMessage

When a valid source is found the search stops. The order in which the output properties are checked is as follows:

  • SetOutputStream
  • OutputFile
  • OutputMessage

Notes for HMAC Algorithms (HS256, HS384, HS512)

When verifying a message originally signed with an HMAC algorithm, Key must be set to the same key used during signing. The key must be known by both parties in order for signing and verification to take place.

The following is an example of verifying with a known HMAC key: byte[] key = new byte[] { 170, 171, 221, 209, 7, 181, 48, 178, 48, 118, 242, 132, 36, 218, 74, 140, 216, 165, 161, 70, 11, 42, 246, 205, 235, 231, 19, 48, 87, 141, 122, 10 }; Jws jws = new Jws(); jws.KeyB = key; jws.InputMessage = signedData; jws.Verify(); string verifiedPayload = jws.OutputMessage;

Notes for RSA Algorithms (RS256, RS384, RS512, PS256, PS384, PS512)

The RSA based algorithms use asymmetric encryption. Signing is done with a private key and verification is done with a public key. The public key is typically in PEM format.

The following is an example of verifying using an RSA algorithm: Jws jws = new Jws(); jws.Certificate = new Certificate("..\\jwt.cer"); jws.InputMessage = signedData; jws.Verify(); string verifiedPayload = jws.OutputMessage;

Notes for ECDSA Algorithms (ES256, ES384, ES512)

ECDSA algorithms require a valid ECC public key to verify the message. If the key was originally created with the ECC component, the PEM encoded PublicKey may be used directly with the Certificate property. The following is an example PEM encoded public certificate created by the ECC component:

-----BEGIN PUBLIC KEY-----
MIIBMjCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAA
AAD///////////////8wRAQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrG
NdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt
6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA
//////////+85vqtpxeehPO5ysL8YyVRAgEBA0EEIC5rbLp11Mnz6cBXLLriaDIov3rm8RAY
x/OR0bOKiff0cQy+sLVaxjseqFk/+Xvl4ORSv5Z6HdHv5GyEpA0UoA==
-----END PUBLIC KEY-----

The following is an example of verifying with an ECC public key: Jws jws = new Jws(); jws.Certificate = new Certificate(CertStoreTypes.cstPublicKeyFile, pubKey, "", "*"); jws.InputMessage = signedData; jws.Verify(); string verifiedPayload = jws.OutputMessage;

To use an ECC public key created by other means, the ECC component may be used to import the key parameters. Populate the Rx and Ry of the ECC component first to obtain the PEM formatted public key. For instance:

//Import an existing ECC public key Ecc ecc = new Ecc(); byte[] x_bytes = new byte[] { 171, 170, 196, 151, 94, 196, 231, 12, 128, 232, 17, 61, 45, 105, 41, 209, 192, 187, 112, 242, 110, 178, 95, 240, 36, 55, 83, 171, 190, 176, 78, 13 }; byte[] y_bytes = new byte[] { 197, 75, 134, 245, 245, 28, 199, 9, 7, 117, 1, 54, 49, 178, 135, 252, 62, 89, 35, 180, 117, 80, 231, 23, 110, 250, 28, 124, 219, 253, 224, 156 }; ecc.Key.RxB = x_bytes; ecc.Key.RyB = y_bytes; string pubKey = ecc.Key.PublicKey; Jws jws = new Jws(); jws.Certificate = new Certificate(CertStoreTypes.cstPublicKeyFile, pubKey, "", "*"); jws.InputMessage = signedData; jws.Verify(); string verifiedPayload = jws.OutputMessage;

Notes for Unsecured (none)

To parse a JWS token without any security call the Verify method without setting Key or Certificate. For instance: Jws jws = new Jws(); jws.InputMessage = signedData; jws.Verify(); string unsecuredPayload = jws.OutputMessage;

Other Functionality

In addition to standard signing and verifying, the component also supports a variety of other features including:

  • Adding custom header parameters with AddHeaderParam
  • Enforcing algorithm restrictions when verifying by setting StrictValidation
  • Inspect the JWS without verifying by calling Parse

We appreciate your feedback.  If you have any questions, comments, or suggestions about this article please contact our support team at kb@nsoftware.com.