© Marius Iulian Mihailescu and Stefania Loredana Nita 2021
M. I. Mihailescu, S. L. NitaPro Cryptography and Cryptanalysis https://doi.org/10.1007/978-1-4842-6367-9_9

9. Cryptography Libraries in C# and .NET

Marius Iulian Mihailescu1   and Stefania Loredana Nita1
(1)
Bucharest, Romania
 

In this chapter, we will cover some of the most important cryptography libraries that can be used within .NET and C# applications. Choosing a cryptography library, especially one that is open source, represents a sensitive task compared to the .NET Framework itself. You need to be sure of what you are doing and know what kind of data you are using. Relying upon an open source project for a security aspect could lead to significant security incidents if there are bugs in the open source project that can be used by an attacker.

In the following sections, we will cover these libraries: NSec [1], Bouncy Castle [2], Inferno [3], and SecureBlackbox [4].

NSec

NSec [1] is one of the most modern and easy to use cryptographic libraries for .NET Core. The library is based on Libsodium [5].

Some of the features of NSec are as follows:
  • Modern approach. Libsodium offers a small set of cryptographic algorithms and primitives. The nice thing about NSec and Libsodium compared to other libraries is the support of features such as X25519, Ed25519, and ChaCha-Poly1305. The performance and how the features are implemented within NSec are based on a modern .NET API which is developed based on types Span<T> and ReadOnlySpan<T>.

  • Extremely easy to use. It is very useful and easy to use. The elegance and reliability of NSec means it’s easy to implement. It offers support for the type data model, which is designed with respect for keys and shared secrets, based on dedicated classes instead of empty byte arrays. This helps developers avoid using a key within a wrong algorithm.

  • Secure. The mission of NSec is to make the cryptographic primitives as easy as possible.

  • Fast. NSec and Libsodium are very fast during their cryptographic processes. There is no allocation memory for the heaps. NSec is designed to avoid any kind of memory allocations or non-useful copies.

  • Agile. Most of the algorithms implemented in NSec are derived from a tiny set of base classes. The purpose is to provide a productive way of writing code against the algorithm interfaces instead of a specific algorithm.

In the following examples, you’ll see how NSec works and how easy it is to use. The NSec uses with success some features from C# 8.0 and they are very easy to spot and understand.

Listing 9-1 shows how to work with the Ed25519 signature algorithm and sign messages. Figure 9-1 shows the output.
../images/493660_1_En_9_Chapter/493660_1_En_9_Fig1_HTML.jpg
Figure 9-1

NSec output

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NSec.Cryptography;
namespace NSecLibrary
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("NSec Library");
            //** select the Ed25519 signature algorithm
            var algorithm = SignatureAlgorithm.Ed25519;
            //** create a new key pair
            var key = Key.Create(algorithm);
            //** generate some data to be signed
            var data = Encoding.UTF8.GetBytes("Use the Force, Luke!");
            //** sign the data using the private key
            var signature = algorithm.Sign(key, data);
            //** verify the data using the signature and the public key
            if (algorithm.Verify(key.PublicKey, data, signature))
            {
                Console.WriteLine("The message ");
                for (int i = 0; i < data.Length; i++)
                {
                    Console.Write(data[i].ToString() + " ");
                }
                Console.WriteLine(" has been verified with success.");
            }
            else
            {
                Console.WriteLine("The message: {0} has not been verified.", data.ToString());
            }
        }
    }
}
Listing 9–1

Ed25519 Signature

NSec Installation

NSec can be found and installed using the NuGet package [6] and it contains all the details necessary for the installation process (see Figure 9-2).
../images/493660_1_En_9_Chapter/493660_1_En_9_Fig2_HTML.jpg
Figure 9-2

NSec.Cryptography NuGet package

NSec can also be added to a project using the following different ways:
  • Using dotnet CLI:

$ dotnet add package NSec.Cryptography –-version XX.X.X
  • Using Visual Studio:

PM> Install-Package NSec.Cryptography -Version XX.X.X
  • Using a reference in the *.csproj file:

<PackageReference Include="NSec.Cryptography" Version="XX.X.X"/>

Bouncy Castle

Bouncy Castle is one of the most important and well-known libraries. It contains an implementation in C# of cryptographic algorithms and protocols.

