Elliptic Curve Cryptography (ECC) support

Introduction

The IPWorks Encrypt development library supports Elliptic Curve Cryptography in a single unified API via the ECC component. This component implements the following standards: ECDSA (Elliptic Curve Digital Signature Algorithm), EdDSA (Edwards-curve Digital Signature Algorithm), ECDH (Elliptic Curve Diffie Hellman), and ECIES (Elliptic Curve Integrated Encryption Scheme). Using these standards, the ECC component supports creating ECC keys, computing a shared secret, signing and verifying signatures, and encrypting and decrypting data.

This guide will cover the basics for each of these fundamental operations.

Contents

ECC keys

The ECC component supports the following curves and corresponding key types:

  • secp256r1 (NIST P-256)
  • secp384r1 (NIST P-384)
  • secp521r1 (NIST P-521)
  • X25519
  • Ed25519
  • X448
  • Ed448
  • secp160k1
  • secp192k1
  • secp224k1
  • secp256k1
  • brainpoolP160r1
  • brainpoolP192r1
  • brainpoolP224r1
  • brainpoolP256r1
  • brainpoolP320r1
  • brainpoolP384r1
  • brainpoolP512r1
  • brainpoolP160t1
  • brainpoolP192t1
  • brainpoolP224t1
  • brainpoolP256t1
  • brainpoolP320t1
  • brainpoolP384t1
  • brainpoolP512t1

ECC keys come in pairs, one private and one public key. The mathematical parameters of these keys depends upon the specific ECC curve. For the NIST curves (secp256r1, secp384r1, secp521r1), Koblitz curves (secp160k1, secp192k1, secp224k1, secp256k1), and Brainpool curves, the public key consists of two parameters, Rx and Ry; the private key consists of only one parameter value, K. For Curve25519 and Curve448 curves, the public key consists of one parameter, XPk, and the private key consists of one parameter, XSk.

Creating ECC keys

To generate new ECC keys, simply call the CreateKey method and pass the desired key type as a string. After calling CreateKey, the Key property will hold the paired public and private key. The PEM-encoded string representation of the keys, as well as the mathematical parameters themselves, can be accessed via the this Key property. Below is an example in C#:

Ecc ecc = new Ecc(); ecc.CreateKey("X25519"); string priv = ecc.Key.PrivateKey; // PEM-encoded private key string pub = ecc.Key.PublicKey; // PEM-encoded public key byte[] XPk = ecc1.Key.XPkB; // public key parameter in raw bytes byte[] XSk = ecc1.Key.XSkB; // private key parameter in raw bytes

Certain ECC component operations restrict the type of key that can be used. Each subsection devoted to a specific operation includes a list of key types that are applicable to that operation.

Computing a shared secret

The ECC component can compute a shared secret between two parties using a public and private key. The ECDH standard is used to compute the shared secret.

The following key types are supported for this operation:

  • secp256r1 (P-256)
  • secp384r1 (P-384)
  • secp521r1 (P-521)
  • X25519
  • X448
  • secp160k1
  • secp192k1
  • secp224k1
  • secp256k1
  • brainpoolP160r1
  • brainpoolP192r1
  • brainpoolP224r1
  • brainpoolP256r1
  • brainpoolP320r1
  • brainpoolP384r1
  • brainpoolP512r1
  • brainpoolP160t1
  • brainpoolP192t1
  • brainpoolP224t1
  • brainpoolP256t1
  • brainpoolP320t1
  • brainpoolP384t1
  • brainpoolP512t1

To compute a shared secret, first set the public key in the RecipientKey property and the private key in the Key property. If necessary, set the ComputeSecretKDF property to the hash or HMAC algorithm that should be applied to the raw secret. Then, call the ComputeSecret method and the resulting value will be stored in the SharedSecret property.

Code sample

Below is an example of computing a shared secret between two separate entities in C#:

Ecc alice = new Ecc(); Ecc bob = new Ecc(); // alice and bob will both compute the same secret independently alice.CreateKey("X25519"); string alicePubKey = alice.Key.PublicKey; string alicePrivKey = alice.Key.PrivateKey; bob.CreateKey("X25519"); string bobPubKey = bob.Key.PublicKey; string bobPrivKey = bob.Key.PrivateKey; alice.Reset(); bob.Reset(); // note: public keys must be exchanged between parties by some external mechanism alice.Key.PrivateKey = alicePrivKey; alice.RecipientKey.PublicKey = bobPubKey; alice.UseHex = true; // hex-encodes the shared secret after computation for easier display alice.ComputeSecret(); string aliceSecret = alice.SharedSecret; bob.Key.PrivateKey = bobPrivKey; bob.RecipientKey.PublicKey = alicePubKey; bob.UseHex = true; bob.ComputeSecret(); string bobSecret = bob.SharedSecret; // aliceSecret and bobSecret will match

