Table of Contents

X25519 Class

Definition

Namespace
Bodu.Security.Cryptography
Assembly
Bodu.Security.Cryptography.dll
Package
Bodu.Security.Cryptography 1.2.0
Source
X25519.KeyFormats.cs

Provides a managed implementation of the X25519 elliptic-curve Diffie-Hellman key agreement function defined in RFC 7748, exposed through the standard AsymmetricAlgorithm framework. This class cannot be inherited.

public sealed class X25519 : RawKeyAsymmetricAlgorithm, IDisposable
Inheritance
X25519
Implements
Inherited Members
Extension Methods

Examples

using var alice = X25519.Create();
using var bob = X25519.Create();
alice.GenerateKey();
bob.GenerateKey();

byte[] aliceShared = alice.DeriveSharedSecret(bob.ExportPublicKey());
byte[] bobShared = bob.DeriveSharedSecret(alice.ExportPublicKey());
// aliceShared and bobShared are identical.

Remarks

X25519 performs scalar multiplication on the Montgomery form of Curve25519, providing a 128-bit security level with 32-byte keys and a 32-byte shared secret. Two parties each generate a key pair, exchange public keys, and call DeriveSharedSecret(ReadOnlySpan<byte>) with the peer's public key to arrive at the same shared secret. The shared secret is a raw curve point coordinate and should be passed through a key derivation function (such as HKDF or Blake2b in keyed mode) before use as symmetric key material.

Parameters at a glance.

  • Private key: 32 bytes; clamped per RFC 7748 §5 at the point of use, never in stored form.
  • Public key: 32 bytes (the little-endian u-coordinate of the scalar multiple of the base point).
  • Shared secret: 32 bytes; security level: 128 bits.
  • Specification: RFC 7748.

Low-order point rejection. When the peer public key is one of the small set of low-order points, every scalar produces an all-zero shared secret that an attacker can predict without knowing the private key. DeriveSharedSecret(ReadOnlySpan<byte>) applies the check recommended by RFC 7748 §6.1 strictly and throws CryptographicException instead of returning the all-zero output.

Key formats. Only the raw RFC 7748 32-byte key encodings are supported via ImportPrivateKey(ReadOnlySpan<byte>), ImportPublicKey(ReadOnlySpan<byte>), ExportPrivateKey(), and ExportPublicKey(), plus the RFC 8410 PKCS#8 / SubjectPublicKeyInfo DER containers (OID 1.3.101.110) and the RFC 7468 PEM helpers inherited from AsymmetricAlgorithm (ImportFromPem, ExportPkcs8PrivateKeyPem, ExportSubjectPublicKeyInfoPem). The XML members and encrypted PKCS#8 are out of scope and throw NotSupportedException.

Scalar multiplication runs in constant time with respect to the private key. Private key material is zeroed when the instance is disposed. This implementation, like the rest of the library, offers best-effort side-channel resistance and has not been independently audited.

Constructors

X25519()

Initializes a new instance of the X25519 class with no key material.

public X25519()

Remarks

Call GenerateKey() to create a fresh key pair, or import existing material with ImportPrivateKey(ReadOnlySpan<byte>) or ImportPublicKey(ReadOnlySpan<byte>) before deriving a shared secret.

Fields

KeySizeInBytes

The size, in bytes, of an X25519 private key and public key (32 bytes each).

public const int KeySizeInBytes = 32

Field Value

int

SharedSecretSizeInBytes

The size, in bytes, of the shared secret produced by DeriveSharedSecret(ReadOnlySpan<byte>).

public const int SharedSecretSizeInBytes = 32

Field Value

int

Properties

AlgorithmName

Gets the algorithm name "X25519".

public string AlgorithmName { get; }

Property Value

string

The constant string "X25519".

HasPrivateKey

Gets a value indicating whether the instance currently holds a private key.

public bool HasPrivateKey { get; }

Property Value

bool

true when private key material is present; otherwise, false.

Exceptions

ObjectDisposedException

The instance has been disposed.

HasPublicKey

Gets a value indicating whether the instance currently holds a public key.

public bool HasPublicKey { get; }

Property Value

bool

true when public key material is present; otherwise, false.

Exceptions

ObjectDisposedException

The instance has been disposed.

KeyExchangeAlgorithm

When overridden in a derived class, gets the name of the key exchange algorithm. Otherwise, throws an NotImplementedException.

public override string? KeyExchangeAlgorithm { get; }

Property Value

string

The name of the key exchange algorithm.

SecurityStrengthBits

Gets the approximate classical security strength of X25519, in bits.

public int SecurityStrengthBits { get; }

Property Value

int

The value 128.

SignatureAlgorithm

