PBToolboxAI v3 ← Site

crypto — n_pbt_crypto #

← Référence des composants · Sommaire du guide

Empreintes, HMAC, chiffrement par mot de passe, valeurs aléatoires et signatures RSA — la Web Crypto API du moteur WebView2, que votre PowerBuilder 10 n'a nulle part ailleurs. Aucune DLL tierce, aucun service : ce que le poste sait déjà faire, offert à PowerScript.

▶ Le voir en vrai — Application de démonstration, tuile Crypto : les résultats, le code qui les produit et cette page, côte à côte.


En bref #

Objet non visueln_pbt_crypto
Sert àVérifier qu'un fichier n'a pas changé, garder un secret dans un INI, signer une commande, authentifier un appel d'API
PrincipeUne page cachée fait le calcul ; chaque méthode est un appel PowerScript ordinaire qui rend sa valeur
DépendanceLe runtime WebView2, déjà requis par la bibliothèque — rien d'autre

Démarrage rapide #

n_pbt_crypto lnv_crypto
string ls_hash, ls_cipher

lnv_crypto = create n_pbt_crypto

// The fingerprint of a text, in hexadecimal
ls_hash = lnv_crypto.of_sha256("Invoice 4152, 690.00 EUR")

// A secret kept with a password : one string to store, the password to keep
ls_cipher = lnv_crypto.of_encrypt("s3cret", "The safe code is 4152.")
MessageBox("Back", lnv_crypto.of_decrypt("s3cret", ls_cipher))

destroy lnv_crypto

Chaque méthode est un appel qui rend sa valeur : pas d'événement, pas d'attente à écrire.


Les formats, lisibles par les autres outils #


Propriétés #

PropriétéTypeDéfautRôle
is_last_errorstring""Pourquoi le dernier appel a rendu une chaîne vide, false ou un code négatif : mauvais mot de passe, fichier illisible, algorithme inconnu, limite démo
il_timeout_mslong20000Durée maximale d'un appel. Hacher un gros fichier ou générer une clé de 4096 bits peut prendre quelques secondes
il_pbkdf2_roundslong100000Tours PBKDF2 d'of_encrypt, of_password_hash et of_encrypt_file : environ 100 ms. Plus est plus lent pour tout le monde, l'attaquant compris

Constantes : HASH_MD5, HASH_SHA1, HASH_SHA256, HASH_SHA384, HASH_SHA512 pour l'algorithme ; KEY_2048, KEY_3072, KEY_4096 pour la taille d'une clé RSA, KEY_RSA_2048, KEY_EC_P256, KEY_ED25519 pour la sorte d'une paire ; JWT_HS256, JWT_RS256, JWT_ES256, JWT_EDDSA ; CHARSET_ALL, CHARSET_ALPHANUM, CHARSET_LETTERS, CHARSET_DIGITS, CHARSET_HEX.


Méthodes #