Signing

The ECC component supports signing data via the ECDSA or EdDSA standards. The component will use the key specified in the Key property to hash input data and then sign the resulting hash. To sign a hash directly instead of computing it first, the HashValue property should be set to the hash to sign.

If the input data is stored in a file, set the InputFile property to the appropriate file path. If the input data is stored in memory, set the InputMessage property to the string containing the data. The .NET and Java editions also support inputing from a stream via the SetInputStream method.

The HashAlgorithm property determines the algorithm used for hash computation. The Algorithm property is used to determine the eligibility of the Key for this operation. Supported algorithms and key types are as follows:

  • secp256r1 (P-256)
  • secp384r1 (P-384)
  • secp521r1 (P-521)
  • Ed25519
  • Ed448
  • secp160k1
  • secp192k1
  • secp224k1
  • secp256k1
  • brainpoolP160r1
  • brainpoolP192r1
  • brainpoolP224r1
  • brainpoolP256r1
  • brainpoolP320r1
  • brainpoolP384r1
  • brainpoolP512r1
  • brainpoolP160t1
  • brainpoolP192t1
  • brainpoolP224t1
  • brainpoolP256t1
  • brainpoolP320t1
  • brainpoolP384t1
  • brainpoolP512t1

Once the Key, input data, Algorithm, and HashAlgorithm have all been populated (or HashValue), simply call the Sign method. The computed hash is stored in the HashValue property, and the signed hash is stored in the HashSignature property.

If the Ed25519 or Ed448 curves are used, two additional parameters are applicable:

  • HashEdDSA
  • EdDSAContext

The HashEdDSA property determines whether EdDSA keys should be used with a PureEdDSA algorithm (Ed25519/Ed448) or a HashEdDSA algorithm (Ed25519ph, Ed448ph). By default, HashEdDSA is False and the component uses the PureEdDSA algorithm. Please note that setting HashValue and signing a hash directly is not supported when signing with a PureEdDSA algorithm. The EdDSAContext configuration option is used to specify context data when using PureEdDSA algorithms.

After the Sign method has been called, the Progress event will fire with updates during hash computation. Signing the computed hash is quick and does not require progress updates.

A code example that includes signing can be found at the bottom of the Verifying signatures section below.

Verifying signatures

The ECC component supports verifying signatures created via the ECDSA and EdDSA standards. In order to verify a signature the component requires the hash signature itself, the public key corresponding to the private key used to sign, and the original data that was signed.

The following algorithms and key types are applicable for this operation:

  • secp256r1 (P-256)
  • secp384r1 (P-384)
  • secp521r1 (P-521)
  • Ed25519
  • Ed448
  • secp160k1
  • secp192k1
  • secp224k1
  • secp256k1
  • brainpoolP160r1
  • brainpoolP192r1
  • brainpoolP224r1
  • brainpoolP256r1
  • brainpoolP320r1
  • brainpoolP384r1
  • brainpoolP512r1
  • brainpoolP160t1
  • brainpoolP192t1
  • brainpoolP224t1
  • brainpoolP256t1
  • brainpoolP320t1
  • brainpoolP384t1
  • brainpoolP512t1

The signature to verify should be set in the HashSignature property, and the public key of the signing party should be set in the SignerKey property. If the data that was signed is stored in a file, the InputFile property should be set to the appropriate file path. Otherwise, the InputMessage property should be set to the string holding the data. The .NET and Java editions also support inputing from a stream via the SetInputStream method.

The HashAlgorithm property must be set to the appropriate hashing algorithm when the following curves are used: secp256r1, secp384r1, or secp521r1. When using the Ed25519 or Ed448 curves, the HashEdDSA property and EdDSAContext configuration option may apply. HashEdDSA determines whether to use a PureEdDSA algorithm (Ed25519/Ed448) or a HashEdDSA algorithm (Ed25519ph/Ed448ph). By default, HashEdDSA is False and a PureEdDSA algorithm will be used. The EdDSAContext configuration option can be used to specify context data when using PureEdDSA algorithms.