It has a significant number of useful features. The list of the most important features is [7] as follows:
  • “Support for parsing and generate PKCS-12 files” [7].

  • “X.509: Support for V1 and V3 certificates (generating and parsing). Also, V2 CRLs and certificates based on the attributes” [7].

  • “PBE algorithms supported by PbeUtilities” [7].

  • “Signature algorithms supported by SignerUtilities” [7].

  • “Symmetric key algorithms: AES, Blowfish, Camellia, CAST5, CAST6, ChaCha, DES, DESede, GOST28147, HC-128, HC-256, IDEA, ISAAC, Noekeon, RC2, RC4, RC5-32, RC5-64, RC6, Rijndael, Salsa20, SEED, Serpent, Skipjack, TEA/XTEA, Threefish, Tnepres, Twofish, VMPC and XSalsa20” [7].

  • “Symmetric key modes: CBC, CFB, CTS, GOFB, OFB, OpenPGPCFB, and SIC (or CTR)” [7].

  • “Symmetric key paddings: ISO10126d2, ISO7816d4, PKCS-5/7, TBC, X.923, and Zero Byte” [7].

  • “Asymmetric key algorithms: ElGamal, DSA, ECDSA, NaccacheStern and RSA (with blinding)” [7].

  • “Asymmetric key paddings/encodings: ISO9796d1, OAEP, and PKCS-1” [7].

  • “AEAD block cipher modes: CCM, EAX, GCM and OCB” [7].

  • “Digests: GOST3411, Keccak, MD2, MD4, MD5, RIPEMD128, RIPEMD160, RIPEMD256, RIPEMD320, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA3, Tiger, and Whirlpool” [7].

  • “XOFs: SHAKE” [7].

  • “Signer mechanisms: DSA, ECDSA, ECGOST3410, ECNR, GOST3410, ISO9796d2, PSS, RSA, X9.31-1998” [7].

  • “Key agreement: Diffie-Hellman, EC-DH, EC-MQV, J-PAKE, SRP-6a” [7].

  • “MACs: CBCBlockCipher, CFBBlockCipher, CMAC, GMAC, GOST28147, HMac, ISO9797 Alg. 3, Poly1305, SipHash, SkeinMac, VMPCMAC” [7].

  • “PBE generators: PKCS-12, and PKCS-5 - schemes 1 and 2” [7].

  • “OpenPGP (RFC 4880)” [7].

  • “Cryptographic Message Syntax (CMS, RFC 3852), including streaming API” [7].

  • “Online Certificate Status Protocol (OCSP, RFC 2560)” [7].

  • “Time Stamp Protocol (TSP, RFC 3161)” [7].

  • “TLS/DTLS client/server up to version 1.2, with support for the most common ciphersuites and extensions, and many less common ones. Non-blocking API available” [7].

  • Elliptic curve cryptography.

Bouncy Castle Examples

Listing 9-2 shows an example of using Bouncy Castle to generate cryptographic keys. In Figure 9-3 you can see the output for a key size set to 256.
../images/493660_1_En_9_Chapter/493660_1_En_9_Fig3_HTML.jpg
Figure 9-3

Generating cryptography keys using BouncyCastle

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
namespace BouncyCastleLibrary
{
    class Program
    {
        public static string Xvalue;
        public static string Yvalue;
        public static int Dvalue;
        static void Main(string[] args)
        {
            Console.WriteLine("Using BouncyCastle Library to show how we can generate cryptography keys (private and public). ");
            Console.WriteLine("Please, choose the size of the keys (128 or 256)");
            int key_size = Convert.ToInt32(Console.ReadLine());
            if (key_size == 128)
                KeyGeneration(128);
            else if (key_size == 256)
                KeyGeneration(256);
        }
        public static AsymmetricCipherKeyPair GenerateKeys(int keySize)
        {
            //** choosing ECDSA for key generation
            var key_generation = new ECKeyPairGenerator("ECDSA");
            //** for creating randomly values
            var randomly_secure_value = new SecureRandom();
            //** generating the parameters based on the random value and size of the key
            var key_generation_parameters = new KeyGenerationParameters(randomly_secure_value, keySize);
            //** proceed with the initialization of the generation algorithm with parameters
            key_generation.Init(key_generation_parameters);
            //** the key pair generation
            return key_generation.GenerateKeyPair();
        }
        public static void KeyGeneration(int key_size)
        {
            //** generating process
            var key_pair = GenerateKeys(key_size);
            TextWriter text_writer = new StringWriter();
            PemWriter pem_writer = new PemWriter(text_writer);
            pem_writer.WriteObject(key_pair.Private);
            pem_writer.Writer.Flush();
            string privateKey = text_writer.ToString();
            Console.WriteLine("The private key is: {0}", privateKey);
            ECPrivateKeyParameters privateKeyParam = (ECPrivateKeyParameters)key_pair.Private;
            Console.WriteLine("D value is: {0}", privateKeyParam.D.ToString());
            text_writer = new StringWriter();
            pem_writer = new PemWriter(text_writer);
            pem_writer.WriteObject(key_pair.Public);
            pem_writer.Writer.Flush();
            ECPublicKeyParameters publicKeyParam = (ECPublicKeyParameters)key_pair.Public;
            string publickey = text_writer.ToString();
            Console.WriteLine("The public key is: {0}", publickey);
            Console.ReadKey();
            Xvalue = publicKeyParam.Q.XCoord.ToBigInteger().ToString();
            Yvalue = publicKeyParam.Q.YCoord.ToBigInteger().ToString();
        }
    }
}
Listing 9-2