When implemented in a derived class, gets the name of the signature algorithm. Otherwise, always throws a NotImplementedException.

public override string? SignatureAlgorithm { get; }

Property Value

string

The name of the signature algorithm.

Methods

Create()

Creates a new X25519 instance with no key material.

public static X25519 Create()

Returns

X25519

A new X25519 instance.

DeriveSharedSecret(ReadOnlySpan<byte>)

Derives the 32-byte X25519 shared secret between this instance's private key and the supplied peer public key.

public byte[] DeriveSharedSecret(ReadOnlySpan<byte> peerPublicKey)

Parameters

peerPublicKey ReadOnlySpan<byte>

The peer's 32-byte RFC 7748 public key.

Returns

byte[]

The 32-byte shared secret. Pass the value through a KDF before using it as symmetric key material.

Exceptions

ObjectDisposedException

The instance has been disposed.

ArgumentException

peerPublicKey is not exactly 32 bytes long.

CryptographicException

The instance does not hold a private key, or peerPublicKey is a low-order point whose shared secret would be all zero.

DeriveSharedSecret(ReadOnlySpan<byte>, Span<byte>)

Derives the 32-byte X25519 shared secret between this instance's private key and the supplied peer public key, writing it into destination.

public void DeriveSharedSecret(ReadOnlySpan<byte> peerPublicKey, Span<byte> destination)

Parameters

peerPublicKey ReadOnlySpan<byte>

The peer's 32-byte RFC 7748 public key.

destination Span<byte>

The 32-byte span that receives the shared secret.

Exceptions

ObjectDisposedException

The instance has been disposed.

ArgumentException

peerPublicKey or destination is not exactly 32 bytes long.

CryptographicException

The instance does not hold a private key, or peerPublicKey is a low-order point whose shared secret would be all zero. The destination is zeroed before the exception is thrown.

ExportPrivateKey()

Exports the raw 32-byte RFC 7748 private key.

public byte[] ExportPrivateKey()

Returns

byte[]

A fresh copy of the 32-byte private scalar exactly as generated or imported.

Exceptions

ObjectDisposedException

The instance has been disposed.

CryptographicException

The instance does not hold a private key.

ExportPublicKey()

Exports the raw 32-byte RFC 7748 public key.

public byte[] ExportPublicKey()

Returns

byte[]

A fresh copy of the 32-byte public u-coordinate.

Exceptions

ObjectDisposedException

The instance has been disposed.

CryptographicException

The instance does not hold a public key.

FromXmlString(string)

When overridden in a derived class, reconstructs an AsymmetricAlgorithm object from an XML string. Otherwise, throws a NotImplementedException.

public override void FromXmlString(string xmlString)

Parameters

xmlString string

The XML string to use to reconstruct the AsymmetricAlgorithm object.

GenerateKey()

Generates a fresh random key pair, replacing any existing key material on the instance.

public void GenerateKey()

Remarks

The private key is drawn from a cryptographically secure random source. Any previously held private key is zeroed before being replaced.

Exceptions

ObjectDisposedException

The instance has been disposed.

ImportEncryptedPkcs8PrivateKey(ReadOnlySpan<byte>, ReadOnlySpan<byte>, out int)

When overridden in a derived class, imports the public/private keypair from a PKCS#8 EncryptedPrivateKeyInfo structure after decrypting with a byte-based password, replacing the keys for this object.

public override void ImportEncryptedPkcs8PrivateKey(ReadOnlySpan<byte> passwordBytes, ReadOnlySpan<byte> source, out int bytesRead)

Parameters

passwordBytes ReadOnlySpan<byte>

The bytes to use as a password when decrypting the key material.

source ReadOnlySpan<byte>

The bytes of a PKCS#8 EncryptedPrivateKeyInfo structure in the ASN.1-BER encoding.

bytesRead int

When this method returns, contains a value that indicates the number of bytes read from source. This parameter is treated as uninitialized.

Exceptions

CryptographicException

The password is incorrect.

-or-

The contents of source indicate the Key Derivation Function (KDF) to apply is the legacy PKCS#12 KDF, which requires char-based passwords.

-or-

The contents of source do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure.

-or-

The contents of source indicate the key is for an algorithm other than the algorithm represented by this instance.

-or-

The contents of source represent the key in a format that is not supported.

-or-

The algorithm-specific key import failed.

NotImplementedException

A derived type has not overriden this member.

ImportEncryptedPkcs8PrivateKey(ReadOnlySpan<char>, ReadOnlySpan<byte>, out int)

When overridden in a derived class, imports the public/private keypair from a PKCS#8 EncryptedPrivateKeyInfo structure after decrypting with a char-based password, replacing the keys for this object.