Once these properties are set, simply call the VerifySignature method. The component will compute the hash for the specified data and use it to populate the HashValue property. It will then verify the signature using the specified SignerKey and HashSignature. If the computed signature matches the provided signature, the VerifySignature method will return True, otherwise it will return False.

To verify a hash signature without computing the hash first, simply set the HashValue property with the computed hash before calling VerifySignature. This is not applicable when using a PureEdDSA algorithm like Ed25519 or Ed448.

Code samples

Below is an example of signing and verifying a signature with a PureEdDSA algorithm in C#:

//Create an EdDSA key with Party 1 Ecc ecc1 = new Ecc(); ecc1.CreateKey("Ed25519"); string ecc1_priv = ecc1.Key.PrivateKey; string ecc1_pub = ecc1.Key.PublicKey; //Sign the data on Party 1 string originalData = "test data"; ecc1.Reset(); ecc1.Key.PrivateKey = ecc1_priv; ecc1.InputMessage = originalData; ecc1.UseHex = true; //Hex encode the hash signature for ease of use. ecc1.Sign(); string hashSignature = ecc1.HashSignature; //Transmit the hash signature, public key, and original data to Party 2 //Verify the data on Party 2 Ecc ecc2 = new Ecc(); ecc2.SignerKey.PublicKey = ecc1_pub; ecc2.InputMessage = originalData; ecc2.HashSignature = hashSignature; ecc2.UseHex = true; //Decode the hex encoded hash signature bool isVerified = ecc2.VerifySignature();

Below is an example of verifying a signature directly without computing the hash first (using HashEdDSA) in C#:

//Create an EdDSA key with Party 1 Ecc ecc1 = new Ecc(); ecc1.CreateKey("Ed25519"); string ecc1_priv = ecc1.Key.PrivateKey; string ecc1_pub = ecc1.Key.PublicKey; //Sign the data on Party 1 string originalData = "test data"; ecc1.Reset(); ecc1.Key.PrivateKey = ecc1_priv; ecc1.InputMessage = originalData; ecc1.UseHex = true; //Hex encode the hash signature for ease of use. ecc1.HashEdDSA = true; // Use "Ed25519ph" ecc1.Sign(); string computedHash = ecc1.HashValue; string hashSignature = ecc1.HashSignature; //Transmit the hash signature, public key, and computed hash to Party 2 //Verify the data on Party 2 Ecc ecc2 = new Ecc(); ecc2.SignerKey.PublicKey = ecc1_pub; ecc2.HashValue = computedHash; ecc2.HashSignature = hashSignature; ecc2.HashEdDSA = true; ecc2.UseHex = true; //Decode the hex encoded hash signature bool isVerified = ecc2.VerifySignature();

Encrypting

