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, ChaCha, CAST, DES, 3DES, etc.)
- Asymmetric Algorithms
- XML Signing and Encrypting
- OSDP (Operating System Data Protection)
- ECC
- EzRand
- Hash
- Poly1305
- PBKDF
- Argon2
- JWE Encrypting and Decrypting
- JWS Signing and Verifying
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);
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.
OSDP
The OSDP component provides an easy interface for protecting data using the Microsoft Windows Data Protection API on Windows or the macOS Keychain. 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!
Argon2
The Argon2 component provides the ability to securely hash passwords while protecting against various types of attacks. Argon2 is a cryptographic memory-hard algorithm that is used for key derivation.
The Argon2 component supports the following variants:
- Argon2d
- Argon2i
- Argon2id (default)
To create a key using this component, start by setting the required properties:
- Password
- Salt (should be at least 16 bytes)
Optionally, you can specify the Algorithm variant, KeyLength in bytes, number of Iterations, degree of Parallelism and MemoryCost in kilobytes.
After that, call the CreateKey method, which will populate the read-only Key property with the derived key.
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-----
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-----
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 support@nsoftware.com.