AES – Encryption with CBC Mode and PKCS5/7 Padding

The next three examples (see Listing 9-3, Listing 9-4 and Listing 9-5) show how to deal with key agreement and exchange algorithms.

If you are working with a basic agreement (see Listing 9-3) that uses a cofactor with an agreed value, the following example should be quite explanatory.
public byte[] AgreementOnAgreedValue
                      (AsymmetricECPrivateKey privateKey,
                      AsymmetricECPublicKey otherEncKey)
{
      IAgreementCalculatorService createService =
            CryptoServicesRegistrar.CreateService(privateKey);
      IAgreementCalculator<FipsEC.AgreementParameters>
          contract_agreement =
          createService.CreateAgreementCalculator(FipsEC.Cdh);
      return contract_agreement.Calculate(otherEncKey);
}
Listing 9-3

Basic Agreement with an Agreed Value

In Listing 9-4, you can see how to work with a SHA256 function and with a PRF (Pseudo Random Function) function in order to compute on the agreed value.
public byte[] AgreementOnAgreedValueWithPrf
              (AsymmetricECPrivateKey privateKey,
               AsymmetricECPublicKey otherEncKey,
               byte[] paddingSalt)
{
       IAgreementCalculatorService createService =
            CryptoServicesRegistrar.CreateService(privateKey);
       IAgreementCalculator<FipsEC.AgreementParameters>
            contract_agreement = dhFact.CreateAgreementCalculator
            (FipsEC.Cdh.WithKeyMaterialGenerator
            (new FipsPrfKmg
                         (FipsPrfAlgorithm.Sha256HMac,
     paddingSalt));
      return contract_agreement.Calculate(otherEncKey);
}
Listing 9-4

Agreement Using PRF

Listing 9-5 shows another type of agreement with X9.63 KDF (Key Derivation Function).
public byte[] AgreementWithCofactorAndKdf
      (AsymmetricECPrivateKey privateKey,
       AsymmetricECPublicKey otherEncKey,
       byte[] paddingSalt)
{
     IAgreementCalculatorService createService =
           CryptoServicesRegistrar.CreateService(privateKey);
     IAgreementCalculator<FipsEC.AgreementParameters>
         contract_agreement =
              dhFact.CreateAgreementCalculator
              (FipsEC.Cdh.WithKeyMaterialGenerator
              (new FipsKdfKmg(FipsKdf.X963,
                     paddingSalt, 32));
     return agreement.Calculate(otherEncKey);
}
Listing 9-5

Working with an Agreement Based on X9.63 KDF

Bouncy Castle Installation

Bouncy Castle can be found and installed using the NuGet package [8] and it contains all the details necessary for the installation process (see Figure 9-4).
../images/493660_1_En_9_Chapter/493660_1_En_9_Fig4_HTML.jpg
Figure 9-4

Bouncy Castle NuGet package

Bouncy Castle can also be added to a project in the following ways:
  • Using dotnet CLI:

$ dotnet add package BouncyCastle –-version XX.X.X
  • Using Visual Studio:

PM> Install-Package BouncyCastle -Version XX.X.X
  • Using a reference in the *.csproj file:

<PackageReference Include="BouncyCastle" Version="XX.X.X"/>

Inferno

Inferno is another interesting cryptographic library for .NET developed using C#. It offers a unique elegance in writing the code and the performances obtained during processing the code are quite promising.

The following list represents the features [9] of Inferno:
  • “[random]: CryptoRandom (.NET Random done right)” [9]

  • “[ciphers]: AES-256 only (fast, constant-time, side-channel-resistant AES-NI)” [9]

  • “[hi-level]: AEAD (AES-CTR-HMAC). Streaming AEAD (EtM Transforms)” [9]

  • “ [ciphers-misc]: AES-CTR implementation (CryptoTransform) “ [9]

  • “[ciphers-misc]: AEAD (AES-CBC-HMAC)” [9]

  • “[hash]: SHA2 hash factories (256, 384, 512). SHA-384 is recommended (default)” [9]

  • “[hash]: SHA1 hash factory (mostly for legacy integration)” [9]

  • “[mac]: HMAC2 (.NET HMAC done right)” [9]

  • “[mac]: HMAC-SHA1, HMAC-SHA2 factories” [9]

  • “[kdf]: HKDF, PBKDF2, SP800_108_Ctr. Any HMAC factory is supported” [9]

  • “[otp]: TOTP” [9]

  • “[helpers]” [9]
    • “Constant-time byte and string comparison” [9]

    • “Safe UTF8” [9]

    • “Fast 64-bit byte-array Xor” [9]

Inferno Examples

The following examples show case studies of how the library should be used. In Listing 9-6, you can see the basic declaration of functions that deal with encryption, decryption, and authentication. Note the flexibility and the suggestive declaration of the functions.
//** The namespace that has to be used when working with
//** Inferno is: SecurityDrive.Inferno
//** If SecurityDrive.Inferno is not visible it means that is
//** not properly installed and it is necessary to check the
//** section "Inferno Installation"
public static class BasicOperations
{
    public static byte[] Encrypt(byte[] master_crypto_key,
         ArraySegment<byte> clearText,
         ArraySegment<byte>? saltPadding = null);
    public static byte[] Decrypt(byte[] master_crypto_key,
         ArraySegment<byte> cryptotext,
         ArraySegment<byte>? saltPadding = null);
    public static bool Authenticate(byte[] master_crypto_key,
         ArraySegment<byte> cryptotext,
         ArraySegment<byte>? saltPadding = null);
}
Listing 9-6

Encryption, Decryption, and Authentication Functions

Working with hash functions (see Listing 9-7) is quite interesting and easy. It is very important once you reached at the end of the process and application to invoke dispose.
public static Func<SHA384> HashFactory
Listing 9-7

Working with Hash

Listing 9-8 shows how to use a HMAC.
var dataForHmac = Utils.SafeUTF8.GetBytes("Welcome To Apress!");
//** this is for HMACSHA384
using (var theHmac = SuiteB.HmacFactory())
{
      theHmac.Key = new byte[] { 6, 5, 4, 3, 2 };
      theHmac.ComputeHash(dataForHmac).ToBase16().Dump();
}
Listing 9-8

Using HMAC

Listing 9-9 shows a case of DSA (Digital Signature Algorithm) usage. Figure 9-5 shows the output.
using SecurityDriven.Inferno.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace InfernoLibrary
{
    class Program
    {
        static void Main(string[] args)
        {
            //** generate the DSA keys
            CngKey thePrivateKey = CngKeyExtensions.CreateNewDsaKey();
            //** generate the cryptographic keys
            //** that will be used with DSA
            byte[] dsa_private_key_blob = thePrivateKey.GetPrivateBlob();
            //** convert and store private key as bytes
            byte[] dsa_public_key_blob = thePrivateKey.GetPublicBlob();
            //** convert and store public key as bytes
            CngKey dsa_public_key = dsa_public_key_blob.ToPublicKeyFromBlob();
            //** some data (sample)
            byte[] data_sample = Guid.NewGuid().ToByteArray();
            byte[] theSignature = null;
            //** using the private key, generate the
            //** DSA signature and store it properly
            using (var ecdsaAlgorithm = new ECDsaCng(thePrivateKey) { HashAlgorithm = CngAlgorithm.Sha384 })
            {
                theSignature = ecdsaAlgorithm.SignData(data_sample);
            }
            //** play with the data
            data_sample[5] ^= 1;
            Console.WriteLine("Private key is: {0}", BitConverter.ToString(dsa_private_key_blob));
            Console.WriteLine(" Public key is: {0}", BitConverter.ToString(dsa_public_key_blob));
            Console.WriteLine(" Sample data: {0}", BitConverter.ToString(data_sample));
            Console.WriteLine(" The signature is: {0}", BitConverter.ToString(theSignature));
            //** using the public key, verify the DSA signature
            using (var ecdsaAlgorithm = new ECDsaCng(dsa_public_key){ HashAlgorithm = CngAlgorithm.Sha384})
            {
                if (ecdsaAlgorithm.VerifyData(data_sample, theSignature))
                {
                    Console.WriteLine(" Oups! Something went wrong. Signature was unable to be verified properly.");
                }
                else
                {
                    Console.WriteLine(" The signature has been verified with success.");
                }
                Console.ReadKey();
            }
        }
    }
}
Listing 9-9

Dealing with a DSA

../images/493660_1_En_9_Chapter/493660_1_En_9_Fig5_HTML.jpg
Figure 9-5

The output

Inferno Installation

Inferno can be found and installed using the NuGet package [10] and it contains all the details necessary for the installation process (see Figure 9-6).
../images/493660_1_En_9_Chapter/493660_1_En_9_Fig6_HTML.jpg
Figure 9-6

Inferno NuGet package

Inferno can also be added to a project in the following ways:
  • Using dotnet CLI:

$ dotnet add package Inferno –-version XX.X.X
  • Using Visual Studio:

PM> Install-Package Inferno -Version XX.X.X
  • Using a reference in the *.csproj file:

<PackageReference Include="Inferno" Version="XX.X.X"/>

SecureBlackbox

SecureBlackbox is one of the most comprehensive sets of tools and classes for dealing with digital security and security for a network.

Developing security solutions is done in the same way as in NSec or Inferno. The methods and functions are similar to the ones from NSec and Inferno. The differences are very few and are specific to the allocation of buffer arrays.

The library is not free. To run the example in Listing 9-10, you need a license. Once you run the application, the message from Figure 9-7 will be shown.
../images/493660_1_En_9_Chapter/493660_1_En_9_Fig7_HTML.jpg
Figure 9-7

License required to run the example

For testing purposes, for the example from Listing 9-10, the owner of the library can be contacted and they will provide to you with a temporary license key.

Once the license is received (usually it is a text file, such as LicenseKey.txt), two functions can be called and the license as a key parameter to them. The two functions are void SetLicenseKey(ByteArray key) and void SetLicenseKey(string key).

If you need more key licenses at the same time, just call the function(s) several times with different file names. Each license should have a different name.

using SBSymmetricCrypto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace SecureBlackBoxLibrary
{
    class Program
    {
        static void Main(string[] args)
        {
            var key = "b14ca5898a4e4133bbce2ea2315a1916";
            byte[] encryptionKeyBuffer;
            byte[] initializationBuffer = new byte[16];
            using (Aes aes = Aes.Create())
            {
                aes.Key = Encoding.UTF8.GetBytes(key);
                aes.IV = initializationBuffer;
                encryptionKeyBuffer = aes.Key;
                initializationBuffer = aes.IV;
            }
            //** create a crypto factory instances
            //** used for symmetric encryption
            TElSymmetricCryptoFactory symmetric_factory_container = new TElSymmetricCryptoFactory();
            //** use the factory container to
            //** declare an appropriate algorithm
            TElSymmetricCrypto symmetric_encryption = symmetric_factory_container.CreateInstance (SBConstants.Unit.SB_ALGORITHM_CNT_AES256, TSBSymmetricCryptoMode.cmDefault);
            //** declare a key and assign the proper
            //** value for the secret key and the initialization vector
            TElSymmetricKeyMaterial km = new TElSymmetricKeyMaterial(null);
            km.Key = encryptionKeyBuffer;
            km.IV = initializationBuffer;
            //** assign the key container to the cryptographic
            //** object using the property KeyMaterial
            symmetric_encryption.KeyMaterial = km;
            //** proceed further with the encryption
            byte[] input_buffer = Encoding.UTF8.GetBytes("Welcome to Apress!");
            //** declare the output_buffer and output_size and initialize //** them with 0. In this way we will point out that the first //** invoked is getting the call.
            byte[] output_buffer = null;
            int output_size = 0;
            //** Finding out about the output
            //** length - this may be approximate
            symmetric_encryption.Encrypt(input_buffer, 0, input_buffer.Length, ref output_buffer, 0, ref output_size);
            //** create an allocation as an array for the output data
            output_buffer = new byte[output_size];
            //** do the encryption
            symmetric_encryption.Encrypt(input_buffer, 0, input_buffer.Length, ref output_buffer, 0, ref output_size);
            //**copy the data that has been encrypted to a dedicate buffer
            output_buffer = SBUtils.Unit.CloneArray(output_buffer, 0, output_size);
            //** before continue with the encryption
            //** invoke InitializeEncryption method
            //** within the cryptographic object
            symmetric_encryption.InitializeEncryption();
            //** as much as it is required, pass the data as necessary
            //** using invokations of EncryptUpdate()
            symmetric_encryption.EncryptUpdate(input_buffer, 0, input_buffer.Length, ref output_buffer, 0,ref output_size);
            output_buffer = SBUtils.Unit.CloneArray(output_buffer, 0, output_size);
            //** Once we reach at the end of EncryptUpdate(),
            //** we need to endup the encryption
            //** by invoking FinalizeEncryption() method
            symmetric_encryption.FinalizeEncryption(ref output_buffer, 0, ref output_size);
            output_buffer = SBUtils.Unit.CloneArray(output_buffer, 0, output_size);
            Console.WriteLine("The encryption key is: {0}", key);
            Console.WriteLine(" The buffer for encryption key is: {0}", BitConverter.ToString(encryptionKeyBuffer));
            Console.WriteLine(" The initialization buffer is: {0}", BitConverter.ToString(initializationBuffer));
            Console.WriteLine(" The encryption is: {0}", symmetric_encryption.ToString());
            Console.ReadKey();
        }
    }
}
Listing 9-10

Example of Symmetric Encryption with SecureBlackbox

SecureBlackbox Installation

SecureBlackbox can be found and installed using the NuGet package [10] and it contains all the details necessary for the installation process (see Figure 9-8).
../images/493660_1_En_9_Chapter/493660_1_En_9_Fig8_HTML.jpg
Figure 9-8

SecureBlackbox NuGet package

SecureBlackbox can also be added to a project in the following ways:
  • Using dotnet CLI:

$ dotnet add package SecureBlackbox –-version XX.X.X
  • Using Visual Studio:

PM> Install-Package SecureBlackbox -Version XX.X.X
  • Using a reference in the *.csproj file:

<PackageReference Include=" SecureBlackbox" Version="XX.X.X"/>

Conclusion

In this chapter, we covered the most important cryptography libraries (NSec, Bouncy Castle, Inferno, and SecureBlackbox) that can serve as guidance, extra libraries, and tools for achieving the confidentiality, integrity, and authenticity of the data within applications developed by professionals. The criteria used for selecting these libraries are their recognition by other professionals in the field and FIPS and NIST standards.

The libraries can be used in parallel with the System.Security.Cryptography namespace, offering a more exhaustive coverage of the cryptographic primitives.

Bibliography

  1. [1]

    NSec. Available online: https://nsec.rocks/.

     
  2. [2]
     
  3. [3]

    Inferno. Available online: https://nugetmusthaves.com/Package/Inferno.

     
  4. [4]

    SecureBlackbox. Available online: https://nugetmusthaves.com/Package/SecureBlackbox.

     
  5. [5]

    Libsodium for .NET. Available online: https://nugetmusthaves.com/Package/libsodium-net.

     
  6. [6]

    NSec.Cryptography. Available online: www.nuget.org/packages/NSec.Cryptography/20.2.0.

     
  7. [7]

    Bouncy Castle Features. Available online: www.bouncycastle.org/csharp/index.html.

     
  8. [8]

    Bouncy Castle NuGet Package. Available online: www.nuget.org/packages/BouncyCastle/.

     
  9. [9]

    Inferno Features and Project. Available online: https://securitydriven.net/inferno/.

     
  10. [10]

    Inferno NuGet Package. Available online: www.nuget.org/packages/Inferno/.

     
  11. [11]

    SecureBlackbox NuGet Package. Available online: www.nuget.org/packages/SecureBlackbox/.

     
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.140.195.28