The ECC component supports encrypting and decrypting data via the ECIES standard. Encryption requires an ECDSA public key, which should be set in the RecipientKey property. The Algorithm field of the ReceipientKey will be used to determine the eligibility of the key for encryption operations. Supported key types are as follows:

  • secp256r1
  • secp384r1
  • secp521r1
  • secp160k1
  • secp192k1
  • secp224k1
  • secp256k1
  • brainpoolP160r1
  • brainpoolP192r1
  • brainpoolP224r1
  • brainpoolP256r1
  • brainpoolP320r1
  • brainpoolP384r1
  • brainpoolP512r1
  • brainpoolP160t1
  • brainpoolP192t1
  • brainpoolP224t1
  • brainpoolP256t1
  • brainpoolP320t1
  • brainpoolP384t1
  • brainpoolP512t1
  • During encryption, the ECC key is used to generate a shared secret that both sides can use to encrypt or decrypt the data. The EncryptionAlgorithm property determines the symmetric encryption algorithm to use with this shared secret. The HMACAlgorithm property determines the hashing algorithm that will generate a secure Message Authentication Code during encryption to verify the data's integrity. The HMACOptionalInfo configuration option can be used to specify optional data used during the HMAC step of encryption and decryption (formatted as a hex string). The HMACKeySize configuration option can be set if a specific key size is required during the HMAC step.

    The IV property can be set to an initialization vector used as input to the encryption function; by default the component will use an IV filled with null bytes, which is a standard practice since the encryption key will only be used once. The KDF property specifies the Key Derivation Function used to convert a shared secret into an encryption key, and the KDFHashAlgorithm property specifies the hash algorithm to use during this step. The KDFOptionalInfo configuration setting can be used to specify optional data for this step (formatted as a hex string).

    In addition to the encryption parameters, the input data must be specified. If the data is held in a file, the InputFile property should be set to the appropriate file path. Otherwise, the InputMessage property should be set to the string representation of the data. The .NET and Java editions also support inputing from a stream via the SetInputStream method.

    Once these properties are configured, simply call the Encrypt method and the encrypted data will be available in the OutputMessage property. If the OutputFile property is set or SetOutputStream is called prior to encryption, the encrypted data will instead be written to the appropriate file or stream.

    A code example that includes encryption can be found at the bottom of the Decrypting section below.

    Decrypting

    The ECC component supports encrypting and decrypting data via the ECIES standard. Decryption requires an ECDSA private key that is paired with the public key used to encrypt, and this private key should be set in the Key property. The Algorithm field of the specified Key is used to determine the eligibility of the key for this operation. Supported key types are as follows:

    • secp256r1
    • secp384r1
    • secp521r1
    • secp160k1
    • secp192k1
    • secp224k1
    • secp256k1
    • brainpoolP160r1
    • brainpoolP192r1
    • brainpoolP224r1
    • brainpoolP256r1
    • brainpoolP320r1
    • brainpoolP384r1
    • brainpoolP512r1
    • brainpoolP160t1
    • brainpoolP192t1
    • brainpoolP224t1
    • brainpoolP256t1
    • brainpoolP320t1
    • brainpoolP384t1
  • brainpoolP512t1
  • During decryption, the ECC key is used to generate a shared secret that both sides can use to encrypt or decrypt the data. The EncryptionAlgorithm property should be set to the symmetric encryption algorithm that was used with this shared secret to encrypt the data. The HMACAlgorithm property should be set to the hashing algorithm that was used to generate a secure Message Authentication Code during encryption. The HMACOptionalInfo configuration option can be used to specify optional data used during the HMAC step (only required if optional data was also specified prior to encryption). The HMACKeySize configuration option can be set if a specific key size is required during the HMAC step.

    The IV property can be set to an initialization vector if an IV was used as input to the encryption function; by default the component will use an IV filled with null bytes, which is a standard practice since the encryption key will only be used once. The KDF property should be set to the Key Derivation Function (used to convert a shared secret into an encryption key) that was used during encryption, and the KDFHashAlgorithm property should be set to the hash algorithm that was used during this step while encrypting. The KDFOptionalInfo configuration setting can be used to specify optional data for this step (only required if optional data was also specified prior to encryption).

    Once the decryption parameters are set, the input data must be specified. If the data is held in a file, the InputFile property should be set to the appropriate file path. Otherwise, the InputMessage property should be set to the string representation of the data. The .NET and Java editions also support inputing from a stream via the SetInputStream method.

    Once these properties are configured, simply call the Decrypt method and the decrypted data will be available in the OutputMessage property. If the OutputFile property is set or SetOutputStream is called prior to encryption, the encrypted data will instead be written to the appropriate file or stream.

    Code sample

    Below is an example of encrypting and decrypting data in C#:

    //Create an ECDSA key with Party 2 Ecc ecc2 = new Ecc(); ecc2.CreateKey("secp256r1"); string ecc2_priv = ecc2.Key.PrivateKey; string ecc2_pub = ecc2.Key.PublicKey; //Transmit public key to Party 1 //Encrypt the message on Party 1 using public key from Party 2 Ecc ecc1 = new Ecc(); ecc1.KDF = "KDF1"; //Use KDF1 ecc1.EncryptionAlgorithm = EccEncryptionAlgorithms.iesAES; ecc1.KDFHashAlgorithm = EccKDFHashAlgorithms.iesSHA1; ecc1.Config("KDFOptionalInfo=202122232425262728292a2b2c2d2e2f"); //optional Hex encoded string ecc1.InputMessage = "test data"; ecc1.RecipientKey.PublicKey = ecc2_pub; ecc1.UseHex = true; ecc1.Encrypt(); string encryptedMessage = ecc1.OutputMessage; //Transmit the encrypted message to Party 2 //Decrypt the message using the private key for Party 2 ecc2.KDF = "KDF1"; ecc2.EncryptionAlgorithm = EccEncryptionAlgorithms.iesAES; ecc2.KDFHashAlgorithm = EccKDFHashAlgorithms.iesSHA1; ecc2.Config("KDFOptionalInfo=202122232425262728292a2b2c2d2e2f"); ecc2.Key.PrivateKey = ecc2_priv; ecc2.InputMessage = encryptedMessage; ecc2.UseHex = true; ecc2.Decrypt(); Console.WriteLine(ecc2.OutputMessage);

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