PBToolboxAI v5 ← Site

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 visualen_pbt_crypto
Serve aVerificare che un file non sia cambiato, custodire un segreto in un INI, firmare un ordine, autenticare una chiamata API
PrincipioUna pagina nascosta fa il calcolo; ogni metodo è una normale chiamata PowerScript che restituisce il suo valore
DipendenzaIl runtime WebView2, già richiesto dalla libreria — nient'altro

Avvio rapido #

// Local variables
n_pbt_crypto lnv_crypto
string ls_hash, ls_cipher

// Create the object once, free it when done
lnv_crypto = create n_pbt_crypto

// The fingerprint of a text, in hexadecimal
ls_hash = lnv_crypto.of_sha256(/*text*/ "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(/*password*/ "s3cret", /*text*/ "The safe code is 4152.")
MessageBox("Back", lnv_crypto.of_decrypt(/*password*/ "s3cret", /*cipher*/ ls_cipher))

// Free the object
destroy lnv_crypto

Ogni metodo è una chiamata che restituisce il suo valore: nessun evento, nessuna attesa da scrivere.


I formati, leggibili dagli altri strumenti #

Decifrare un valore di of_encrypt lato server, in Python (libreria cryptography):

import base64, struct
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

raw = base64.b64decode(value)
assert raw[:4] == b"PBK2"
rounds = struct.unpack(">I", raw[4:8])[0]
salt, nonce, data = raw[8:24], raw[24:36], raw[36:]   # data = ciphertext + 16-byte tag
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, rounds).derive(password.encode("utf-8"))
text = AESGCM(key).decrypt(nonce, data, None).decode("utf-8")

Proprietà #

ProprietàTipoPredefinitoRuolo
il_timeout_mslong20000Quanto può durare una chiamata alla pagina nascosta: generare una chiave da 4096 bit, o derivare una chiave con milioni di cicli PBKDF2, può richiedere qualche secondo. Hash, file e blob sono nativi: nessun limite di tempo si applica a loro
il_pbkdf2_roundslong100000Round PBKDF2 di of_encrypt e of_encrypt_file: circa 100 ms, pagati a ogni lettura del valore. Da 1.000 a 10.000.000: un altro valore fa RIFIUTARE la chiamata (of_get_last_error() lo dice, -5 per un file), mai sostituire Vale anche per of_encrypt_blob. Un valore porta i suoi cicli: cambiarli non rende mai illeggibile un valore più vecchio
il_password_hash_roundslong600000Round PBKDF2 di of_password_hash: 600.000, la cifra OWASP per PBKDF2-HMAC-SHA-256 (circa 0,6 s). Un hash memorizzato è ciò che un attaccante forza offline se la tabella trapela; si verifica una sola volta per accesso. Stessi limiti Aumentateli, e of_password_needs_rehash dice quali valori memorizzati rinnovare
il_jwt_leeway_secondslong60Tolleranza di of_jwt_verify e of_jwt_verify_jwks su exp e nbf, in secondi: gli orologi di due macchine derivano. Da 0 a 86 400; ogni altro valore fa rifiutare la verifica
ipo_ownerpowerobjectnullL'oggetto visuale per cui lavora questo helper: la licenza si verifica sulla sua classe. Da impostare prima della prima chiamata. Necessario solo nell'applicazione dimostrativa; una chiave di sviluppo o di runtime sblocca l'helper senza di esso. Non sbloccato, è in modalità demo — testi di al più 2048 caratteri e file di al più 2048 byte, in ingresso come in uscita I blob seguono il limite dei file

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_RSA_3072, KEY_RSA_4096, KEY_EC_P256, KEY_EC_P384, KEY_ED25519 per il tipo di coppia; JWT_HS256, JWT_HS384, JWT_HS512, JWT_RS256, JWT_RS384, JWT_RS512, JWT_PS256, JWT_PS384, JWT_PS512, JWT_ES256, JWT_ES384, JWT_EDDSA; SIGN_PKCS1, SIGN_PSS per lo schema di una firma RSA; CHARSET_ALL, CHARSET_ALPHANUM, CHARSET_LETTERS, CHARSET_DIGITS, CHARSET_HEX; KEYFORMAT_TEXT, KEYFORMAT_HEX, KEYFORMAT_BASE64 per la forma di una chiave HMAC.