public override void ImportEncryptedPkcs8PrivateKey(ReadOnlySpan<char> password, ReadOnlySpan<byte> source, out int bytesRead)

Parameters

password ReadOnlySpan<char>

The password to use for decrypting the key material.

source ReadOnlySpan<byte>

The bytes of a PKCS#8 EncryptedPrivateKeyInfo structure in the ASN.1-BER encoding.

bytesRead int

When this method returns, contains a value that indicates the number of bytes read from source. This parameter is treated as uninitialized.

Exceptions

CryptographicException

The password is incorrect.

-or-

The contents of source do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure.

-or-

The contents of source indicate the key is for an algorithm other than the algorithm represented by this instance.

-or-

The contents of source represent the key in a format that is not supported.

-or-

The algorithm-specific key import failed.

NotImplementedException

A derived type has not overriden this member.

ImportPkcs8PrivateKey(ReadOnlySpan<byte>, out int)

When overriden in a derived class, imports the public/private keypair from a PKCS#8 PrivateKeyInfo structure after decryption, replacing the keys for this object.

public override void ImportPkcs8PrivateKey(ReadOnlySpan<byte> source, out int bytesRead)

Parameters

source ReadOnlySpan<byte>

The bytes of a PKCS#8 PrivateKeyInfo structure in the ASN.1-BER encoding.

bytesRead int

When this method returns, contains a value that indicates the number of bytes read from source. This parameter is treated as uninitialized.

Exceptions

CryptographicException

The contents of source do not represent an ASN.1-BER-encoded PKCS#8 PrivateKeyInfo structure.

-or-

The contents of source indicate the key is for an algorithm other than the algorithm represented by this instance.

-or-

The contents of source represent the key in a format that is not supported.

-or-

The algorithm-specific key import failed.

NotImplementedException

A derived type has not overriden this member.

ImportPrivateKey(ReadOnlySpan<byte>)

Imports a raw 32-byte RFC 7748 private key and derives the matching public key, replacing any existing key material on the instance.

public void ImportPrivateKey(ReadOnlySpan<byte> privateKey)

Parameters

privateKey ReadOnlySpan<byte>

The 32-byte private scalar to import. The caller's buffer is copied and never modified.

Remarks

The scalar is stored exactly as supplied; RFC 7748 clamping is applied at the point of use, so the value returned by ExportPrivateKey() round-trips byte-for-byte.

Exceptions

ObjectDisposedException

The instance has been disposed.

ArgumentException

privateKey is not exactly 32 bytes long.

ImportPublicKey(ReadOnlySpan<byte>)

Imports a raw 32-byte RFC 7748 public key, replacing any existing key material on the instance.

public void ImportPublicKey(ReadOnlySpan<byte> publicKey)

Parameters

publicKey ReadOnlySpan<byte>

The 32-byte public u-coordinate to import.

Remarks

This import is purely syntactic: it validates only the 32-byte length and stores the u-coordinate verbatim. It does not prove the point is contributory or reject low-order points - RFC 7748 defines X25519 over u-coordinates and places the all-zero-output check at key-agreement time. DeriveSharedSecret(ReadOnlySpan<byte>) performs that mandatory rejection; IsLowOrderPoint(ReadOnlySpan<byte>) is available for callers that want to screen a peer key before agreement.

Any private key previously held by the instance is zeroed and discarded, leaving a public-only instance whose key can be exported but that cannot derive shared secrets.

Exceptions

ObjectDisposedException

The instance has been disposed.

ArgumentException

publicKey is not exactly 32 bytes long.

ImportSubjectPublicKeyInfo(ReadOnlySpan<byte>, out int)

When overriden in a derived class, imports the public key from an X.509 SubjectPublicKeyInfo structure after decryption, replacing the keys for this object.

public override void ImportSubjectPublicKeyInfo(ReadOnlySpan<byte> source, out int bytesRead)

Parameters

source ReadOnlySpan<byte>

The bytes of an X.509 SubjectPublicKeyInfo structure in the ASN.1-DER encoding.

bytesRead int

When this method returns, contains a value that indicates the number of bytes read from source. This parameter is treated as uninitialized.

Exceptions

CryptographicException

The contents of source do not represent an ASN.1-DER-encoded X.509 SubjectPublicKeyInfo structure.

-or-

The contents of source indicate the key is for an algorithm other than the algorithm represented by this instance.

-or-

The contents of source represent the key in a format that is not supported.

-or-

The algorithm-specific key import failed.

NotImplementedException

A derived type has not overriden this member.

IsLowOrderPoint(ReadOnlySpan<byte>)

Determines whether publicKey is one of the known low-order Curve25519 u-coordinates, for which X25519 yields an all-zero shared secret that an observer can predict without the private key.

