5.1 P/Invoke - Incorporare DLL

Questa pagina mostra come usare OpenSSL tramite P/Invoke in .NET.


Cos'è P/Invoke?

P/Invoke (Platform Invoke) permette di chiamare funzioni DLL native da .NET:

Codice .NET  →  P/Invoke  →  libcrypto-3-x64.dll  →  OpenSSL

Preparare le DLL

1. Copiare le DLL nel Progetto

cd MioProjetto
copy "D:\Projects\openssl-3.6.0\bin\bin\libcrypto-3-x64.dll" .\
copy "D:\Projects\openssl-3.6.0\bin\bin\libssl-3-x64.dll" .\

2. Modificare .csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
 
  <ItemGroup>
    <!-- Librerie Native OpenSSL -->
    <None Update="libcrypto-3-x64.dll">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="libssl-3-x64.dll">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>

Esempio Semplice P/Invoke

using System;
using System.Runtime.InteropServices;
 
namespace MioProjetto;
 
/// <summary>
/// Binding P/Invoke per OpenSSL
/// </summary>
public static class OpenSslInterop
{
    private const string LIBCRYPTO = "libcrypto-3-x64.dll";
 
    // Inizializzazione
    [DllImport(LIBCRYPTO, CallingConvention = CallingConvention.Cdecl)]
    public static extern int OPENSSL_init_crypto(ulong opts, IntPtr settings);
 
    // Ottenere versione
    [DllImport(LIBCRYPTO, CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr OpenSSL_version(int type);
 
    // Costanti
    public const int OPENSSL_VERSION_STRING = 6;
    public const ulong OPENSSL_INIT_LOAD_CRYPTO_STRINGS = 0x00000002;
    public const ulong OPENSSL_INIT_ADD_ALL_CIPHERS = 0x00000004;
    public const ulong OPENSSL_INIT_ADD_ALL_DIGESTS = 0x00000008;
 
    /// <summary>
    /// Inizializza OpenSSL
    /// </summary>
    public static void Initialize()
    {
        OPENSSL_init_crypto(
            OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
            OPENSSL_INIT_ADD_ALL_CIPHERS |
            OPENSSL_INIT_ADD_ALL_DIGESTS,
            IntPtr.Zero);
    }
 
    /// <summary>
    /// Restituisce la versione OpenSSL
    /// </summary>
    public static string GetVersion()
    {
        var ptr = OpenSSL_version(OPENSSL_VERSION_STRING);
        return Marshal.PtrToStringAnsi(ptr) ?? "Unknown";
    }
}
 
// Utilizzo:
class Program
{
    static void Main()
    {
        OpenSslInterop.Initialize();
        Console.WriteLine($"Versione OpenSSL: {OpenSslInterop.GetVersion()}");
    }
}

Generazione Chiavi ML-DSA

public static class MlDsaInterop
{
    private const string LIBCRYPTO = "libcrypto-3-x64.dll";
 
    [DllImport(LIBCRYPTO)] private static extern IntPtr EVP_PKEY_CTX_new_from_name(IntPtr libctx, string name, IntPtr propq);
    [DllImport(LIBCRYPTO)] private static extern int EVP_PKEY_keygen_init(IntPtr ctx);
    [DllImport(LIBCRYPTO)] private static extern int EVP_PKEY_keygen(IntPtr ctx, out IntPtr pkey);
    [DllImport(LIBCRYPTO)] private static extern void EVP_PKEY_CTX_free(IntPtr ctx);
    [DllImport(LIBCRYPTO)] private static extern void EVP_PKEY_free(IntPtr pkey);
    [DllImport(LIBCRYPTO)] private static extern int i2d_PrivateKey(IntPtr pkey, ref IntPtr pp);
    [DllImport(LIBCRYPTO)] private static extern int i2d_PUBKEY(IntPtr pkey, ref IntPtr pp);
    [DllImport(LIBCRYPTO)] private static extern void OPENSSL_free(IntPtr ptr);
 
    /// <summary>
    /// Genera una coppia di chiavi ML-DSA-65
    /// </summary>
    public static (byte[] privateKey, byte[] publicKey) GenerateMlDsa65KeyPair()
    {
        IntPtr ctx = EVP_PKEY_CTX_new_from_name(IntPtr.Zero, "mldsa65", IntPtr.Zero);
        if (ctx == IntPtr.Zero)
            throw new Exception("Impossibile creare il contesto");
 
        try
        {
            if (EVP_PKEY_keygen_init(ctx) <= 0)
                throw new Exception("Impossibile inizializzare keygen");
 
            IntPtr pkey;
            if (EVP_PKEY_keygen(ctx, out pkey) <= 0)
                throw new Exception("Impossibile generare la chiave");
 
            try
            {
                // Esportare chiave privata
                IntPtr privPtr = IntPtr.Zero;
                int privLen = i2d_PrivateKey(pkey, ref privPtr);
                byte[] privateKey = new byte[privLen];
                Marshal.Copy(privPtr, privateKey, 0, privLen);
                OPENSSL_free(privPtr);
 
                // Esportare chiave pubblica
                IntPtr pubPtr = IntPtr.Zero;
                int pubLen = i2d_PUBKEY(pkey, ref pubPtr);
                byte[] publicKey = new byte[pubLen];
                Marshal.Copy(pubPtr, publicKey, 0, pubLen);
                OPENSSL_free(pubPtr);
 
                return (privateKey, publicKey);
            }
            finally
            {
                EVP_PKEY_free(pkey);
            }
        }
        finally
        {
            EVP_PKEY_CTX_free(ctx);
        }
    }
}

Classe Interop Completa

Per un'implementazione P/Invoke completa vedere:

API WvdS.System.Security.Cryptography

Questa libreria contiene:

  • Firma/Verifica ML-DSA
  • Incapsulamento/Decapsulamento ML-KEM
  • Certificati X.509 con algoritmi PQ
  • Firme ibride

Errori Comuni

"DLL not found"

System.DllNotFoundException: Unable to load DLL 'libcrypto-3-x64.dll'

Soluzione:

  1. DLL presenti nella directory di output (bin/Debug/)?
  2. CopyToOutputDirectory corretto in .csproj?
  3. Piattaforma corretta? (x64 vs x86)

"Entry point not found"

System.EntryPointNotFoundException: Unable to find entry point 'XYZ'

Soluzione:

  1. Nome funzione scritto correttamente?
  2. CallingConvention.Cdecl impostato?
  3. Versione OpenSSL supporta la funzione?

Memory Leak

Liberare sempre le risorse!

  • EVP_PKEY_free(pkey)
  • EVP_PKEY_CTX_free(ctx)
  • OPENSSL_free(ptr)

Suggerimenti

LibraryImport invece di DllImport (da .NET 7)

// Approccio moderno
[LibraryImport("libcrypto-3-x64.dll")]
public static partial int OPENSSL_init_crypto(ulong opts, IntPtr settings);

Continua con


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional

Zuletzt geändert: il 30/01/2026 alle 08:57