Metodi #

MetodoRuolo
of_get_last_error ( ) → stringPerché l'ultima chiamata ha restituito una stringa vuota, false o un codice negativo: password errata, file illeggibile, algoritmo sconosciuto, limite demo Un argomento NULL vale come vuoto: il motivo non è mai NULL.
of_open ( ) → longCrea la pagina nascosta. Facoltativo — ogni metodo lo fa — ma chiamarlo all'apertura della finestra paga il costo una volta. Restituisce l'handle (> 0) o -2 se non è stato possibile crearla (of_get_last_error() dice perché)
of_is_open ( ) → booleanVero appena la pagina nascosta esiste
of_hash (string as_algorithm, string as_text) → stringL'hash di un testo, in esadecimale; HASH_* per l'algoritmo, letto senza maiuscole, trattini o spazi (SHA-256 vale HASH_SHA256). NATIVO per ogni algoritmo: nessuna pagina si apre per un hash. Vuoto in caso di errore
of_sha256 (string as_text) → stringof_hash con HASH_SHA256 — quello che si usa di più
of_hmac (string as_algorithm, string as_key, string as_text { , string as_key_format }) → stringL'HMAC di un testo con una chiave segreta, in esadecimale — ciò che un'API chiede per autenticare una chiamata. La chiave è un TESTO per impostazione predefinita; KEYFORMAT_HEX o KEYFORMAT_BASE64 ne danno i BYTE: un segreto fornito codificato, o il risultato di un HMAC precedente (AWS SigV4 ne concatena quattro). Una chiave vuota è rifiutata, e anche una chiave PEM: non è mai un segreto HMAC Anche il base64url è letto.
of_hmac_verify (string as_algorithm, string as_key, string as_text, string as_expected { , string as_key_format }) → booleanVero se as_expected è l'HMAC di as_text con as_key, confrontato in TEMPO COSTANTE — la verifica della firma di un webhook (Stripe, GitHub, Shopify) senza tradirla, mai if of_hmac(…) = intestazione. as_expected in esadecimale (maiuscole indifferenti) o in base64; togli prima un prefisso come sha256=. Falso altrimenti as_expected può anche essere in base64url.
of_hash_file (string as_algorithm, string as_path) → stringL'hash di un file della postazione, di qualsiasi tipo e dimensione (letto a pezzi), senza caricarlo in PowerBuilder. Un percorso RELATIVO si cerca nella cartella corrente, poi nella cartella in cui l'applicazione è partita, poi accanto all'EXE — la regola di tutti i file di questo componente. Vuoto in caso di errore; of_get_last_error() distingue l'algoritmo sconosciuto dal file illeggibile
of_base64_encode (string as_text) → stringUn 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) → stringIl TESTO di ritorno (UTF-8). Un valore che non è base64, o i cui byte non sono un testo (un PDF, un'immagine), rende una stringa vuota e of_get_last_error() — mai rumore. I byte si scrivono in un file con of_base64_decode_to_file Oppure of_base64_decode_blob, che li rende in un blob.
of_base64_encode_file (string as_path) → stringI byte di un file della postazione in base64 — un allegato, un'immagine per una chiamata JSON — letti dalla DLL, mai caricati in PowerBuilder. Fino a 32 MB in un'applicazione a 32 bit (l'IDE PowerBuilder lo è), 512 MB a 64 bit; oltre, of_base64_encode_file_to_file. Vuoto in caso di errore; of_get_last_error() dice quale: un file troppo grande per questo processo, illeggibile, o il limite demo
of_base64_encode_file_to_file (string as_source, string as_target) → longIl base64 di un file scritto in UN ALTRO file (ASCII, senza a capo), dalla DLL, a memoria costante e qualunque sia la dimensione: ciò che invia un grosso caricamento. Percorsi come per of_hash_file. Rende 0, -5 su un percorso vuoto o una destinazione che dipende dalla cartella corrente di un'unità (\x, C:x), -2 sorgente illeggibile, -4 destinazione non scrivibile o, in demo, sorgente oltre 2048 byte
of_base64_decode_to_file (string as_base64, string as_path) → longScrive i byte di un valore base64 in un file — l'allegato con cui un'API ha risposto. Un valore VUOTO scrive un file vuoto; un percorso RELATIVO si scrive nella cartella in cui l'applicazione è partita (la cartella corrente al caricamento della libreria, la stessa nell'IDE e compilata), dove of_hash_file lo ritrova. Rende 0 una volta scritto, -5 su un percorso vuoto o che dipende dalla cartella corrente di un'unità (\x, C:x), o un valore che non è base64, -4 se il file non può essere scritto o, in demo, oltre 2048 byte
of_encrypt (string as_password, string as_text) → stringCifra un testo con una password: una sola stringa base64 da memorizzare, che porta i suoi round PBKDF2 (vedi I formati). Una password VUOTA è rifiutata — chiunque rileggerebbe il valore. Vuoto in caso di errore
of_decrypt (string as_password, string as_cipher) → stringIl testo di ritorno, con la stessa password. Password errata o valore alterato: stringa vuota e of_get_last_error(), mai rumore. Un valore il cui contenuto non è un testo (un file cifrato con of_encrypt_file) rende vuoto anch'esso: of_decrypt_file o of_decrypt_blob sui suoi byte.
of_uuid ( ) → stringUn UUID casuale (versione 4)
of_random_hex (integer ai_bytes) → stringai_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) → longUna coppia RSA in PEM (KEY_2048, KEY_3072, KEY_4096). Restituisce 0 quando entrambe le chiavi sono riempite, -5 su qualsiasi altra dimensione (entrambe restano vuote), -4 in caso di errore
of_sign (string as_private_pem, string as_text { , string as_scheme, string as_hash }) → stringLa firma di un testo con la chiave privata, in base64. La chiave dice la sua famiglia: RSA (RSASSA-PKCS1-v1_5, SHA-256), ECDSA P-256 (SHA-256, firma in DER — ciò che openssl dgst e Java SHA256withECDSA verificano) o Ed25519 (64 byte). Vuoto in caso di errore as_scheme e as_hash: ciò che l'altra parte si aspetta — SIGN_PSS per una chiave RSA (sale della lunghezza dell'hash, MGF1 con lo stesso hash), HASH_SHA384 o HASH_SHA512; un testo vuoto tiene quelli della chiave; una chiave EC prende un hash, mai PSS; una chiave Ed25519 né l'uno né l'altro. Una chiave PUBBLICA data al suo posto viene nominata
of_verify (string as_public_pem, string as_text, string as_signature { , string as_scheme, string as_hash }) → booleanVero se la firma è stata fatta esattamente su questo testo dalla chiave privata corrispondente; falso — non un errore — se il testo è cambiato. Una firma ECDSA si legge in DER (openssl, Java) o in P1363 (`rs, 64 byte: WebCrypto, .NET) Con lo schema e l'hash del firmatario (vedi of_sign`).
of_generate_keypair (string as_kind, ref string as_public_pem, ref string as_private_pem) → longUna coppia per TIPO: KEY_RSA_2048/3072/4096, KEY_EC_P256 o KEY_EC_P384 (firme corte) o KEY_ED25519. Rende 0, -5 su un tipo che non è nessuno di questi (mai una coppia RSA al suo posto), -4 in caso di errore
of_key_kind (string as_pem) → stringLa famiglia di una chiave PEM: rsa, ec-p256, ec-p384 o ed25519; si legge solo il PRIMO blocco del testo. Una chiave PKCS#1 (BEGIN RSA PRIVATE KEY), un certificato o una chiave cifrata è NOMINATA in of_get_last_error(), con il comando openssl che la converte
of_jwk_to_pem (string as_jwk_json) → stringUna chiave pubblicata come JWK (RFC 7517: kty RSA, EC P-256 / P-384 o OKP Ed25519) in PEM, ciò che prendono of_verify, of_jwt_verify e openssl: pubblica in SPKI, privata in PKCS#8 quando il JWK contiene d. I suoi membri d'uso (alg, use, key_ops, kid) non fanno parte della chiave. Vuoto in caso di errore
of_pem_to_jwk (string as_pem) → stringUna chiave PEM come JWK (JSON): ciò che pubblica un insieme di chiavi, o ciò che un'API chiede di registrare. Un PEM privato dà un JWK privato (con d): tenetelo per voi. Vuoto in caso di errore
of_base64url_encode (string as_text) → stringbase64 sicuro per URL (JWT, query string)
of_base64url_decode (string as_base64url) → stringIl testo di ritorno; vuoto se non è base64url, o se i suoi byte non sono un testo
of_hex_encode (string as_text) → stringI byte UTF-8 di un testo in esadecimale
of_hex_decode (string as_hex) → stringIl testo di ritorno; vuoto se non è esadecimale, o se i suoi byte non sono un testo
of_random_password (integer ai_length, string as_charset) → stringUna password casuale (4-256 caratteri) su un insieme CHARSET_* (vuoto = CHARSET_ALL), senza bias. Vuota su una lunghezza errata o un insieme sconosciuto
of_equals_constant_time (string as_a, string as_b) → booleanUguaglianza a tempo costante: confrontare un token o un codice senza tradirlo
of_password_hash (string as_password) → stringCiò che si MEMORIZZA per una password: PBKDF2 con sale (il_password_hash_rounds, 600.000 round), round e sale nel valore. Mai lo stesso risultato due volte. Una password vuota si hash (verificare una password vuota è legittimo). Il formato è PROPRIO di PBToolboxAI (vedi I formati)
of_password_verify (string as_password, string as_stored) → booleanVero se la password è quella del valore conservato, confrontata a tempo costante. Un valore i cui round escono da 1 000 - 10 000 000 è rifiutato: un valore ostile non fa girare la verifica per minuti
of_password_needs_rehash (string as_stored) → booleanVero quando un valore memorizzato da of_password_hash porta MENO cicli di il_password_hash_rounds: subito dopo un of_password_verify riuscito, quando la password è nota, ricalcolatene l'hash e memorizzate il nuovo valore — il consiglio OWASP quando il costo sale. Falso altrimenti; falso con of_get_last_error() quando as_stored non è un tale valore. Nessuna pagina
of_totp_secret ( ) → stringUn segreto nuovo per i codici a due fattori (RFC 6238), in base32
of_totp_code (string as_secret { , string as_hash, integer ai_digits, integer ai_period }) → stringIl codice a sei cifre del momento, quello che mostra l'autenticatore Con delle impostazioni: HASH_SHA1, HASH_SHA256 o HASH_SHA512, da 6 a 8 cifre, un periodo da 1 a 3 600 secondi — quelle che scrive of_totp_uri
of_totp_verify (string as_secret, string as_code { , ref long al_step }) → booleanVero se il codice è quello dell'istante, del passo precedente o del successivo. Con al_step, il PASSO di 30 s per cui il codice è stato accettato (0 altrimenti): un codice non deve servire due volte (RFC 6238) — conserva l'ultimo passo accettato per l'utente e rifiuta un passo che non sia maggiore. Un segreto che contiene altro che A-Z, 2-7, spazi o = è rifiutato (uno 0 digitato per una O) Gli spazi digitati dall'utente («864 010», come l'app lo mostra) sono ignorati.
of_totp_verify (string as_secret, string as_code, string as_hash, integer ai_digits, integer ai_period, ref long al_step) → booleanLa stessa verifica con le impostazioni di of_totp_code; al_step conta periodi di ai_period secondi
of_totp_uri (string as_secret, string as_account, string as_issuer { , string as_hash, integer ai_digits, integer ai_period }) → stringL'URI otpauth:// da mettere in un QR code per iscrivere l'utente Il segreto vi è scritto in base32 maiuscolo, senza spazi né =; un segreto con un altro carattere è rifiutato. Con le impostazioni di of_totp_code, l'app mostra i codici che esso calcola con esse
of_hotp_code (string as_secret, long al_counter) → stringIl codice a sei cifre di un CONTATORE (HOTP, RFC 4226, SHA-1): un token hardware, un codice inviato per email — il server tiene il contatore e lo incrementa a ogni codice accettato. al_counter: 0 o più. Vuoto in caso di errore
of_generate_key ( ) → stringUna chiave AES-256 nuova, 32 byte in esadecimale
of_encrypt_with_key (string as_key, string as_text { , string as_aad }) → stringAES-256-GCM con una chiave ESPLICITA (hex o base64): base64 di nonce + cifrato + tag, ciò che openssl o Python decifrano as_aad: DATI AGGIUNTIVI, autenticati ma non cifrati — un numero d'ordine, un identificativo: il valore si decifra solo con gli stessi. La chiave può anche essere in base64url
of_decrypt_with_key (string as_key, string as_cipher { , string as_aad }) → stringIl testo di ritorno con la stessa chiave; vuoto e of_get_last_error() altrimenti Con gli STESSI dati aggiuntivi quando c'erano.
of_rsa_encrypt (string as_public_pem, string as_text { , string as_hash }) → stringUn segreto breve cifrato verso una chiave PUBBLICA: solo la privata lo legge. Le impostazioni ESATTE, per l'altro lato: RSA-OAEP, SHA-256, MGF1 con SHA-256, senza label — Java (OAEPWithSHA-256AndMGF1Padding) e openssl pkeyutl usano MGF1-SHA-1 per impostazione predefinita, vedi I formati. 190 byte al massimo con una chiave a 2048 bit; oltre, of_get_last_error() lo dice — cifra i dati con of_encrypt_with_key e solo la loro chiave così. Una chiave PRIVATA al posto della pubblica è nominata as_hash: l'hash di OAEP e di MGF1 — HASH_SHA1 è ciò che usano per impostazione predefinita openssl pkeyutl, .NET (OaepSHA1) e Java (OAEPPadding)
of_rsa_decrypt (string as_private_pem, string as_cipher { , string as_hash }) → stringIl segreto di ritorno, con la chiave privata Con l'hash del mittente (HASH_SHA256 per impostazione predefinita).
of_jwt_sign (string as_algorithm, string as_key, string as_claims_json, long al_expires_seconds { , string as_key_format { , string as_header_json } }) → stringUn JWT firmato: JWT_HS256 (segreto condiviso), JWT_RS256/JWT_ES256/JWT_EDDSA (chiave privata); l'algoritmo è OBBLIGATORIO. iat aggiunto, exp se la durata è > 0 — un exp nei claims E una durata è rifiutato — e un exp/iat/nbf fornito deve essere un numero. L'intestazione nomina l'algoritmo come registrato (EdDSA). as_key_format: il segreto HS256 in KEYFORMAT_TEXT, KEYFORMAT_HEX o KEYFORMAT_BASE64; una chiave PEM non è mai un segreto HS256. as_header_json: membri AGGIUNTI all'intestazione, per esempio {"kid":"2026-09"} — la chiave che un'API sceglie (Apple, ogni fornitore che ruota le sue chiavi); alg e typ restano quelli della libreria. Vuoto in caso di errore Anche JWT_HS384/HS512, JWT_RS384/RS512, JWT_PS256/PS384/PS512 e JWT_ES384 (chiave KEY_EC_P384). Una chiave della famiglia sbagliata, o la sua metà PUBBLICA, viene nominata
of_jwt_verify (string as_algorithm, string as_key, string as_token { , string as_key_format { , string as_expected_claims_json } }) → stringLe claim (JSON) di un token la cui firma, il cui algoritmo e le cui date sono giusti (il_jwt_leeway_seconds di tolleranza, un minuto per impostazione predefinita). L'algoritmo è OBBLIGATORIO e confrontato ESATTAMENTE con quello dell'intestazione: "" è rifiutato, e una chiave PEM non è mai un segreto HS — altrimenti una chiave PUBBLICA serviva da segreto e un token falsificato passava. Un exp o un nbf che non è un numero, e un'intestazione crit, sono rifiutati. as_key_format come per of_jwt_sign. as_expected_claims_json: le claim che devono esserci con quel valore — l'emittente (iss), il pubblico (aud, trovato anche in una lista); una claim mancante o diversa è rifiutata e nominata. Vuoto e of_get_last_error() altrimenti; una claim si legge con n_pbt_json (of_load, poi of_get_string)
of_jwt_verify_jwks (string as_algorithm, string as_jwks_json, string as_token { , string as_expected_claims_json }) → stringLe claim di un token verificato con un INSIEME DI CHIAVI (JWKS, RFC 7517) — ciò che Azure AD, Google, Auth0 o Keycloak pubblicano al loro jwks_uri (da leggere con il restclient, da tenere qualche ora): il kid del token sceglie la chiave. Un token senza kid, un kid assente dall'insieme o presente due volte, una chiave di un altro algoritmo o di un altro uso (use = enc): rifiutati, of_get_last_error() dice quale. Date e claim attese come per of_jwt_verify
of_jwt_claims (string as_token) → stringI claim SENZA verifica, per leggere chi il token nomina prima di scegliere la chiave
of_jwt_header (string as_token) → stringL'intestazione (JSON) di un token SENZA verifica: leggerne il kid per scegliere la chiave che lo verificherà. Non fidarsene mai da sola
of_protect (string as_text { , boolean ab_machine { , string as_entropy } }) → stringUn segreto protetto da Windows per questo utente (o questa macchina): DPAPI, nativo. Ciò che si mette nell'INI per una password di server. NON lo protegge da un altro programma avviato dallo stesso utente (né, con ab_machine, da un altro account della postazione): as_entropy, un segreto della tua applicazione, restringe questo cerchio — la stessa entropia serve per rileggerlo
of_unprotect (string as_protected { , string as_entropy }) → stringIl testo di ritorno, sullo stesso account e con la STESSA entropia se ce n'è una; vuoto e of_get_last_error() altrove
of_crc32 (string as_text) → stringIl CRC32 di un testo, 8 cifre esadecimali — un controllo di integrità, non un hash
of_crc32_file (string as_path) → stringIl CRC32 di un file, letto dalla DLL fino in fondo: una lettura che fallisce a metà è un errore, mai il CRC dell'inizio
of_encrypt_file (string as_password, string as_source, string as_target) → longUn file cifrato con password, in nativo e a pezzi (stesso contenitore di of_encrypt), fino a 64 GB — il limite di un contenitore AES-GCM. Percorsi relativi: la sorgente si cerca come per of_hash_file, la destinazione si scrive nella cartella in cui l'applicazione è partita. Rende 0, -5 argomento vuoto o il_pbkdf2_rounds fuori limiti, -2 sorgente illeggibile, -4 destinazione non scrivibile o, in demo, sorgente oltre 2048 byte, -7 sorgente oltre 64 GB Una destinazione che dipende dalla cartella corrente di un'unità (\x, C:x) rende -5, e of_get_last_error() lo dice
of_decrypt_file (string as_password, string as_source, string as_target) → longIl file di ritorno. Rende 0, -3 password errata o file alterato (nulla è scritto: il testo è verificato in un file temporaneo nascosto che Windows cancella anche se l'applicazione muore), -5/-2/-4 come of_encrypt_file
of_base64_encode_blob (blob ab_data) → stringI byte di un blob in base64 — una foto letta con SelectBlob, un PDF ricevuto — senza file. Nativo. Fino a 32 MB a 32 bit, 512 MB a 64 bit; in demo, 2048 byte. Vuoto in caso di errore
of_base64_decode_blob (string as_base64, ref blob ab_data) → longI BYTE di un valore base64 (o base64url) in un blob — un allegato ricevuto, pronto per UpdateBlob. Nativo. Rende 0 una volta riempito ab_data, -5 quando il valore non è base64, -4 in demo oltre 2048 byte; ab_data è allora vuoto
of_hash_blob (string as_algorithm, blob ab_data) → stringL'hash dei byte di un blob, con gli algoritmi di of_hash — lo stesso valore di of_hash_file sugli stessi byte. Nativo. Vuoto in caso di errore
of_hmac_blob (string as_algorithm, string as_key, blob ab_data { , string as_key_format }) → stringL'HMAC dei byte di un blob con una chiave segreta, la chiave come per of_hmac: un file ricevuto, verificato con la firma che il servizio ha inviato con esso. Nativo. Vuoto in caso di errore
of_crc32_blob (blob ab_data) → stringIl CRC32 dei byte di un blob, 8 cifre esadecimali. Nativo
of_encrypt_blob (string as_password, blob ab_data, ref blob ab_cipher) → longI byte di un blob cifrati con password: il contenitore di of_encrypt e di of_encrypt_file, in byte (52 in più dei dati) — letto in base64, si decifra con of_decrypt, e viceversa. Nativo, il_pbkdf2_rounds. Rende 0 una volta riempito ab_cipher, -5 password vuota o cicli fuori limite, -4 in caso di errore o, in demo, oltre 2048 byte
of_decrypt_blob (string as_password, blob ab_cipher, ref blob ab_data) → longI byte di of_encrypt_blob (o di un valore di of_encrypt letto in byte), con la stessa password. Rende 0 una volta riempito ab_data, -3 password errata o byte alterati (ab_data resta vuoto), -5 password vuota o byte che non sono un tale valore, -4 in demo oltre 2048 byte
of_close ( ) → longRilascia la pagina nascosta; fatto per te alla distruzione dell'oggetto Rende 0 una volta fatto.
of_reset ( ) → longTorna ai valori predefiniti: il_timeout_ms, il_pbkdf2_rounds, il_password_hash_rounds, il_jwt_leeway_seconds, of_get_last_error(); ipo_owner resta Rende 0 una volta fatto.

Esempi #

Verificare che un file non sia cambiato #

// Local variables
string ls_expected, ls_actual

// The fingerprint recorded at deploy time, and the one of the file on this workstation
ls_expected = ProfileString("deploy.ini", "files", "orders.pbd", "")
ls_actual = lnv_crypto.of_hash_file(/*algorithm*/ n_pbt_crypto.HASH_SHA256, /*path*/ "C:\MyApp\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 #

// Local variables
n_pbt_json lnv_j
n_pbt_restclient lnv_rest

// The REST client of the window
lnv_rest = create n_pbt_restclient

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

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

// Free the client
destroy lnv_rest

Custodire un segreto in un INI #

// Local variables
string ls_master, ls_db_password

// At setup : encrypt once, store the string
SetProfileString("app.ini", "db", "password", lnv_crypto.of_encrypt(/*password*/ ls_master, /*text*/ 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(/*password*/ ls_master, /*cipher*/ ProfileString("app.ini", "db", "password", ""))
if ls_db_password = "" then MessageBox("Login", lnv_crypto.of_get_last_error())

Firmare un ordine, verificarlo altrove #

// Local variables
string ls_public, ls_private, ls_signature, ls_order_json

// The order to sign, as the application writes it
ls_order_json = '{"order":4152,"amount":690.00}'

// Sign the order with the private key
lnv_crypto.of_generate_keypair(/*bits*/ n_pbt_crypto.KEY_2048, /*public_pem*/ ls_public, /*private_pem*/ ls_private)   // once ; keep ls_private
ls_signature = lnv_crypto.of_sign(/*private_pem*/ ls_private, /*text*/ ls_order_json)

// The public key and the signature travel with the order ; anyone can check :
if not lnv_crypto.of_verify(/*public_pem*/ ls_public, /*text*/ ls_order_json, /*signature*/ ls_signature) then MessageBox("Order", "This order was altered.")

Un token per un'API, e la sua verifica #

// Local variables
string ls_api_secret, ls_token, ls_claims, ls_role
n_pbt_restclient lnv_rest
n_pbt_json lnv_json

// The REST client of the window, and the secret the API shares with you
lnv_rest = create n_pbt_restclient
ls_api_secret = ProfileString("app.ini", "api", "secret", "")

// The client : a token valid one hour, in the Authorization header
ls_token = lnv_crypto.of_jwt_sign(/*algorithm*/ n_pbt_crypto.JWT_HS256, /*key*/ ls_api_secret, /*claims_json*/ '{"sub":"jdoe","role":"admin"}', /*expires_seconds*/ 3600)
lnv_rest.of_set_bearer(/*token*/ 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(/*algorithm*/ n_pbt_crypto.JWT_HS256, /*key*/ ls_api_secret, /*token*/ ls_token)
if ls_claims = "" then MessageBox("API", lnv_crypto.of_get_last_error())

// One claim, read with n_pbt_json
lnv_json.of_load(/*json*/ ls_claims)
ls_role = lnv_json.of_get_string(/*path*/ "role")
destroy lnv_rest

Verificare la firma di un webhook #

// Local variables
string ls_header, ls_signature, ls_raw_body, ls_webhook_secret

// ls_raw_body and ls_header : the body and the signature header your web service received ;
// the secret, the one the service shares with you
ls_webhook_secret = ProfileString("app.ini", "webhook", "secret", "")

// The service signs the raw body with the shared secret and sends the HMAC in a header
// (GitHub : "sha256=<hex>", Stripe : hex, Shopify : base64). Compare in CONSTANT time.
ls_signature = ls_header
if Left(ls_signature, 7) = "sha256=" then ls_signature = Mid(ls_signature, 8)
if not lnv_crypto.of_hmac_verify(/*algorithm*/ n_pbt_crypto.HASH_SHA256, /*key*/ ls_webhook_secret, /*text*/ ls_raw_body, /*expected*/ ls_signature) then
	MessageBox("Webhook", "This call was not sent by the service.")
	return
end if

Una password di server custodita da Windows, e un utente a due fattori #

// Local variables
string ls_db_password, ls_secret, ls_login, ls_typed, ls_stored_hash, ls_six_digits
long ll_step, ll_last_step

// ls_login, ls_typed and ls_six_digits come from the login window ; ls_secret,
// ls_stored_hash and ll_last_step from the record of the user. At setup : the INI holds a value Windows protects for this account, never the password ;
// the entropy, a secret of the application, keeps other programs of the same user out
SetProfileString("app.ini", "db", "password", lnv_crypto.of_protect(/*text*/ ls_db_password, /*machine*/ false, /*entropy*/ "MyApp-7f3a"))

// At run time : the same entropy
ls_db_password = lnv_crypto.of_unprotect(/*protected*/ ProfileString("app.ini", "db", "password", ""), /*entropy*/ "MyApp-7f3a")

// 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(/*secret*/ ls_secret, /*account*/ ls_login, /*issuer*/ "MyApp")

// Logging in : the stored hash, then the six digits - and never the same code twice (RFC 6238)
if lnv_crypto.of_password_verify(/*password*/ ls_typed, /*stored*/ ls_stored_hash) and lnv_crypto.of_totp_verify(/*secret*/ ls_secret, /*code*/ ls_six_digits, /*step*/ ll_step) then
	if ll_step > ll_last_step then ll_last_step = ll_step   // store it with the user ; accept the login
end if

Verificare un token di Azure AD, Google o Auth0 (JWKS) #

// Local variables
n_pbt_restclient lnv_rest
string ls_jwks, ls_token, ls_claims

// ls_token : the token the user brought (the Authorization header). The key set of the identity provider : fetched once, kept a few hours
lnv_rest = create n_pbt_restclient
if lnv_rest.of_get(/*url*/ "https://login.microsoftonline.com/common/discovery/v2.0/keys") = 200 then ls_jwks = lnv_rest.of_response_text()
destroy lnv_rest

// The kid of the token picks the key ; the issuer and the audience must be yours
ls_claims = lnv_crypto.of_jwt_verify_jwks(/*algorithm*/ n_pbt_crypto.JWT_RS256, /*jwks_json*/ ls_jwks, /*token*/ ls_token, /*expected_claims_json*/ '{"iss":"https://login.microsoftonline.com/my-tenant-id/v2.0","aud":"api://orders"}')
if ls_claims = "" then MessageBox("Login", lnv_crypto.of_get_last_error())

Una foto dal database, in base64 (blob) #

// Local variables
blob lb_photo
string ls_answer
n_pbt_json lnv_j

// ls_answer : the base64 an API answered. The photo of the employee, read from the database
SELECTBLOB photo INTO :lb_photo FROM employee WHERE emp_id = 4152 USING SQLCA;

// Its bytes in base64 in the JSON body of a call, with its fingerprint
lnv_j.of_set_string(/*path*/ "photo", /*value*/ lnv_crypto.of_base64_encode_blob(/*data*/ lb_photo))
lnv_j.of_set_string(/*path*/ "sha256", /*value*/ lnv_crypto.of_hash_blob(/*algorithm*/ n_pbt_crypto.HASH_SHA256, /*data*/ lb_photo))

// The other way : the bytes an API answered, back into the database
if lnv_crypto.of_base64_decode_blob(/*base64*/ ls_answer, /*data*/ lb_photo) = 0 then
	UPDATEBLOB employee SET photo = :lb_photo WHERE emp_id = 4152 USING SQLCA;
end if

Buone pratiche #

← Riferimento dei componenti · Sommario della guida