public static bool IsLowOrderPoint(ReadOnlySpan<byte> publicKey)

Parameters

publicKey ReadOnlySpan<byte>

The 32-byte peer u-coordinate to screen.

Returns

bool

true when the encoding (ignoring its high bit, per RFC 7748) is a low-order point; otherwise, false.

Remarks

This is an optional preflight for callers that prefer to reject a peer key before agreement, for example to avoid even attempting a derivation. It is not required for safety: DeriveSharedSecret(ReadOnlySpan<byte>) already rejects the all-zero result that these points produce. The comparison clears the ignored bit 255 of the input so non-canonical high-bit-set encodings of the same coordinate are also detected.

Exceptions

ArgumentException

publicKey is not exactly 32 bytes long.

ToXmlString(bool)

When overridden in a derived class, creates and returns an XML string representation of the current AsymmetricAlgorithm object. Otherwise, throws a NotImplementedException.

public override string ToXmlString(bool includePrivateParameters)

Parameters

includePrivateParameters bool

true to include private parameters; otherwise, false.

Returns

string

An XML string encoding of the current AsymmetricAlgorithm object.

TryExportEncryptedPkcs8PrivateKey(ReadOnlySpan<byte>, PbeParameters, Span<byte>, out int)

When overridden in a derived class, attempts to export the current key in the PKCS#8 EncryptedPrivateKeyInfo format into a provided buffer, using a byte-based password.

public override bool TryExportEncryptedPkcs8PrivateKey(ReadOnlySpan<byte> passwordBytes, PbeParameters pbeParameters, Span<byte> destination, out int bytesWritten)

Parameters

passwordBytes ReadOnlySpan<byte>

The bytes to use as a password when encrypting the key material.

pbeParameters PbeParameters

The password-based encryption (PBE) parameters to use when encrypting the key material.

destination Span<byte>

The byte span to receive the PKCS#8 EncryptedPrivateKeyInfo data.

bytesWritten int

When this method returns, contains a value that indicates the number of bytes written to destination. This parameter is treated as uninitialized.

Returns

bool

true if destination is big enough to receive the output; otherwise, false.

Exceptions

CryptographicException

The key could not be exported.

-or-

pbeParameters indicates that TripleDes3KeyPkcs12 should be used, which requires char-based passwords.

NotImplementedException

A derived type has not overriden this member.

TryExportEncryptedPkcs8PrivateKey(ReadOnlySpan<char>, PbeParameters, Span<byte>, out int)

When overriden in a derived class, attempts to export the current key in the PKCS#8 EncryptedPrivateKeyInfo format into a provided buffer, using a char-based password.

public override bool TryExportEncryptedPkcs8PrivateKey(ReadOnlySpan<char> password, PbeParameters pbeParameters, Span<byte> destination, out int bytesWritten)

Parameters

password ReadOnlySpan<char>

The password to use when encrypting the key material.

pbeParameters PbeParameters

The password-based encryption (PBE) parameters to use when encrypting the key material.

destination Span<byte>

The byte span to receive the PKCS#8 EncryptedPrivateKeyInfo data.

bytesWritten int

When this method returns, contains a value that indicates the number of bytes written to destination. This parameter is treated as uninitialized.

Returns

bool

true if destination is big enough to receive the output; otherwise, false.

Exceptions

CryptographicException

The key could not be exported.

NotImplementedException

A derived type has not overriden this member.

TryExportPkcs8PrivateKey(Span<byte>, out int)

When overridden in a derived class, attempts to export the current key in the PKCS#8 PrivateKeyInfo format into a provided buffer.

public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)

Parameters

destination Span<byte>

The byte span to receive the PKCS#8 PrivateKeyInfo data.

bytesWritten int

When this method returns, contains a value that indicates the number of bytes written to destination. This parameter is treated as uninitialized.

Returns

bool

true if destination is big enough to receive the output; otherwise, false.

Exceptions

CryptographicException

The key could not be exported.

NotImplementedException

A derived type has not overriden this member.

TryExportSubjectPublicKeyInfo(Span<byte>, out int)

When overridden in a derived class, attempts to export the current key in the X.509 SubjectPublicKeyInfo format into a provided buffer.

public override bool TryExportSubjectPublicKeyInfo(Span<byte> destination, out int bytesWritten)

Parameters

destination Span<byte>

The byte span to receive the X.509 SubjectPublicKeyInfo data.

bytesWritten int

When this method returns, contains a value that indicates the number of bytes written to destination. This parameter is treated as uninitialized.

Returns

bool

true if destination is big enough to receive the output; otherwise, false.

Exceptions

CryptographicException

The key could not be exported.

NotImplementedException

A derived type has not overriden this member.

Applies to

ProductVersions
.NET8, 10