J'ai récemment été confronté à une problématique sur l'un de mes projets d'application lourde. Celle-ci, développée en ElectronJS et avec un backend en NestJS, devait contenir des fonctionnalités gratuites et payantes. Et afin d'identifier et permettre à un client d'accéder à ces fonctionnalités payantes, je devais alors créer un système de licence applicative.
Prérequis
Pour créer un système basique de génération de licence applicative, vous aurez besoin de la librairie npm crypto
et d'un service NestJS a part dans lequel nous allons centraliser tout notre générateur et ses fonctions annexes.
Générer une paire de clé RSA pour chiffrer votre licence
Notre licence se base sur un chiffrement d'informations liées à la licence grâce à un couple de clé RSA. Nous devons donc dans un premier temps générer une paire de clé RSA si aucune existe actuellement :
async generateKeyPairRSA(): Promise<void> {
const { privateKey, publicKey } = await generateKeyPairSync('rsa', {
modulusLength: 512,
privateKeyEncoding: { type: 'pkcs1', format: 'pem' },
publicKeyEncoding: { type: 'pkcs1', format: 'pem' },
})
await writeFileSync('./rsa_licence', privateKey)
await writeFileSync('./rsa_licence.pub', publicKey)
}
Vous aurez également besoin de deux méthodes afin de travailler avec ces clés :
- Une méthode permettant de récupérer la clé privé RSA : cette méthode sera uniquement disponible pour votre backend, elle ne doit en aucun cas être exposée.
async getPrivateKey(): Promise<string> {
let private_key
let exist = await existsSync("./rsa_licence")
if(!exist) {
await this.generateKeyPairRSA()
}
private_key = await readFileSync("./rsa_licence", {encoding: 'utf-8'})
return private_key
}
- La seconde méthode permet de récupérer la clé publique RSA, permettant de déchiffrer la licence et ainsi récupérer ses informations : Cette méthode peut être exposé sur un endpoint API afin que votre client puisse récupérer cette clé publique.
async getPublicKey(): Promise<string> {
let public_key
let exist = await existsSync("./rsa_licence.pub")
if(!exist) {
await this.generateKeyPairRSA()
}
public_key = await readFileSync("./rsa_licence.pub", {encoding: 'utf-8'})
return public_key
}
Chiffrer votre licence applicative avec NestJS et Crypto
Une fois notre paire de clé RSA générée, il ne vous reste plus qu'à générer une licence à partir d'un objet json contenant les informations de votre licence (ID du client associé à la licence, quota d'appareils, date de validité etc.)
async generateLicenceKey(licence_info: any, private_key: string): Promise<string> {
const signer = createSign('rsa-sha256')
signer.update(licence_info)
const encoded = Buffer.from(licence_info).toString('hex')
const signature = signer.sign(private_key, 'hex')
const licenseKey = `${encoded}.${signature}`
return licenseKey
}
Une fois votre licence générée, vous pouvez l'enregistrer en BDD, associé à l'utilisateur auquel elle appartient.
Déchiffrer votre licence applicative
Côté client, si vous souhaitez déchiffrer votre licence afin de vérifier les informations de celle-ci (licence encore valide ? Bon utilisateur associé à cette licence ? etc.), voici la méthode :
async decryptLicence(licence: string, public_key: string): Promise<any> {
const [encoded, signature] = licence.split('.')
const data = Buffer.from(encoded, 'hex').toString()
// Create an RSA verifier
const verifier = createVerify('rsa-sha256')
verifier.update(data)
// Verify the signature for the data using the public key
const valid = verifier.verify(public_key, signature, 'hex')
return {valid, data}
}
Conclusion
Ce petit service vous permet donc de générer des licences sécurisées et simples par le biais des clé RSA. A vous d'améliorer ce service afin de le rendre plus sécurisé si nécessaire et/ou de le complexifié.