MéthodeRôle
of_open ( ) → longCrée la page cachée. Facultatif — chaque méthode le fait — mais l'appeler à l'ouverture de la fenêtre paie le coût une fois. Rend le handle (> 0) ou un code négatif
of_is_open ( ) → booleanVrai dès que la page cachée existe
of_hash (string as_algorithm, string as_text) → stringL'empreinte d'un texte, en hexadécimal ; HASH_* pour l'algorithme. Vide en cas d'échec
of_sha256 (string as_text) → stringof_hash avec HASH_SHA256 — celui qu'on utilise le plus
of_hmac (string as_algorithm, string as_key, string as_text) → stringLe HMAC d'un texte sous une clé secrète, en hexadécimal — ce qu'une API demande pour authentifier un appel
of_hash_file (string as_algorithm, string as_path) → stringL'empreinte d'un fichier du poste, de n'importe quel type et jusqu'à 512 Mo, sans le charger dans PowerBuilder
of_base64_encode (string as_text) → stringUn texte en base64 (ses octets UTF-8) : ce qu'un navigateur ou Python écrirait. Vide en cas d'échec
of_base64_decode (string as_base64) → stringLe texte de retour ; une valeur qui n'est pas du base64 rend une chaîne vide et is_last_error, jamais du bruit
of_base64_encode_file (string as_path) → stringLes octets d'un fichier du poste en base64 — une pièce jointe, une image pour un appel JSON — jusqu'à 512 Mo, lus par la DLL, jamais chargés dans PowerBuilder
of_base64_decode_to_file (string as_base64, string as_path) → longÉcrit les octets d'une valeur base64 dans un fichier — la pièce jointe qu'une API a répondue. Rend 0 une fois écrit, -5 sur un chemin vide ou une valeur qui n'est pas du base64, -4 si le fichier ne peut pas être écrit
of_encrypt (string as_password, string as_text) → stringChiffre un texte avec un mot de passe : une seule chaîne base64 à stocker. Vide en cas d'échec
of_decrypt (string as_password, string as_cipher) → stringLe texte de retour, avec le même mot de passe. Mauvais mot de passe ou valeur altérée : chaîne vide et is_last_error, jamais du bruit
of_uuid ( ) → stringUn UUID aléatoire (version 4)
of_random_hex (integer ai_bytes) → stringai_bytes octets aléatoires (1 à 4096) en hexadécimal : un sel, un jeton
of_generate_keypair (integer ai_bits, ref string as_public_pem, ref string as_private_pem) → longUne paire RSA en PEM (KEY_*). Rend 0 une fois les deux clés remplies, -4 en cas d'échec
of_sign (string as_private_pem, string as_text) → stringLa signature d'un texte par la clé privée, en base64. Vide en cas d'échec
of_verify (string as_public_pem, string as_text, string as_signature) → booleanVrai si la signature a été faite sur exactement ce texte par la clé privée correspondante ; faux — pas une erreur — si le texte a changé
of_generate_keypair (string as_kind, ref string as_public_pem, ref string as_private_pem) → long`Une paire par SORTE : KEY_RSA_2048/3072/4096, KEY_EC_P256 (signatures courtes) ou KEY_ED25519. Rend 0, -4 en cas d'échec
of_key_kind (string as_pem) → string`La famille d'une clé PEM : rsa, ec-p256 ou ed25519
of_base64url_encode (string as_text) → string`base64 sûr pour une URL (JWT, chaîne de requête)
of_base64url_decode (string as_base64url) → string`Le texte de retour ; vide si ce n'est pas du base64url
of_hex_encode (string as_text) → string`Les octets UTF-8 d'un texte en hexadécimal
of_hex_decode (string as_hex) → string`Le texte de retour ; vide si ce n'est pas de l'hexadécimal
of_random_password (integer ai_length, string as_charset) → string`Un mot de passe aléatoire (4 à 256 caractères) sur un jeu CHARSET_*, sans biais. Vide sur une longueur fautive
of_equals_constant_time (string as_a, string as_b) → boolean`Égalité en temps constant : comparer un jeton ou un code sans le trahir
of_password_hash (string as_password) → string`Ce qu'on STOCKE pour un mot de passe : PBKDF2 salé (il_pbkdf2_rounds), tours et sel dans la valeur. Jamais le même résultat deux fois
of_password_verify (string as_password, string as_stored) → boolean`Vrai si le mot de passe est celui de la valeur stockée, comparé en temps constant
of_totp_secret ( ) → string`Un secret neuf pour les codes à deux facteurs (RFC 6238), en base32
of_totp_code (string as_secret) → string`Le code à six chiffres de l'instant, celui que montre l'authentificateur
of_totp_verify (string as_secret, string as_code) → boolean`Vrai si le code est celui de l'instant, du pas précédent ou du suivant
of_totp_uri (string as_secret, string as_account, string as_issuer) → string`L'URI otpauth:// à mettre dans un QR code pour enrôler l'utilisateur
of_generate_key ( ) → string`Une clé AES-256 neuve, 32 octets en hexadécimal
of_encrypt_with_key (string as_key, string as_text) → string`AES-256-GCM avec une clé EXPLICITE (hex ou base64) : base64 de nonce + chiffré + tag, ce qu'openssl ou Python déchiffrent
of_decrypt_with_key (string as_key, string as_cipher) → string`Le texte de retour avec la même clé ; vide et is_last_error sinon
of_rsa_encrypt (string as_public_pem, string as_text) → string`Un secret court chiffré vers une clé PUBLIQUE (RSA-OAEP) : seule la privée le lit
of_rsa_decrypt (string as_private_pem, string as_cipher) → string`Le secret de retour, avec la clé privée
of_jwt_sign (string as_algorithm, string as_key, string as_claims_json, long al_expires_seconds) → string`Un JWT signé : JWT_HS256 (secret partagé), JWT_RS256/JWT_ES256/JWT_EDDSA (clé privée) ; iat ajouté, exp si la durée est > 0. Vide en cas d'échec
of_jwt_verify (string as_algorithm, string as_key, string as_token) → string`Les claims (JSON) d'un jeton dont la signature, l'algorithme et les dates sont bons ; vide et is_last_error sinon
of_jwt_claims (string as_token) → string`Les claims SANS vérification, pour lire qui le jeton nomme avant de choisir la clé
of_protect (string as_text { , boolean ab_machine }) → string`Un secret protégé par Windows pour cet utilisateur (ou cette machine) : DPAPI, natif. Ce qu'on range dans l'INI pour un mot de passe de serveur
of_unprotect (string as_protected) → string`Le texte de retour, sur le même compte ; vide et is_last_error ailleurs
of_crc32 (string as_text) → string`Le CRC32 d'un texte, 8 chiffres hexadécimaux — un contrôle d'intégrité, pas une empreinte
of_crc32_file (string as_path) → string`Le CRC32 d'un fichier, lu par la DLL
of_encrypt_file (string as_password, string as_source, string as_target) → long`Un fichier chiffré par mot de passe, en natif (même conteneur qu'of_encrypt). Rend 0, -5 argument vide, -2 source illisible, -4 cible inscriptible
of_decrypt_file (string as_password, string as_source, string as_target) → long`Le fichier de retour. Rend 0, -3 mauvais mot de passe ou fichier altéré (rien n'est écrit), -5/-2/-4 comme of_encrypt_file
of_close ( )Libère la page cachée ; fait pour vous à la destruction de l'objet
of_reset ( )Retour aux valeurs par défaut

Exemples #

Vérifier qu'un fichier n'a pas changé #

string ls_expected, ls_actual
ls_expected = ProfileString("deploy.ini", "files", "orders.pbd", "")
ls_actual = lnv_crypto.of_hash_file(n_pbt_crypto.HASH_SHA256, "orders.pbd")
if ls_actual <> ls_expected then MessageBox("Deploy", "orders.pbd is not the file that was tested.")

Une pièce jointe en base64 pour une API #

// Sending : the file's bytes in the JSON body
lnv_j.of_set_string("filename", "invoice_4152.pdf")
lnv_j.of_set_string("content", lnv_crypto.of_base64_encode_file("C:\invoices\4152.pdf"))
lnv_rest.of_post("https://api.example.com/documents", lnv_j.of_text())

// Receiving : the answer's attachment, back on disk
lnv_crypto.of_base64_decode_to_file(lnv_rest.of_json_value("content"), "C:\inbox\receipt.pdf")

Garder un secret dans un INI #

// At setup : encrypt once, store the string
SetProfileString("app.ini", "db", "password", lnv_crypto.of_encrypt(ls_master, ls_db_password))

// At run time : the master password comes from the user, the INI gives the rest
ls_db_password = lnv_crypto.of_decrypt(ls_master, ProfileString("app.ini", "db", "password", ""))
if ls_db_password = "" then MessageBox("Login", lnv_crypto.is_last_error)

Signer une commande, la vérifier ailleurs #

string ls_public, ls_private, ls_signature
lnv_crypto.of_generate_keypair(n_pbt_crypto.KEY_2048, ls_public, ls_private)   // once ; keep ls_private
ls_signature = lnv_crypto.of_sign(ls_private, ls_order_json)
// The public key and the signature travel with the order ; anyone can check :
if not lnv_crypto.of_verify(ls_public, ls_order_json, ls_signature) then MessageBox("Order", "This order was altered.")

Un jeton pour une API, et sa vérification #

// The client : a token valid one hour, in the Authorization header
ls_token = lnv_crypto.of_jwt_sign(n_pbt_crypto.JWT_HS256, ls_api_secret, '{"sub":"guillaume","role":"admin"}', 3600)
lnv_rest.of_set_bearer(ls_token)

// The server side (or a check of what came back) : the claims, once the signature and the expiry are right
ls_claims = lnv_crypto.of_jwt_verify(n_pbt_crypto.JWT_HS256, ls_api_secret, ls_token)
if ls_claims = "" then MessageBox("API", lnv_crypto.is_last_error)
ls_role = gnv_utils.of_json_get_str(ls_claims, "role")

Un mot de passe de serveur rangé par Windows, et un utilisateur à deux facteurs #

// At setup : the INI holds a value Windows protects for this account, never the password
SetProfileString("app.ini", "db", "password", lnv_crypto.of_protect(ls_db_password))
// At run time
ls_db_password = lnv_crypto.of_unprotect(ProfileString("app.ini", "db", "password", ""))

// Enrolling a user : a secret in the database, the QR on screen
ls_secret = lnv_crypto.of_totp_secret()
uo_qr.is_data = lnv_crypto.of_totp_uri(ls_secret, ls_login, "MyApp")
// Logging in : the stored hash, then the six digits
if lnv_crypto.of_password_verify(ls_typed, ls_stored_hash) and lnv_crypto.of_totp_verify(ls_secret, ls_six_digits) then ...

Bonnes pratiques #

← Référence des composants · Sommaire du guide