crypto — n_pbt_crypto #
← Riferimento dei componenti · Sommario della guida
Hash, HMAC, cifratura con password, valori casuali e firme RSA — la Web Crypto API del motore WebView2, che il tuo PowerBuilder 10 non ha da nessun'altra parte. Nessuna DLL di terzi, nessun servizio: ciò che la postazione sa già fare, offerto a PowerScript.
▶ Vederlo dal vivo — Applicazione dimostrativa, riquadro Crypto: i risultati, il codice che li produce e questa pagina, fianco a fianco.
In breve #
| Oggetto non visuale | n_pbt_crypto |
| Serve a | Verificare che un file non sia cambiato, custodire un segreto in un INI, firmare un ordine, autenticare una chiamata API |
| Principio | Una pagina nascosta fa il calcolo; ogni metodo è una normale chiamata PowerScript che restituisce il suo valore |
| Dipendenza | Il runtime WebView2, già richiesto dalla libreria — nient'altro |
Avvio rapido #
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
Ogni metodo è una chiamata che restituisce il suo valore: nessun evento, nessuna attesa da scrivere.
I formati, leggibili dagli altri strumenti #
- Hash e HMAC: esadecimale minuscolo, il testo letto in UTF-8 — Python, Java e
openssl dgstdanno lo stesso valore. of_encrypt: una sola stringa base64 che porta il sale (16 byte), il nonce (12) e il cifrato AES-256-GCM; la chiave è derivata dalla password con PBKDF2-SHA-256, 100 000 iterazioni. Bastaof_decryptcon la stessa password, su qualsiasi postazione.- Chiavi: PEM, pubblica in SPKI (
BEGIN PUBLIC KEY), privata in PKCS#8 (BEGIN PRIVATE KEY); firme RSASSA-PKCS1-v1_5 con SHA-256, in base64 — ciò cheopenssllegge e verifica. - Niente MD5: la Web Crypto non lo offre, e nulla dovrebbe più chiederlo.
Proprietà #
| Proprietà | Tipo | Predefinito | Ruolo |
|---|---|---|---|
is_last_error | string | "" | Perché l'ultima chiamata ha restituito una stringa vuota, false o un codice negativo: password errata, file illeggibile, algoritmo sconosciuto, limite demo |
il_timeout_ms | long | 20000 | Durata massima di una chiamata. Calcolare l'hash di un file grande o generare una chiave a 4096 bit può richiedere qualche secondo |
il_pbkdf2_rounds | long | 100000 | Round PBKDF2 di of_encrypt, of_password_hash e of_encrypt_file: circa 100 ms. Di più è più lento per tutti, attaccante compreso |
Costanti: HASH_MD5, HASH_SHA1, HASH_SHA256, HASH_SHA384, HASH_SHA512 per l'algoritmo; KEY_2048, KEY_3072, KEY_4096 per la dimensione di una chiave RSA, KEY_RSA_2048, KEY_EC_P256, KEY_ED25519 per il tipo di coppia; JWT_HS256, JWT_RS256, JWT_ES256, JWT_EDDSA; CHARSET_ALL, CHARSET_ALPHANUM, CHARSET_LETTERS, CHARSET_DIGITS, CHARSET_HEX.
Metodi #
| Metodo | Ruolo |
|---|---|
of_open ( ) → long | Crea la pagina nascosta. Facoltativo — ogni metodo lo fa — ma chiamarlo all'apertura della finestra paga il costo una volta. Restituisce l'handle (> 0) o un codice negativo |
of_is_open ( ) → boolean | Vero appena la pagina nascosta esiste |
of_hash (string as_algorithm, string as_text) → string | L'hash di un testo, in esadecimale; HASH_* per l'algoritmo. Vuoto in caso di errore |
of_sha256 (string as_text) → string | of_hash con HASH_SHA256 — quello che si usa di più |
of_hmac (string as_algorithm, string as_key, string as_text) → string | L'HMAC di un testo con una chiave segreta, in esadecimale — ciò che un'API chiede per autenticare una chiamata |
of_hash_file (string as_algorithm, string as_path) → string | L'hash di un file della postazione, di qualsiasi tipo e fino a 512 MB, senza caricarlo in PowerBuilder |
of_base64_encode (string as_text) → string | Un testo in base64 (i suoi byte UTF-8): ciò che un browser o Python scriverebbe. Vuoto in caso di errore |
of_base64_decode (string as_base64) → string | Il testo di ritorno; un valore che non è base64 rende una stringa vuota e is_last_error, mai spazzatura |
of_base64_encode_file (string as_path) → string | I byte di un file della postazione in base64 — un allegato, un'immagine per una chiamata JSON — fino a 512 MB, letti dalla DLL, mai caricati in PowerBuilder |
of_base64_decode_to_file (string as_base64, string as_path) → long | Scrive i byte di un valore base64 in un file — l'allegato con cui un'API ha risposto. Rende 0 una volta scritto, -5 su un percorso vuoto o un valore che non è base64, -4 se il file non può essere scritto |
of_encrypt (string as_password, string as_text) → string | Cifra un testo con una password: una sola stringa base64 da conservare. Vuoto in caso di errore |
of_decrypt (string as_password, string as_cipher) → string | Il testo di ritorno, con la stessa password. Password errata o valore alterato: stringa vuota e is_last_error, mai spazzatura |
of_uuid ( ) → string | Un UUID casuale (versione 4) |
of_random_hex (integer ai_bytes) → string | ai_bytes byte casuali (da 1 a 4096) in esadecimale: un sale, un token |
of_generate_keypair (integer ai_bits, ref string as_public_pem, ref string as_private_pem) → long | Una coppia RSA in PEM (KEY_*). Restituisce 0 quando entrambe le chiavi sono riempite, -4 in caso di errore |
of_sign (string as_private_pem, string as_text) → string | La firma di un testo con la chiave privata, in base64. Vuoto in caso di errore |
of_verify (string as_public_pem, string as_text, string as_signature) → boolean | Vero se la firma è stata fatta esattamente su questo testo dalla chiave privata corrispondente; falso — non un errore — se il testo è cambiato |
of_generate_keypair (string as_kind, ref string as_public_pem, ref string as_private_pem) → long` | Una coppia per TIPO: KEY_RSA_2048/3072/4096, KEY_EC_P256 (firme corte) o KEY_ED25519. Rende 0, -4 in caso di errore |
of_key_kind (string as_pem) → string` | La famiglia di una chiave PEM: rsa, ec-p256 o ed25519 |
of_base64url_encode (string as_text) → string` | base64 sicuro per URL (JWT, query string) |
of_base64url_decode (string as_base64url) → string` | Il testo di ritorno; vuoto se non è base64url |
of_hex_encode (string as_text) → string` | I byte UTF-8 di un testo in esadecimale |
of_hex_decode (string as_hex) → string` | Il testo di ritorno; vuoto se non è esadecimale |
of_random_password (integer ai_length, string as_charset) → string` | Una password casuale (4-256 caratteri) su un insieme CHARSET_*, senza bias. Vuota su una lunghezza errata |
of_equals_constant_time (string as_a, string as_b) → boolean` | Uguaglianza a tempo costante: confrontare un token o un codice senza tradirlo |
of_password_hash (string as_password) → string` | Ciò che si CONSERVA per una password: PBKDF2 salato (il_pbkdf2_rounds), round e sale nel valore. Mai lo stesso risultato due volte |
of_password_verify (string as_password, string as_stored) → boolean` | Vero se la password è quella del valore conservato, confrontata a tempo costante |
of_totp_secret ( ) → string` | Un segreto nuovo per i codici a due fattori (RFC 6238), in base32 |
of_totp_code (string as_secret) → string` | Il codice a sei cifre del momento, quello che mostra l'autenticatore |
of_totp_verify (string as_secret, string as_code) → boolean` | Vero se il codice è quello del momento, del passo precedente o del successivo |
of_totp_uri (string as_secret, string as_account, string as_issuer) → string` | L'URI otpauth:// da mettere in un QR code per iscrivere l'utente |
of_generate_key ( ) → string` | Una chiave AES-256 nuova, 32 byte in esadecimale |
of_encrypt_with_key (string as_key, string as_text) → string` | AES-256-GCM con una chiave ESPLICITA (hex o base64): base64 di nonce + cifrato + tag, ciò che openssl o Python decifrano |
of_decrypt_with_key (string as_key, string as_cipher) → string` | Il testo di ritorno con la stessa chiave; vuoto e is_last_error altrimenti |
of_rsa_encrypt (string as_public_pem, string as_text) → string` | Un segreto corto cifrato verso una chiave PUBBLICA (RSA-OAEP): solo la privata lo legge |
of_rsa_decrypt (string as_private_pem, string as_cipher) → string` | Il segreto di ritorno, con la chiave privata |
of_jwt_sign (string as_algorithm, string as_key, string as_claims_json, long al_expires_seconds) → string` | Un JWT firmato: JWT_HS256 (segreto condiviso), JWT_RS256/JWT_ES256/JWT_EDDSA (chiave privata); iat aggiunto, exp se la durata è > 0. Vuoto in caso di errore |
of_jwt_verify (string as_algorithm, string as_key, string as_token) → string` | I claim (JSON) di un token la cui firma, algoritmo e date sono corretti; vuoto e is_last_error altrimenti |
of_jwt_claims (string as_token) → string` | I claim SENZA verifica, per leggere chi il token nomina prima di scegliere la chiave |
of_protect (string as_text { , boolean ab_machine }) → string` | Un segreto protetto da Windows per questo utente (o questa macchina): DPAPI, nativo. Ciò che si mette nell'INI per una password di server |
of_unprotect (string as_protected) → string` | Il testo di ritorno, sullo stesso account; vuoto e is_last_error altrove |
of_crc32 (string as_text) → string` | Il CRC32 di un testo, 8 cifre esadecimali — un controllo di integrità, non un hash |
of_crc32_file (string as_path) → string` | Il CRC32 di un file, letto dalla DLL |
of_encrypt_file (string as_password, string as_source, string as_target) → long` | Un file cifrato con password, in nativo (stesso contenitore di of_encrypt). Rende 0, -5 argomento vuoto, -2 sorgente illeggibile, -4 destinazione non scrivibile |
of_decrypt_file (string as_password, string as_source, string as_target) → long` | Il file di ritorno. Rende 0, -3 password errata o file alterato (nulla è scritto), -5/-2/-4 come of_encrypt_file |
of_close ( ) | Rilascia la pagina nascosta; fatto per te alla distruzione dell'oggetto |
of_reset ( ) | Ritorno ai valori predefiniti |
Esempi #
Verificare che un file non sia cambiato #
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.")
Un allegato in base64 per un'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")
Custodire un segreto in 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)
Firmare un ordine, verificarlo altrove #
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 token per un'API, e la sua verifica #
// 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")
Una password di server custodita da Windows, e un utente a due fattori #
// 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 ...
Buone pratiche #
- Un solo oggetto per finestra, aperto con lei (
of_open): la pagina nascosta costa qualche centinaio di millisecondi la prima volta, nulla dopo. - La password non si conserva:
of_encryptrestituisce tutto ciò che serve per decifrare, tranne lei — è il contratto. - La chiave privata non viaggia: firma a casa tua, pubblica la chiave pubblica.
of_verifychiede solo quella. - Leggi
is_last_errorquando un metodo restituisce una stringa vuota: il motivo è lì, e spesso dice «password errata» o «file illeggibile» — non un difetto del componente.