Approche technique : Le serveur génère dynamiquement une paire de clés RSA pour chaque tentative de connexion. La clé publique est transmise au client tandis que la clé privée est temporairement conservée. Le client chiffre le mot de passe avec la clé publique avant l'envoi des informations d'identification. Le serveur déchiffre le mot de passe, vérifie sa validité et sa conformité aux politiques de sécurité.
Génération de la clé publique RSA (Backend)
/// <summary>
/// Génère et retourne une clé publique RSA pour un utilisateur spécifique
/// </summary>
/// <param name="utilisateur">Nom d'utilisateur</param>
/// <returns>Clé publique au format PEM</returns>
[HttpGet]
[AllowAnonymous]
public Reponse<string> GenererCleRsaPublique(string utilisateur)
{
var fournisseurRsa = new RSACryptoServiceProvider(2048);
var clePrivee = fournisseurRsa.ToXmlString(true);
CacheSystem.Ajouter(utilisateur, clePrivee, 10);
var clePublique = fournisseurRsa.ToXmlString(false);
clePublique = ConvertisseurRsa.PublicXmlVersPem(clePublique);
return new Reponse<string>()
{
Code = 1,
Resultat = clePublique
};
}
Chiffrement RSA (Frontend)
import JSEncrypt from 'jsencrypt'
export const serviceRsa = {
chiffrer: function(texteClair, clePublique) {
const encrypteur = new JSEncrypt();
encrypteur.setPublicKey(clePublique);
var texteChiffre = encrypteur.encrypt(texteClair);
if(texteChiffre)
return texteChiffre;
else
alert("Le chiffrement a échoué")
},
dechiffrer: function(texteChiffre, clePrivee){
const decrypteur = new JSEncrypt();
decrypteur.setPrivateKey(clePrivee);
return decrypteur.decrypt(texteChiffre);
}
};
this.$api.obtenirClePublique({ utilisateur: this.param.utilisateur }).then(reponse => {
donnees.motDePasse = serviceRsa.chiffrer(donnees.motDePasse, reponse.Resultat);
});
Validation de connexion (Backend)
[HttpPost]
[AllowAnonymous]
public Reponse<ResultatConnexion>> Connexion([FromBody]ModeleConnexion modele)
{
string motDePasseReel = string.Empty;
try
{
var fournisseurRsa = new System.Security.Cryptography.RSACryptoServiceProvider();
var clePrivee = cache.Obtenir(modele.utilisateur);
if(clePrivee == null)
{
reponse.Code = CodesStatut.CONNEXIONECOUCEE;
reponse.Message = "Session expirée, veuillez vous reconnecter!";
reponse.Resultat = new ResultatConnexion { Succes = false };
return reponse;
}
fournisseurRsa.FromXmlString(clePrivee.ToString());
var motDePasseChiffre = fournisseurRsa.Decrypt(Convert.FromBase64String(modele.motDePasse), false);
motDePasseReel = System.Text.Encoding.UTF8.GetString(motDePasseChiffre);
modele.motDePasse = motDePasseReel;
cache.Supprimer(modele.utilisateur);
}
catch
{
reponse.Code = CodesStatut.CONNEXIONECOUCEE;
reponse.Message = "Accès client non autorisé";
reponse.Resultat = new ResultatConnexion { Succes = false };
return reponse;
}
var motDePasseHash = OutilsMD5.CalculerMd5(modele.motDePasse);
// Vérification de la complexité du mot de passe
var regexComplexite = new System.Text.RegularExpressions.Regex(@"^.*(?=.{8,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*]).*$");
if (!regexComplexite.IsMatch(motDePasseReel))
{
reponse.Code = CodesStatut.FAIBLE_COMPLEXITE;
reponse.Message = "Le mot de passe ne respecte pas les exigences de sécurité!";
return reponse;
}
// Vérification de l'expiration du mot de passe
if (DateTime.Today.Subtract(dateMaximale).TotalDays > 90)
{
reponse.Code = CodesStatut.MOTDEPASSE_EXPIRE;
reponse.Message = "Le mot de passe a expiré, veuillez le mettre à jour!";
return reponse;
}
if (DateTime.Today.Subtract(dateMaximale).TotalDays > 80)
{
reponse.Message = "Connexion réussie, le mot de passe va bientôt expirer. Merci de le mettre à jour!";
return reponse;
}
}
Le système garantit que les nouveaux mots de passe diffèrent de l'ancien et des trois précédents, assurant ainsi une rotation sécurisée des mots de passe.