---
title: "JAVA : Crypter/Décrypter des données avec une courbe elliptique."
canonical: "https://blog.kaptus.net/space/BKAP/blog/266928129/JAVA%20%3A%20Crypter%2FD%C3%A9crypter%20des%20donn%C3%A9es%20avec%20une%20courbe%20elliptique."
format: markdown
---
> Macro (toc)

# 1. Introduction.

Cet article à  pour but de présenter une méthode pour crypter/décrypter des données avec un algorithme de courbe elliptique ([Curve25519](https://en.wikipedia.org/wiki/Curve25519)) et récupération de le clé de cryptage dans le '***AWS Parameter store***' en **JAVA 21**.

J’ai souhaité écrire cet article car lorsque je me suis lancé dans ce projet, j’ai pu constater que les sources d’informations sur le sujet étaient assez réduites. On trouve beaucoup de documentations pour crypter/décrypter des données en Java en générant la clé directement dans le code, mais très peu en utilisant une clé sur un support externe. 

Je voulais donc faire un retour d’expérience sur ce sujet, qui je pense, devrait intéresser d’autres personnes. Dans tous les cas, ça m’aura permit de bien poser mon problème et d’avoir trouvé une approche élégante qui me convienne. 

# 2. Mon objectif.

Depuis quelque temps, je me pose la question de crypter certaines données ([dites sensibles au sens RGPD](https://www.cnil.fr/fr/verifier-la-pertinence-des-donnees)) que je stocke dans les bases de données de mes applications. Le souci que j'avais jusqu'à  présent c'était : 

- ***Quel algorithme de cryptage utiliser ?***
- ***Comment et ou stocker mes clés de cryptages ?***

Je ne souhaite pas mettre en place une solution de gestion de secret trop compliquée comme [Hashicorp VAULT](https://www.vaultproject.io/), [AWS KMS](https://aws.amazon.com/kms/), … et surtout propriétaires. 

Ma contrainte était de pouvoir facilement changer de solution. 

J'ai donc opté pour stocker mes clés et autres éléments dans le service [AWS Paremeter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html).

> ℹ️ Si je venais à  changer de solution, j'aurais juste à  mettre à  jour mes méthodes d'accès à  ces secrets.

> Macro (drawio)

# 3. Présentation de la solution ECC (Elliptic Curve Cryptography) en Java.

Je ne vais ici décrire les solutions ECC. Si ça vous intéresse, je vous laisse consulter la documentation plus qu'abondante sur le net. J'ai mis quelques liens ci-dessous.

Je vais utiliser l'API de [Bouncy Castle](https://www.bouncycastle.org/java.html). Voici les dépendances maven  à  ajouter dans votre fichier pom.xml : 

```xml
<properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <log4j.version>2.20.0</log4j.version>
        <slf4j.version>2.0.9</slf4j.version>
        <aws.sdk.version>2.22.6</aws.sdk.version>
</properties>
<dependencies>        
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <version>${aws.sdk.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/software.amazon.awssdk/sts -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>sts</artifactId>
            <version>${aws.sdk.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/software.amazon.awssdk/regions -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>regions</artifactId>
            <version>${aws.sdk.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/software.amazon.awssdk/ssm -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>ssm</artifactId>
            <version>${aws.sdk.version}</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>route53</artifactId>
            <version>${aws.sdk.version}</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>route53domains</artifactId>
            <version>${aws.sdk.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on -->
        <dependency>
          <groupId>org.bouncycastle</groupId>
          <artifactId>bcprov-jdk18on</artifactId>
          <version>1.77</version>
        </dependency>
</dependencies> 
```

## 3.1. Création de la classe EC25519Key.

Cet objet va me permettre de centraliser l'ensemble des attributs et méthodes dont je vais avoir besoin pour mon application.

```java
package fr.kaptus.models;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.jce.interfaces.ECKey;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.jce.spec.IESParameterSpec;
import org.bouncycastle.math.ec.ECPoint;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
@Getter
@Setter
@Builder
public class EC25519Key implements ECKey {
    public static final String ECC_TYPE = "Curve25519";
    public static final String EC_ALGO = "EC";
    private static final String CIPHER_ALGO = "ECIESwithAES-CBC";
    private static final IESParameterSpec iesParamSpec = new IESParameterSpec(
            /* derivation = */ null,
            /* encoding = */ null,
            /* macKeySize = */ 256,
            /* cipherKeySize = */ 256,
            /* nonce = */ new byte[16],
            /* usePointCompression = */ false
    );
    private ECPrivateKey privateKey;
    private ECPublicKey publicKey;
    @Override
    public ECParameterSpec getParameters() {
        X9ECParameters curveParams = CustomNamedCurves.getByName(ECC_TYPE);
        return new ECParameterSpec(curveParams.getCurve(), curveParams.getG(), curveParams.getN(), curveParams.getH(), curveParams.getSeed());
    }
    /**
     * Return Base64 encrypted String
     * @param dataToEncrypt
     * @return
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @date 09 févr. 2024; 23:25
     * @Author Alain TOMASIAN (alain.tomasian@kaptus.fr)
     */
    public String encryptMessageBase64(String dataToEncrypt)
            throws InvalidAlgorithmParameterException,
            InvalidKeyException,
            IllegalBlockSizeException,
            BadPaddingException,
            NoSuchPaddingException,
            NoSuchAlgorithmException {
        Cipher encryptCipher = Cipher.getInstance(CIPHER_ALGO);
        encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey, iesParamSpec);
        byte[] secretMessageBytes = dataToEncrypt.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
        return Base64.getEncoder().encodeToString(encryptedMessageBytes);
    }
    /**
     * Return decrypted String from encrypted Base64 String
     * @param encodedMessage
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws InvalidAlgorithmParameterException
     * @date 09 févr. 2024; 23:26
     * @Author Alain TOMASIAN (alain.tomasian@kaptus.fr)
     */
    public String decryptMessageBase64(String encodedMessage)
            throws NoSuchPaddingException,
            NoSuchAlgorithmException,
            InvalidKeyException,
            IllegalBlockSizeException,
            BadPaddingException,
            InvalidAlgorithmParameterException {
        byte[] encryptedMessageBytes = Base64.getDecoder().decode(encodedMessage);
        Cipher decryptCipher = Cipher.getInstance(CIPHER_ALGO);
        decryptCipher.init(Cipher.DECRYPT_MODE, privateKey, iesParamSpec);
        byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
        return new String(decryptedMessageBytes, StandardCharsets.UTF_8);
    }
    /**
     * Get new ECC25519Key object instance
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     * @date 09 févr. 2024; 22:12
     * @Author Alain TOMASIAN (alain.tomasian@kaptus.fr)
     */
    public static EC25519Key getNewKey()
            throws NoSuchAlgorithmException,
            InvalidAlgorithmParameterException {
        Security.addProvider(new BouncyCastleProvider());
        X9ECParameters curveParams = CustomNamedCurves.getByName(ECC_TYPE);
        ECParameterSpec ecSpec = new ECParameterSpec(curveParams.getCurve(), curveParams.getG(), curveParams.getN(), curveParams.getH(), curveParams.getSeed());
        KeyPairGenerator kpg = KeyPairGenerator.getInstance(EC_ALGO, new BouncyCastleProvider());
        kpg.initialize(ecSpec);
        KeyPair keyPair = kpg.generateKeyPair();
        return EC25519Key.builder()
                .privateKey((ECPrivateKey) keyPair.getPrivate())
                .publicKey((ECPublicKey) keyPair.getPublic())
                .build();
    }
    /**
     * Retrieve ECPublicKey from ECPrivateKey
     *
     * @param privateKey
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @date 09 févr. 2024; 23:14
     * @Author Alain TOMASIAN (alain.tomasian@kaptus.fr)
     */
    public static ECPublicKey getPublicKey(ECPrivateKey privateKey)
            throws NoSuchAlgorithmException,
            InvalidKeySpecException {
        Security.addProvider(new BouncyCastleProvider());
        X9ECParameters curveParams = CustomNamedCurves.getByName(EC25519Key.ECC_TYPE);
        ECParameterSpec ecSpec = new ECParameterSpec(curveParams.getCurve(), curveParams.getG(), curveParams.getN(), curveParams.getH(), curveParams.getSeed());
        ECPoint Q = ecSpec.getG().multiply(privateKey.getD());
        ECPublicKeySpec pubSpec = new ECPublicKeySpec(Q, ecSpec);
        KeyFactory keyFactory = KeyFactory.getInstance(EC25519Key.EC_ALGO, new BouncyCastleProvider());
        return (ECPublicKey) keyFactory.generatePublic(pubSpec);
    }
    /**
     * Retrieve EC25519Key object from ECPrivateKey Base64 String encoded
     *
     * @param base64PrivateEncoded
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @date 09 févr. 2024; 23:10
     * @Author Alain TOMASIAN (alain.tomasian@kaptus.fr)
     */
    public static EC25519Key getInstance(String base64PrivateEncoded)
            throws NoSuchAlgorithmException,
            InvalidKeySpecException {
        Security.addProvider(new BouncyCastleProvider());
        byte[] privateKeyBytes = Base64.getDecoder().decode(base64PrivateEncoded);
        KeyFactory kf = KeyFactory.getInstance(EC25519Key.EC_ALGO, new BouncyCastleProvider());
        EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        ECPrivateKey privateKey = (ECPrivateKey) kf.generatePrivate(keySpec);
        ECPublicKey publicKey = getPublicKey(privateKey);
        return EC25519Key.builder()
                .privateKey(privateKey)
                .publicKey(publicKey)
                .build();
    }
}
```

> 📝 Bon je n'ai pas pondu cette classe d'un seul coup. Après un assez grand nombre de tests, d'essais et d'itérations, j'en suis arrivé à  la mise en place de ce modèle.

> ℹ️ Initialement, j'avais prévu de stocker dans mon ***AWS Parameter Store***, la clé public et la clé privée car je n'avais pas trouvé comment générer la clé public à  partir de la clé privé. J'ai naïvement pensé qu'il y avait une méthode dans la classe ***ECPrivateKey***, mais non.   
> ℹ️ Et finalement j'ai trouvé sur le net la solution. Ce qui va donc me simplifier la vie Ã°ÂÂÂ

> 📝 Je n'ai pas eu le réflexe ChatGPT tellement j'avais la tête dans le guidon. Mais en posant la question à  ChatGPT, voici sa réponse : 
> 📝 
> 📝 
> 📝 En essayant le code fourni, j'ai l'erreur :
> 📝 
> 📝 
> 📝 Du coup je précise à  ChatGPT que je suis en Java 21 et voici la réponse : 
> 📝 
> 📝   **Merci ChatGPT** Ã°ÂÂÂ
> 📝 
> 📝 Donc je ne regrette pas d'avoir chercher sur le net du coup.

## 3.2. Génération des clés EC.

Le moyen le plus simple pour déposer la clé générée dans  **“*****AWS Parameter Store*****”** est de l'encoder en [BASE64](https://fr.wikipedia.org/wiki/Base64). Voici le code pour la création des clés au format attendu :

```java
package fr.kaptus.security;

import fr.kaptus.models.EC25519Key;
import fr.kaptus.utilities.Sandbox;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import java.util.concurrent.TimeUnit;

public class Curve25519GeneratedKeys {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        Sandbox.start();

        EC25519Key ec25519Key = EC25519Key.getNewKey() ;
        ECPublicKey publicKey = ec25519Key.getPublicKey();
        ECPrivateKey privateKey =ec25519Key.getPrivateKey();
        System.out.println("public key = " + publicKey.toString());
        System.out.println("private key = " + privateKey.toString());
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        String base64PublicEncoded = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        System.out.println("base64PublicKeyEncoded = " + base64PublicEncoded);
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        String base64PrivateEncoded = Base64.getEncoder().encodeToString(privateKey.getEncoded());
        System.out.println("base64PrivateKeyEncoded = " + base64PrivateEncoded);
        System.out.println("--------------------------------------------------------------------------------------------------------------------");

        Sandbox.stop();
        System.out.println(Sandbox.getTimeMessage(TimeUnit.MILLISECONDS));
    }
}
```

Un exemple de génération de clés : 

![image-20240209-165925.png](media://dacc24af-6465-4ba2-9902-1b62bb4923c4)

Ce qui va nous intéresser c'est de conserver la chaine de caractères suivante : 

1. La clé privé :

## 3.3. Crypter/Décrypter des données.

Le code java pour encrypter/décrypter des données en utilisant les clés générées précédemment : 

```java
package fr.kaptus.security;

import fr.kaptus.models.EC25519Key;
import fr.kaptus.utilities.Sandbox;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.util.concurrent.TimeUnit;

public class Curve25519EncryptDecryptData {
    private static final String ENCRYPTED_DATA = "BCVILzHChmPMpWBte8sgTb8JCjn8qt6hBG0IpcjKvx7tYcn0fkXJeQV59YeZQ1mdt53Q83HLIUSac91xwLFGU1m8md+4vSZg+VvuD59OIq6dTaTDGHXNSUOx0Cy2TBHcBPCnaaLR+xQ1xqHqgcGJtnh2Jkog4J/5DEO0bGQvTZCcZwNPYdfjVuXWj6oi/OFENjH4fbjB20ZuVRJwtRyd14gLgpqrCeAeHP8ZgRQY+Wo3AyQhzZcSqpE50P41fMCqKzcd/9M=";
    private static final String PRIVATE_KEY_BASE64 = "MIICRwIBADCB6gYHKoZIzj0CATCB3gIBATArBgcqhkjOPQEBAiB/////////////////////////////////////////7TBEBCAqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" +
            "qqqqYSRShRAQge0Je0Je0Je0Je0Je0Je0Je0Je0Je0Je0JgtenHcQyGQEQQQqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0kWiCuGaG4oIa04B7dLHdI0UySPU1+bX" +
            "xhsinpxaJ+ztPZAiAQAAAAAAAAAAAAAAAAAAAAFN753qL3nNZYEmMaXPXT7QIBCASCAVMwggFPAgEBBCANJP7mmNlLS98zq8EeCCRnWR4j7767B6+gt1klpoZVgaCB4TCB3" +
            "gIBATArBgcqhkjOPQEBAiB/////////////////////////////////////////7TBEBCAqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqYSRShRAQge0Je0Je0Je0Je0Je" +
            "0Je0Je0Je0Je0Je0JgtenHcQyGQEQQQqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0kWiCuGaG4oIa04B7dLHdI0UySPU1+bXxhsinpxaJ+ztPZAiAQAAAAAAAAAAA" +
            "AAAAAAAAAFN753qL3nNZYEmMaXPXT7QIBCKFEA0IABHhz0qDGFWcncDwD0xkrboTPIi8UfY1XuxFqdqcoc4saGOkhAc/QkvNmjs4BGiMgdVqNsK9RD1l7T62l8qySo+E=";
    private static final String DATA_TO_ENCRYPT = "Le ciel est gris aujourd'hui sur Paris. Mais le gris de Paris est plus lumineux que le gris d'Amsterdam.";

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        Sandbox.start();
        
        EC25519Key ec25519Key = EC25519Key.getInstance(PRIVATE_KEY_BASE64) ;
        String encodedMessage = ec25519Key.encryptMessageBase64(DATA_TO_ENCRYPT);
        System.out.println("PRIVATE_KEY_BASE64 = " + PRIVATE_KEY_BASE64);
        System.out.println("public key recovered = " + ec25519Key.getPublicKey().toString());
        System.out.println("private key recovered = " + ec25519Key.getPrivateKey().toString());
        String decodedMessage = ec25519Key.decryptMessageBase64(encodedMessage);
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        System.out.println("encodedMessage = " + encodedMessage);
        System.out.println("decodedMessage = " + decodedMessage);
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        System.out.println("encodedMessage = " + ENCRYPTED_DATA);
        System.out.println("decodedRecoveredMessage = " + ec25519Key.decryptMessageBase64(ENCRYPTED_DATA));
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        encodedMessage = ec25519Key.encryptMessageBase64(DATA_TO_ENCRYPT);
        System.out.println("encodedMessage = " + encodedMessage);
        System.out.println("decodedRecoveredMessage = " + ec25519Key.decryptMessageBase64(encodedMessage));
        System.out.println("--------------------------------------------------------------------------------------------------------------------");

        Sandbox.stop();
        System.out.println(Sandbox.getTimeMessage(TimeUnit.MILLISECONDS));
    }
}
```

Le résultat : 

![image-20240210-114307.png](media://642f850d-cfa5-4fd3-9e99-01dd7a70a091)

> 📝 L'attribut `ENCRYPTED_DATA` contient la donnée qui a déjà  été encrypter. Cela permet de valider qu'on peut décrypter une données encryptée lors d'une  précédente exécution.

## 3.4. Stockage de la clé privé dans AWS Parameter Store.

Dépôt de la clé privée : 

![image-20240209-174943.png](media://9b0d4605-8da4-49de-ade5-60d69f523a0f)

## 3.5. Récupération de la clé privé dans AWS Parameter Store et cryptage/décryptage des données.

Voici le code Java :

```java
package fr.kaptus.security;

import fr.kaptus.aws.properties.AwsECProperties;
import fr.kaptus.models.EC25519Key;
import fr.kaptus.utilities.Sandbox;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.util.concurrent.TimeUnit;

public class Curve25519AwsParameterStore {
    private static final String ENCRYPTED_DATA = "BB809S8WuQ7eDlPVHkZOBesIkiDvWeZYb+Jh6azUVmRxaKi3VRCSs8+ZbPg3see+O5c+euu0Rr+o8l4lJbEr7wOewGCWgu9sqZBC80OJxOQZDBLTKZ3POLIVl8qFmjJWwr6UnBvsoV9PvgMP9WH2/XlUzrEYEXNwmmmKyulEy93FgRcxAr8Y7G2FTlIhkWTS4JlmPYCq8rFzWEi105/9gqVeuAcdLh/QGhVE5EAotI47TBfq16Kv+dTzmY3rJ8ZTC849OZQ=";
    private static final String  DECRYPTED_DATA = "Le ciel est gris aujourd'hui sur Paris. Mais le gris de Paris est plus lumineux que le gris d'Amsterdam.";

    public static void main(String[] args)
            throws InvalidKeySpecException,
            NoSuchAlgorithmException,
            InvalidAlgorithmParameterException,
            NoSuchPaddingException,
            IllegalBlockSizeException,
            BadPaddingException,
            InvalidKeyException {
        Security.addProvider(new BouncyCastleProvider());
        Sandbox.start();
        String base64PrivateEncoded = AwsECProperties.getBase64PrivateKey();
        EC25519Key ec25519Key = EC25519Key.getInstance(base64PrivateEncoded) ;
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        System.out.println("Parameter store value Private Key= " + base64PrivateEncoded);
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        String encodedMessage = ec25519Key.encryptMessageBase64(DECRYPTED_DATA);
        System.out.println("encodedMessage = " + encodedMessage);
        System.out.println("decodedMessage = " + ec25519Key.decryptMessageBase64(encodedMessage));
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        String decodedMessage = ec25519Key.decryptMessageBase64(ENCRYPTED_DATA);
        System.out.println("decodedRecoveredMessage = " + decodedMessage);
        System.out.println("So good ? " + DECRYPTED_DATA.equals(decodedMessage));
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        Sandbox.stop();
        System.out.println(Sandbox.getTimeMessage(TimeUnit.MILLISECONDS));
    }
}
```

Le résultat : 

![image-20240210-121609.png](media://6dac1bde-6158-4c98-b6a9-b6ff18244598)

Le code de la classe qui me permet de récupérer la valeur encodé en BASE64 de la clé dans le Parameter Store : 

```java
package fr.kaptus.aws.properties;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ssm.SsmClient;
import software.amazon.awssdk.services.ssm.model.GetParameterRequest;
import software.amazon.awssdk.services.ssm.model.GetParameterResponse;
import software.amazon.awssdk.services.ssm.model.SsmException;

public class AwsECProperties {
    private static final Class CLASS = AwsECProperties.class;
    private static final String AWS_DEFAULT_REGION = "eu-west-3";
    private static final String WEBMINUT_ECC_PRIVATE_KEY = "/webminut/test/keys/ecc/private";
    private static final Logger LOGGER = LogManager.getLogger(CLASS);
    protected AwsECProperties() {
        //throw new IllegalStateException("Utility class");
    }
    protected static SsmClient getSsmClient()
            throws SdkClientException {
        return SsmClient.builder()
                .region(getRegion())
                .credentialsProvider(AwsECProperties.getCredentialsProvider())
                .build();
    }
    public static AwsCredentialsProvider getCredentialsProvider()
            throws SdkClientException {
        return ProfileCredentialsProvider.create();
    }
    public static Region getRegion() {
        return Region.of(StringUtils.defaultIfBlank(System.getenv("AWS_REGION"), AWS_DEFAULT_REGION));
    }
    private static String getParaValue(String paraName)
            throws SdkClientException {
        String returnValue = null;
        SsmClient ssmClient = getSsmClient();
        try {
            GetParameterRequest parameterRequest = GetParameterRequest.builder()
                    .name(paraName)
                    .withDecryption(true)
                    .build();
            GetParameterResponse parameterResponse = ssmClient.getParameter(parameterRequest);
            returnValue = parameterResponse.parameter().value();
        } catch (SsmException e) {
            System.out.println(e.getMessage());
        }
        ssmClient.close();
        return returnValue;
    }
    public static String getBase64PrivateKey() {
        try {
            return getParaValue(WEBMINUT_ECC_PRIVATE_KEY);
        } catch (SdkClientException e) {
            LOGGER.debug(e.getMessage(), e);
            return null;
        }
    }
}
```

> ⚠️ Pour que cela fonctionne, il faut bien sur que votre compte AWS soit configuré en local (fichier ***.aws/config*** et ***.aws/credential***)

# 4. Côté performance.

Il est nécessaire de s’intéresser à la performance de la solution car dans mon application, je risque de faire quelques A/R avec les données stocker dans la base. 

| **Number of iterations** | **Encryption in ms** | **Decryption in ms** |
| --- | --- | --- |
| 10 | 61 | 25 |
| 100 | 243 | 154 |
| 1000 | 1305 | 793 |
| 10000 | 8251 | 6755 |
| 100000 | 79454 | 70778 |

![image-20240211-104605.png](media://f383de90-027a-43c9-8941-3332c1047310)


> 📝 Les tests ont été effectués sur :

Le code source Java : 

```java
package fr.kaptus.security;

import fr.kaptus.aws.properties.AwsECProperties;
import fr.kaptus.models.EC25519Key;
import fr.kaptus.utilities.Sandbox;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

public class Curve25519AwsParameterStorePerformance {

    private static final int ITERATION = 100000;
    private static final int SIZE = 320;
    private static final ArrayList<String> STRING_BACKUPED = new ArrayList<String>(ITERATION);
    private static final ArrayList<String> STRING_RECOVERED = new ArrayList<String>(ITERATION);

    public static void main(String[] args)
            throws InvalidKeySpecException,
            NoSuchAlgorithmException,
            InvalidAlgorithmParameterException,
            NoSuchPaddingException,
            IllegalBlockSizeException,
            BadPaddingException,
            InvalidKeyException {
        Security.addProvider(new BouncyCastleProvider());
        Sandbox.start();
        String base64PrivateEncoded = AwsECProperties.getBase64PrivateKey();
        EC25519Key ec25519Key = EC25519Key.getInstance(base64PrivateEncoded);
        Sandbox.stop();
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        System.out.println("Init exec time = " + Sandbox.getTime(TimeUnit.MILLISECONDS) + " in ms");
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        Sandbox.reset();
        Sandbox.start();
        System.out.println("Start loop of " + ITERATION + " and encrypted random int with length = " + SIZE);
        for (int i = 0; i < ITERATION; i++) {
            String encryptMessageBase64 = ec25519Key.encryptMessageBase64(Sandbox.getRandomNumber(SIZE));
            STRING_BACKUPED.add(encryptMessageBase64);
        }
        Sandbox.stop();
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        System.out.println("Encrypted exec time = " + Sandbox.getTime(TimeUnit.MILLISECONDS) + " in ms");
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        Sandbox.reset();
        Sandbox.start();
        System.out.println("Start loop of " + ITERATION + " and decrypted random int with length = " + SIZE);
        for (int i = 0; i < ITERATION; i++) {
            STRING_RECOVERED.add(ec25519Key.decryptMessageBase64(STRING_BACKUPED.get(i)));
        }
        Sandbox.stop();
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
        System.out.println("Decrypted exec time = " + Sandbox.getTime(TimeUnit.MILLISECONDS) + " in ms");
        System.out.println("--------------------------------------------------------------------------------------------------------------------");
    }
}
```

# 5. En conclusion.

Et voilà la travail. Même si cette solution n’est pas parfaite, elle répond à mes attentes en terme de performance et de sécurité.

> ⚠️ *Aucune sécurité n’est infaillible. Tôt au tard, il y aura quelqu’un de plus malin qui arrivera à percer vos défenses. L’objectif est d’essayer de rendre les choses le plus compliquées possible et d’essayer d’anticiper les éventuelles failles.*

**Quelques point d’attentions : **

- **Crypter des données prend plus de place dans la base de données**. Par exemple pour une adresse électronique de **32 caractères**, avec l’algorithme que nous venons de voir, le résultat crypté, et en BASE64 occupera **180 caractères**.
- Dans le schéma de votre base de donnée, **vous ne pouvez plus poser d’index sur la colonne qui a été encryptée**. Il va falloir trouver un solution de substitution pour indexer la colonne si cela est vraiment nécessaire.
- **La clé est stockée en clair** (***BASE64***) dans l'***AWS Parameter Store***. Donc rien ne pourrait empêcher ***Amazon*** d’avoir accès à vos données (*même si les données que je vais stocker n’ont pas vraiment d’intérêt pour eux* 🙂 )

> 📝 Pour plus de sécurité, j’envisage de crypter la clé que je déposerai sur le ***AWS Parameter Store***, et la clé pour décrypter cette clé sera embarqué dans mon application. Ça permettra d’ajouter une sécurité supplémentaire.

- **Penser à stocker la clé dans un endroit sûr,** car on n’est pas à l’abri d’un problème sur l’infrastructure ***AWS*** (*même si le risque est vraiment minime* 🧐 ) et ***ça serait dommage que vous ne puissiez plus accéder à vos données*** 😭
- Mettre en place **une stratégie de subsitution de la clé de cryptage** (*en cas de compromission*)
  - Ce qui va impliquer de décrypter et de re-encrypter l’ensemble des données (*cela risque de peut prendre un certain temps suivant le volume de données à traiter*).

# 6. Liens utiles.

| **Date** | **Lien** | **Commentaire** |
| --- | --- | --- |
| 8/29/2017 | [https://gist.github.com/Eng-Fouad/0241c3bf77f25239b26ba11010bd2407](https://gist.github.com/Eng-Fouad/0241c3bf77f25239b26ba11010bd2407) | Un exemple de code pour générer des clés EC et les encoder en Base64. |
| 2/12/2020 | [https://stackoverflow.com/questions/60197887/constructing-ec-public-key-from-ec-point-and-ecparameterspec-gives-invalid-x-val](https://stackoverflow.com/questions/60197887/constructing-ec-public-key-from-ec-point-and-ecparameterspec-gives-invalid-x-val) | Méthode pour générer la clé public EC à  partir d'une clé privée |
| 7/15/2020 | [https://stackoverflow.com/questions/27112213/ecc-encryption-and-decryption-in-java](https://stackoverflow.com/questions/27112213/ecc-encryption-and-decryption-in-java) | Crypter une chaine de caractères avec une clé ***ECC*** (***Elliptic Curve Cryptography***) |
| 11/20/2020 | [https://lff0305.medium.com/load-ec-public-key-private-key-from-string-bfd9420f667a](https://lff0305.medium.com/load-ec-public-key-private-key-from-string-bfd9420f667a) | Comment récupérer la clé privée et la clé public depuis une chaine de caractères en base64. |
| 5/19/2021 | [https://culturemath.ens.fr/thematiques/lycee/cryptographie-asymetrique-et-courbes-elliptiques](https://culturemath.ens.fr/thematiques/lycee/cryptographie-asymetrique-et-courbes-elliptiques) | Excellent cours de mathématiques sur l'utilisation des courbes elliptiques en cryptographies. |
| 10/17/2022 | [https://france.devoteam.com/paroles-dexperts/comment-reussir-sa-gestion-de-cles-et-secrets-avec-les-solutions-de-kms-et-vault/](https://france.devoteam.com/paroles-dexperts/comment-reussir-sa-gestion-de-cles-et-secrets-avec-les-solutions-de-kms-et-vault/) | Un excellent article qui compare la gestion des secrets avec Vault et KMS. |
| 1/1/2023 | [https://docs.oracle.com/en/java/javase/21/docs/specs/security/standard-names.html](https://docs.oracle.com/en/java/javase/21/docs/specs/security/standard-names.html) | Liste complète des algorithmes de cryptages utilisés en JAVA 21. |
| 5/31/2023 | [https://www.linkedin.com/pulse/encryption-algorithms-choosing-right-one-debra](https://www.linkedin.com/pulse/encryption-algorithms-choosing-right-one-debra) | Description des différents algorithmes de cryptographies. |
| 11/9/2023 | [https://www.damyr.fr/posts/sops/](https://www.damyr.fr/posts/sops/) | Un article vraiment complet sur la mise en place de SOPS pour gérer les secrets directement dans les fichiers de configuration ou les scripts. Excellent.<br>> 📝 Méthode que j'ai utilisé lors d'une mission avec une clé ***AWS KMS***. La mise en place était un peu trapue et la gestion des fichiers à  crypter/décrypter n'étaient pas vraiment triviale.  
> 📝 Mais je n'ai peut-être pas utiliser les bonnes méthodes aussi Ã°ÂÂÂ |
| 2/7/2024 | [https://fr.wikipedia.org/wiki/Cryptographie_sur_les_courbes_elliptiques](https://fr.wikipedia.org/wiki/Cryptographie_sur_les_courbes_elliptiques) | Description de la cryptographie par courbes elliptique sur Wikipédia. |