Développement d'applications - cybersécurité - radiogéolocalisation et radionavigation

Actualités à ne pas manquer

Venez nous rencontrer lors de nos conférences.

Notre savoir faire

Technologies et services

Notre entreprise

Cyberens est une société de conseil fondée en 2010 qui est spécialisée dans ​​les technologies de défense, de sécurité et de l’espace. Située à Bordeaux , nos clients sont français et internationaux.

Importer des clés RSA OpenSSL avec Delphi et Indy

OpenSSL est une bibliothèque de chiffrement open-source implémentant TLS et son prédécesseur SSL. Elle est massivement utilisée par les sites web en HTTPS. Certains composants Indy utilisent OpenSSL comme TIdServerIOHandlerSSLOpenSSL ou TIdSSLIOHandlerSocketOpenSSL. Attention, Indy utilise la version 1.0.2 d’OpenSSL qui n’est pas la dernière version.
Dans cet article, on va utiliser les fichiers IdSSLOpenSSLHeaders et IdSSLOpenSSL d’Indy. Pour iOS, on aura aussi besoin de IdSSLOpenSSLHeaders_Static.

Le but est d’importer les clés RSA contenues dans un fichier au format PEM afin de pouvoir les lire ou les utiliser par la suite. On va traiter trois cas :

  • Une clé publique
  • Un certificat
  • Une clé privée (chiffrée ou non)

Charger OpenSSL

Pour utiliser la bibliothèque OpenSSL sous Windows, OSX ou iOS, il faut qu’elle soit installée sur votre machine. Embarcadero explique comment faire ici : http://docwiki.embarcadero.com/RADStudio/Tokyo/fr/OpenSSL

Pour Android, il y a deux cas :
– si vous avez une version < 6.0 alors vous n’avez rien à faire, OpenSSL est inclus par défaut.
– si vous avez une version >= 6.0 alors il vous faut des fichiers libcrypto.so* et libssl.so* (par exemple, libcrypto.so.1.0.0). Il faut les déployer dans ./assets/internal pour qu’ils soient accessibles via GetDocumentsPath. Vous pouvez en trouver des versions ici : https://indy.fulgan.com/SSL/

Ensuite, il faut charger la bibliothèque dans votre programme. Les fonctions dont on va avoir besoin sont dans la partie crypto d’OpenSSL. On n’a pas besoin de charger la bibliothèque sous iOS car elle est liée statiquement au programme.

function TFromOpenSSL.LoadSSLCryptoLibrary: HMODULE;

begin

{$IFDEF MSWINDOWS}

Result := SafeLoadLibrary(‘libeay32.dll’);

{$ELSE}

{$IFDEF ANDROID}

Result := LoadLibrary(‘libcrypto.so’);

{$ELSE}

{$IFNDEF IOS}

Result := LoadLibrary(‘libcrypto.dylib’);

{$ENDIF}

{$ENDIF}

{$ENDIF}

end;

Cette fonction est utilisée dans le chargement de toute la bibliothèque OpenSSL. Certaines fonctions que l’on va utiliser par la suite ne sont pas chargées dans IdSSLOpenSSLHeaders donc on indique qu’elles se trouvent dans le FSSLModule.

function TFromOpenSSL.LoadOpenSSLLibrary: Boolean;

begin

{FSSLModule est le HMODULE privé de la classe dans lequel est chargé libcrypto}

  if FSSLModule <> 0 then

    Exit(True);

{$IFDEF ANDROID}

  IdOpenSSLSetLibPath(System.IOUtils.TPath.GetDocumentsPath);

{$ENDIF}

  Result := IdSSLOpenSSL.LoadOpenSSLLibrary;

  if Result then

  begin

{$IFNDEF IOS}

    FSSLModule := LoadSSLCryptoLibrary;

    if FSSLModule = 0 then

      Exit(False);

{On indique où sont les fonctions non chargées dans IdSSLOpenSSLHeaders}

    PEM_read_bio_PUBKEY := GetProcAddress(FSSLModule, PChar(‘PEM_read_bio_PUBKEY’));

    X509_get_pubkey := GetProcAddress(FSSLModule, PChar(‘X509_get_pubkey’));

    EVP_PKEY_get1_RSA := GetProcAddress(FSSLModule, PChar(‘EVP_PKEY_get1_RSA’));

    OpenSSL_add_all_algorithms;

    OpenSSL_add_all_ciphers;

    OpenSSL_add_all_digests;

    ERR_load_crypto_strings;

{$ENDIF}

  end;

end;

Charger les fichiers PEM

On va charger le contenu des fichiers PEM dans un PBIO.

function TFromOpenSSL.LoadPEMFile(filePath: string): PBio;

var

{$IFNDEF MSWINDOWS}

  LEncoding: TEncoding;

  LOffset: Integer;

{$ENDIF}

  Buffer: TBytes;

  Stream: TStream;

