crypto — n_pbt_crypto #
← Komponentenreferenz · Inhalt des Handbuchs
Hashes, HMAC, Verschlüsselung per Passwort, Zufallswerte und RSA-Signaturen — die Web Crypto API der WebView2-Engine, die Ihr PowerBuilder 10 sonst nirgends hat. Keine Dritt-DLL, kein Dienst: was der Arbeitsplatz bereits kann, für PowerScript.
▶ Live ansehen — Demoanwendung, Kachel Crypto: die Ergebnisse, der zugehörige Code und diese Seite nebeneinander.
Kurzüberblick #
| Nichtvisuelles Objekt | n_pbt_crypto |
| Wofür | Prüfen, dass eine Datei unverändert ist, ein Geheimnis in einer INI aufbewahren, eine Bestellung signieren, einen API-Aufruf authentifizieren |
| Prinzip | Eine verborgene Seite rechnet; jede Methode ist ein gewöhnlicher PowerScript-Aufruf, der seinen Wert liefert |
| Abhängigkeit | Die WebView2-Runtime, die die Bibliothek ohnehin braucht — sonst nichts |
Schnellstart #
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
Jede Methode ist ein Aufruf, der seinen Wert liefert: kein Ereignis, kein Warten.
Die Formate, für andere Werkzeuge lesbar #
- Hashes und HMAC: Kleinbuchstaben-Hexadezimal, der Text als UTF-8 gelesen — Python, Java und
openssl dgstliefern denselben Wert. of_encrypt: eine einzige Base64-Zeichenkette mit Salt (16 Bytes), Nonce (12) und dem AES-256-GCM-Chiffrat; der Schlüssel wird per PBKDF2-SHA-256 mit 100 000 Runden aus dem Passwort abgeleitet.of_decryptmit demselben Passwort genügt, auf jedem Arbeitsplatz.- Schlüssel: PEM, öffentlich als SPKI (
BEGIN PUBLIC KEY), privat als PKCS#8 (BEGIN PRIVATE KEY); Signaturen RSASSA-PKCS1-v1_5 mit SHA-256, Base64 — wasopensslliest und prüft. - Kein MD5: Web Crypto bietet es nicht an, und nichts sollte es mehr verlangen.
Eigenschaften #
| Eigenschaft | Typ | Standard | Rolle |
|---|---|---|---|
is_last_error | string | "" | Warum der letzte Aufruf eine leere Zeichenkette, false oder einen negativen Code lieferte: falsches Passwort, unlesbare Datei, unbekannter Algorithmus, Demogrenze |
il_timeout_ms | long | 20000 | Maximale Dauer eines Aufrufs. Eine große Datei zu hashen oder einen 4096-Bit-Schlüssel zu erzeugen kann einige Sekunden dauern |
il_pbkdf2_rounds | long | 100000 | PBKDF2-Runden von of_encrypt, of_password_hash und of_encrypt_file: etwa 100 ms. Mehr ist für alle langsamer, den Angreifer eingeschlossen |
Konstanten: HASH_MD5, HASH_SHA1, HASH_SHA256, HASH_SHA384, HASH_SHA512 für den Algorithmus; KEY_2048, KEY_3072, KEY_4096 für die Größe eines RSA-Schlüssels, KEY_RSA_2048, KEY_EC_P256, KEY_ED25519 für die Art eines Paars; JWT_HS256, JWT_RS256, JWT_ES256, JWT_EDDSA; CHARSET_ALL, CHARSET_ALPHANUM, CHARSET_LETTERS, CHARSET_DIGITS, CHARSET_HEX.
Methoden #
| Methode | Rolle |
|---|---|
of_open ( ) → long | Erzeugt die verborgene Seite. Optional — jede Methode tut es — aber beim Öffnen des Fensters aufgerufen, fällt der Aufwand einmal an. Liefert das Handle (> 0) oder einen negativen Code |
of_is_open ( ) → boolean | Wahr, sobald die verborgene Seite existiert |
of_hash (string as_algorithm, string as_text) → string | Der Hash eines Textes, hexadezimal; HASH_* für den Algorithmus. Leer bei Fehler |
of_sha256 (string as_text) → string | of_hash mit HASH_SHA256 — der meistgebrauchte |
of_hmac (string as_algorithm, string as_key, string as_text) → string | Der HMAC eines Textes unter einem geheimen Schlüssel, hexadezimal — was eine API zur Authentifizierung eines Aufrufs verlangt |
of_hash_file (string as_algorithm, string as_path) → string | Der Hash einer Datei des Arbeitsplatzes, beliebiger Art und bis 512 MB, ohne sie in PowerBuilder zu laden |
of_base64_encode (string as_text) → string | Ein Text in Base64 (seine UTF-8-Bytes): was ein Browser oder Python schreiben würde. Leer bei Fehler |
of_base64_decode (string as_base64) → string | Der Text zurück; ein Wert, der kein Base64 ist, ergibt einen leeren String und is_last_error, nie Unsinn |
of_base64_encode_file (string as_path) → string | Die Bytes einer Datei des Arbeitsplatzes in Base64 — ein Anhang, ein Bild für einen JSON-Aufruf — bis 512 MB, von der DLL gelesen, nie in PowerBuilder geladen |
of_base64_decode_to_file (string as_base64, string as_path) → long | Schreibt die Bytes eines Base64-Werts in eine Datei — den Anhang, den eine API geantwortet hat. Gibt 0 nach dem Schreiben zurück, -5 bei leerem Pfad oder einem Wert, der kein Base64 ist, -4, wenn die Datei nicht geschrieben werden kann |
of_encrypt (string as_password, string as_text) → string | Verschlüsselt einen Text mit einem Passwort: eine einzige Base64-Zeichenkette zum Speichern. Leer bei Fehler |
of_decrypt (string as_password, string as_cipher) → string | Der Text zurück, mit demselben Passwort. Falsches Passwort oder veränderter Wert: leere Zeichenkette und is_last_error, niemals Unsinn |
of_uuid ( ) → string | Eine zufällige UUID (Version 4) |
of_random_hex (integer ai_bytes) → string | ai_bytes Zufallsbytes (1 bis 4096) hexadezimal: ein Salt, ein Token |
of_generate_keypair (integer ai_bits, ref string as_public_pem, ref string as_private_pem) → long | Ein RSA-Schlüsselpaar in PEM (KEY_*). Liefert 0, sobald beide Schlüssel gefüllt sind, -4 bei Fehler |
of_sign (string as_private_pem, string as_text) → string | Die Signatur eines Textes mit dem privaten Schlüssel, Base64. Leer bei Fehler |
of_verify (string as_public_pem, string as_text, string as_signature) → boolean | Wahr, wenn die Signatur genau über diesen Text mit dem passenden privaten Schlüssel erstellt wurde; falsch — kein Fehler — wenn der Text geändert wurde |
of_generate_keypair (string as_kind, ref string as_public_pem, ref string as_private_pem) → long` | Ein Schlüsselpaar nach ART: KEY_RSA_2048/3072/4096, KEY_EC_P256 (kurze Signaturen) oder KEY_ED25519. Gibt 0 zurück, -4 bei Fehler |
of_key_kind (string as_pem) → string` | Die Familie eines PEM-Schlüssels: rsa, ec-p256 oder ed25519 |
of_base64url_encode (string as_text) → string` | URL-sicheres Base64 (JWT, Query-String) |
of_base64url_decode (string as_base64url) → string` | Der Text zurück; leer, wenn es kein Base64url ist |
of_hex_encode (string as_text) → string` | Die UTF-8-Bytes eines Texts in Hexadezimal |
of_hex_decode (string as_hex) → string` | Der Text zurück; leer, wenn es kein Hex ist |
of_random_password (integer ai_length, string as_charset) → string` | Ein zufälliges Passwort (4 bis 256 Zeichen) aus einem CHARSET_*-Satz, ohne Verzerrung. Leer bei falscher Länge |
of_equals_constant_time (string as_a, string as_b) → boolean` | Gleichheit in konstanter Zeit: ein Token oder einen Code vergleichen, ohne ihn preiszugeben |
of_password_hash (string as_password) → string` | Was man für ein Passwort SPEICHERT: gesalzenes PBKDF2 (il_pbkdf2_rounds), Runden und Salz im Wert. Nie zweimal dasselbe Ergebnis |
of_password_verify (string as_password, string as_stored) → boolean` | Wahr, wenn das Passwort das des gespeicherten Werts ist, in konstanter Zeit verglichen |
of_totp_secret ( ) → string` | Ein neues Geheimnis für Zwei-Faktor-Codes (RFC 6238), in Base32 |
of_totp_code (string as_secret) → string` | Der sechsstellige Code des Augenblicks, den der Authenticator zeigt |
of_totp_verify (string as_secret, string as_code) → boolean` | Wahr, wenn der Code der des Augenblicks, des vorigen oder des nächsten Schritts ist |
of_totp_uri (string as_secret, string as_account, string as_issuer) → string` | Die otpauth://-URI für einen QR-Code, um den Benutzer zu registrieren |
of_generate_key ( ) → string` | Ein neuer AES-256-Schlüssel, 32 Bytes in Hexadezimal |
of_encrypt_with_key (string as_key, string as_text) → string` | AES-256-GCM mit EXPLIZITEM Schlüssel (Hex oder Base64): Base64 aus Nonce + Chiffrat + Tag, was openssl oder Python entschlüsseln |
of_decrypt_with_key (string as_key, string as_cipher) → string` | Der Text zurück mit demselben Schlüssel; sonst leer und is_last_error |
of_rsa_encrypt (string as_public_pem, string as_text) → string` | Ein kurzes Geheimnis, zu einem ÖFFENTLICHEN Schlüssel verschlüsselt (RSA-OAEP): nur der private liest es |
of_rsa_decrypt (string as_private_pem, string as_cipher) → string` | Das Geheimnis zurück, mit dem privaten Schlüssel |
of_jwt_sign (string as_algorithm, string as_key, string as_claims_json, long al_expires_seconds) → string` | Ein signiertes JWT: JWT_HS256 (gemeinsames Geheimnis), JWT_RS256/JWT_ES256/JWT_EDDSA (privater Schlüssel); iat hinzugefügt, exp bei Dauer > 0. Leer bei Fehler |
of_jwt_verify (string as_algorithm, string as_key, string as_token) → string` | Die Claims (JSON) eines Tokens, dessen Signatur, Algorithmus und Daten stimmen; sonst leer und is_last_error |
of_jwt_claims (string as_token) → string` | Die Claims OHNE Prüfung, um zu lesen, wen das Token nennt, bevor der Schlüssel gewählt wird |
of_protect (string as_text { , boolean ab_machine }) → string` | Ein von Windows für diesen Benutzer (oder diese Maschine) geschütztes Geheimnis: DPAPI, nativ. Was in die INI für ein Serverpasswort kommt |
of_unprotect (string as_protected) → string` | Der Text zurück, auf demselben Konto; anderswo leer und is_last_error |
of_crc32 (string as_text) → string` | Der CRC32 eines Texts, 8 Hexziffern — eine Integritätsprüfung, kein Hash |
of_crc32_file (string as_path) → string` | Der CRC32 einer Datei, von der DLL gelesen |
of_encrypt_file (string as_password, string as_source, string as_target) → long` | Eine per Passwort verschlüsselte Datei, nativ (gleicher Container wie of_encrypt). Gibt 0 zurück, -5 leeres Argument, -2 unlesbare Quelle, -4 nicht beschreibbares Ziel |
of_decrypt_file (string as_password, string as_source, string as_target) → long` | Die Datei zurück. Gibt 0 zurück, -3 falsches Passwort oder veränderte Datei (nichts wird geschrieben), -5/-2/-4 wie of_encrypt_file |
of_close ( ) | Gibt die verborgene Seite frei; geschieht beim Zerstören des Objekts automatisch |
of_reset ( ) | Zurück zu den Standardwerten |
Beispiele #
Prüfen, dass eine Datei unverändert ist #
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.")
Ein Anhang in Base64 für eine 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")
Ein Geheimnis in einer INI aufbewahren #
// 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)
Eine Bestellung signieren, anderswo prüfen #
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.")
Ein Token für eine API und seine Prüfung #
// 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")
Ein von Windows verwahrtes Serverpasswort und ein Zwei-Faktor-Benutzer #
// 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 ...
Bewährte Praxis #
- Ein Objekt pro Fenster, mit ihm geöffnet (
of_open): die verborgene Seite kostet beim ersten Mal einige hundert Millisekunden, danach nichts. - Das Passwort wird nicht gespeichert:
of_encryptliefert alles zum Entschlüsseln — außer ihm; das ist der Vertrag. - Der private Schlüssel reist nicht: zu Hause signieren, den öffentlichen Schlüssel veröffentlichen.
of_verifybraucht nur diesen. is_last_errorlesen, wenn eine Methode eine leere Zeichenkette liefert: der Grund steht dort, und oft heißt er „falsches Passwort“ oder „unlesbare Datei“ — kein Fehler der Komponente.