begin

  Stream := TFileStream.Create(filePath, fmOpenRead or fmShareDenyWrite);

  try

    SetLength(Buffer, Stream.size);

    Stream.ReadBuffer(Buffer[0], Stream.size);

{$IFNDEF MSWINDOWS}

{On traite les problèmes d’encodage de flux sur les plateformes différentes de Windows}

    LEncoding := nil;

    LOffset := TEncoding.GetBufferEncoding(Buffer, LEncoding);

    Buffer := LEncoding.Convert(LEncoding, TEncoding.UTF8, Buffer, LOffset,

      Length(Buffer) – LOffset);

{$ENDIF}

    Result := BIO_new_mem_buf(Buffer, Length(Buffer));

  finally

    Stream.free;

  end;

end;

 

Importer une clé publique RSA

Un fichier au format PEM contenant une clé publique RSA commence par —–BEGIN PUBLIC KEY—– puis est suivi de la clé en Base64 et se termine par —–END PUBLIC KEY—–.

function TFromOpenSSL.FromOpenSSLPublicKey(filePath: string): pRSA;

var

  KeyBuffer: PBIO;

  pkey: PEVP_PKEY;

begin

  KeyBuffer := LoadPEMFile(filePath);

  if KeyBuffer = nil then

    raise Exception.Create(‘Impossible de charger le buffer’);

  try

    pkey := PEM_read_bio_PUBKEY(KeyBuffer, nil, nil, nil);

    if not Assigned(pkey) then

      raise Exception.Create(‘Impossible de charger la clé publique’);

    try

      Result := EVP_PKEY_get1_RSA(pkey);

      if not Assigned(Result) then

        raise Exception.Create(‘Impossible de charger la clé publique RSA’);

    finally

      EVP_PKEY_free(pkey);

    end;

  finally

    BIO_free(KeyBuffer);

  end;

end;

Importer une clé publique RSA à partir d’un certificat X509

Un fichier au format PEM contenant un certificat X509 commence par —–BEGIN CERTIFICATE—– puis est suivi de la clé en Base64 et se termine par —–END CERTIFICATE—–.

function TFromOpenSSL.FromOpenSSLCert(filePath: string): pRSA;

var

  KeyBuffer: PBIO;

  FX509: pX509;

  Key: PEVP_PKEY;

begin

  KeyBuffer := LoadPEMFile(Buffer, Length(Buffer));

  if KeyBuffer = nil then

    raise Exception.Create(‘Impossible de charger le buffer X509’);

  try

    FX509 := PEM_read_bio_X509(KeyBuffer, nil, nil, nil);

    if not Assigned(FX509) then

      raise Exception.Create(‘Impossible de charger le certificat X509’);

    Key := X509_get_pubkey(FX509);

    if not Assigned(Key) then

      raise Exception.Create(‘Impossible de charger la clé publique X509’);

    try

      Result := EVP_PKEY_get1_RSA(Key);

      if not Assigned(Result) then

        raise Exception.Create(‘Impossible de charger la clé publique RSA’);

    finally

      EVP_PKEY_free(Key);

    end;

  finally

    BIO_free(KeyBuffer);

  end;

end;

Importer une clé privée RSA (chiffrée ou non)

Un fichier au format PEM contenant un clé privée RSA commence par —–BEGIN PRIVATE KEY—– puis est suivi de la clé en Base64 et se termine par —–END PRIVATE KEY—–. Si la clé est chiffrée, alors le fichier au format PEM commence par —–BEGIN RSA PRIVATE KEY—– puis est suivi de Proc-Type: 4,ENCRYPTED. Ensuite, il y a des informations sur l’algorithme utilisé pour chiffrer la clé (par exemple AES-128-CBC) puis il y a la clé chiffrée, en Base64. Enfin, le fichier se termine par —–END RSA PRIVATE KEY—–. Dans ce dernier cas, la fonction doit avoir pour entrée le mot de passe, pwd, pour déchiffrer la clé. La fonction PEM_read_bio_RSAPrivateKey prend un PAnsiChar (sous Windows ou OSX) ou un PByte (sous les plateformes mobiles) comme quatrième argument. Cet argument peut contenir le mot de passe. On a donc déclaré le type PReadKeyChar comme ceci:

{$IF (defined(MSWINDOWS) or defined(MACOS)) and (not defined(IOS))}

ReadKeyChar = AnsiChar;

{$ELSE}

ReadKeyChar = Byte;

{$ENDIF}

PReadKeyChar = ^ReadKeyChar;

La méthode FromOpenSSLPrivateKey est définie comme suit:

function TFromOpenSSL.FromOpenSSLPrivateKey(filePath: string;

  pwd: String): pRSA;

var

  KeyBuffer: PBio;

  p: PReadKeyChar;

  I: Integer;

begin

  KeyBuffer := LoadPEMFile(filePath);

  if KeyBuffer = nil then

    raise Exception.Create(‘Impossible de charger le buffer’);

  try

    if pwd <> then

    begin

      p := GetMemory((pwd.Length + 1) * SizeOf(Char));

      for I := 0 to pwd.Length – 1 do

        p[I] := ReadKeyChar(pwd.Chars[I]);

      p[pwd.Length] := ReadKeyChar(#0);

    end

    else

      p := nil;

    try

      Result := PEM_read_bio_RSAPrivateKey(KeyBuffer, nil, nil, p);

      if not Assigned(Result) then

        raise Exception.Create(‘Impossible de charger la clé privée RSA’);

    finally

{On efface le mot de passe}

      FillChar(p, SizeOf(p), 0);

      FreeMem(p);

    end;

  finally

    BIO_free(KeyBuffer);

  end;

end;

Vous trouverez le code source complet de la classe ici.
En conclusion, on peut utiliser OpenSSL avec Delphi grâce à Indy. Cependant, il faut garder à l’esprit que beaucoup de vulnérabilités sont trouvées dans OpenSSL et beaucoup d’algorithmes utilisés sont obsolètes, notamment l’algorithme utilisé dans le format PEM pour dériver une clé d’un mot de passe, car il utilise l’algorithme MD5.  Il faut donc être vigilant et suivre de bonnes pratiques en cryptographie, notamment concernant les longueurs de clés minimales recommandées qu’on peut retrouver sur https://www.keylength.com/fr/compare/

Syntaxe du code grâce à http://markup.su/highlighter/

 

  • 10 octobre 2017 : cybersécurité des objets connectés au séminaire “CAP sur l’innovation : Internet des objets, enfin l’âge de raison ?”, Paris
  • 9 octobre 2017 : journée européenne de la cybersécurité, CCI de Bordeaux
  • 21 septembre 2017 :”La sécurité des systèmes embarqués” au salon ENOVA dans l’atelier de Captronic, Paris.
  • 19 juillet 2017 : article sur la cryptographie sur developpez.com
  • 20 juin 2017 : cybersécurité des objets connectés au séminaire Captronic sur la site de l’école des Mines de Saint-Etienne à Gardanne. Présentation sur l’analyse de risque et les bonnes pratiques en ingénierie de sécurité.
  • 8 juin 2017 : TMS Day à Courtrai. Présentation et démo de notre bibliothèque cryptographique dans un environnement multi-plateformes Windows, Android et Linux.
  • 17 mai 2017 : Présentation de TMS Cryptography Pack pour le lancement de RAD Studio 10.2 Tokyo à Paris
  • 28 avril 2017 : réunion de clôture du projet Horizon 2020 GALENA à Horst (Pays-Bas). Le projet a permis la réalisation d’une application complète de suivi de bout en bout de colis intégrant la preuve de transaction pour chaque intermédiaire.
  • 14 et 16 mars 2017 : A Bordeaux et Nantes, journées RAD Studio 10.1.2 (Delphi et C++ Builder) organisées par Barnsten avec une démo de TMS Cryptography Pack par Cyberens.
  • 20 décembre 2016: GALENA reçoit le prix de l’innovation de la région flamande pour l’European Satellite Navigation Competition (ESNC) 2016.
  • 30 juin 2016: réunion préparatoire du projet USC4BUS pour l’utilisation des ultrasons en billetique.
  • 20 juin 2016: TMS  Software et Cyberens signent un accord de distribution pour COCryL sous le nom commercial de TMS  Cryptography Pack.
  • TMS Software et Cyberens ont conclu un partenariat avec l’objectif de développer conjointement des solutions cryptographiques pour les utilisateurs Delphi, C++ et C. Dans ce partenariat, Cyberens apporte son expertise en cryptographie et TMS Software apporte son expérience des composants intuitifs et faciles à utiliser pour les environnement Delphi et C++Builder.La bibliothèque TMS Cryptography Pack est sur cette page et les discussions sur ce forum.

  • 20-21 mai 2016: deuxième Act In Space organisé par le CNES et l’ESA dans les locaux de l’ENSEIRB-MATMECA à Bordeaux.

  • 18-19 février 2016: réunion GALENA de mi-projet à Prague dans les locaux de la GSA.
  • 27 mars 2015 : Cyberens sponsor du STHACK
  • 27 janvier 2015 : réunion de lancement du projet européen GALENA chez Héliléo à Dax
  • 27 novembre 2014 : destinées numériques à l’ENSEIRB-MATMECA
  • 20 octobre 2014 : article sur la NSA et le chiffrement dans la revue des télécoms
  • 23 septembre 2014 : participation à CHES
  • 11 juillet 2014: Journée French Tech à Bordeaux “Darwin”
  • 1er et 2 juillet 2014 : Cyberens au Toulouse Space Show Toulouse Space Show pour TOPOS
  • 20 juin 2014 : Journée RSSI organisée par le CLUSIR Aquitaine
  • 23 et 24 mai 2014 : Cyberens dans le jury sur le site de Bordeaux TechnoWest d’ActInSpace organisé par le CNES.
cyberens-blanc

Tour 6,

74 rue Georges Bonnac

33000 Bordeaux

Mentions Légales

©copyrights 2019