1/** ************************************************************************** 2// Firma: Hehner Reus Systems GmbH 3// Projekt: com.cerno.basis 4// Datei: cryptTools 5// Erstellung: 12.10.2022 6// Verwendung: Diese Klasse bietet ein paar bequeme Zugriffe auf kryptografische Funktionen an. 7// 8// Benötigte JS-Dateien: DOM.js, CSS.js, Event.js 9// 10// $Id: cryptTools.js,v 1.15 2025/06/06 10:58:11 lpo Exp $ 11// $Author: lpo $ 12// 13/**************************************************************************** */ 14 15"use strict"; 16 17class HRCrypt 18{ 19 /* 20 * Die für eine Eingabe eines Passwortes erlaubten Sonderzeichen. 21 */ 22 static PasswordSpecialCharacters = '[`~!@#$%^&*()\\-_=+|\[{\\]}};:\'\",<.>\/?]'; 23 24 /** 25 * Folgender Algorithmus "verschlüsselt" eine gegebene (kurze!) Zeichenkette nach dem Prinzip "Security by Obscurity". Es handelt sich 26 * nicht wirklich um eine Verschlüsselung, sondern darum, die gegebene Zeichenkette anhand eines unbekannten Verfahrens so schlecht 27 * lesbar wie nur möglich zu machen. Längere Texte können natürlich über eine Häufigkeitsanalyse ohne Weiteres "entschlüsselt" werden. 28 * Der Zweck dieses Algorithmuses liegt darin, kurze Texte wie E-Mailadressen im Quellcode der Seite nicht im Klartext stehen zu haben. 29 * Wir wollen hier eine Hürde schaffen, damit z.B. Bots keine E-Mail-Adressen im Klartext auf der Seite abgreifen können. Auch benutzen 30 * wir kein Standard-Verschlüsselungsverfahren, weil dieses auch clientseitig umgesetzt werden muss und wir dort unseren auf dem Server 31 * gespeicherten Schlüssel nicht nutzen dürfen. 32 * 33 * <p>(Da E-Mail-Adressen i.A. sehr kurz sind, ist der Erfolg beim Angriff der Häufigkeitsanalyse sogar recht unwahrscheinlich.)</p> 34 * 35 * <p><strong>Achtung:</strong></p> 36 * <p>Da hier jeglicher Pseudozufall fehlt und aus wiederholtem Input der selbe Output generiert wird, auf keinen Fall für ernsthafte 37 * Verschlüsselungen nutzen! Das ist KEIN sinnvoller Verschlüsselungsalgorithmus!</p> 38 * 39 * @param message Die zu verschlüsselnde Nachricht. 40 * @return Die "verschlüsselte" Nachricht. 41 */ 42 static hrEncrypt(message) 43 { 44 // Dreht den String einmal um... 45 let reversedString = ''; 46 for (let i = message.length - 1; i >= 0; i--) 47 { 48 reversedString += message.charAt(i); 49 } 50 51 // ...und einmal noch die Bytes spiegeln. 52 let tempBytes = new Uint8Array(reversedString.length); 53 for (let i = 0; i < reversedString.length; i++) 54 { 55 tempBytes[i] = 255 - reversedString.charCodeAt(i); 56 } 57 58 // Base64-Kodierung der Byte-Daten 59 let binaryString = ''; 60 for (let byte of tempBytes) 61 { 62 binaryString += String.fromCharCode(byte); 63 } 64 return btoa(binaryString).trim(); 65 } 66 67 /** 68 * Folgender Algorithmus "entschlüsselt" eine gegebene (kurze!) Zeichenkette, die zuvor nach dem Prinzip "Security by Obscurity" verschlüsselt wurde. 69 * Es handelt sich nicht wirklich um eine Verschlüsselung, sondern darum, die gegebene Zeichenkette anhand eines unbekannten Verfahrens so schlecht 70 * lesbar wie nur möglich zu machen. Längere Texte können natürlich über eine Häufigkeitsanalyse ohne Weiteres "entschlüsselt" werden. 71 * Der Zweck dieses Algorithmuses liegt darin, kurze Texte wie E-Mailadressen im Quellcode der Seite nicht im Klartext stehen zu haben. 72 * Wir wollen hier eine Hürde schaffen, damit z.B. Bots keine E-Mail-Adressen im Klartext auf der Seite abgreifen können. 73 * 74 * @param message Eine über unsere eigene "HR-Encode" verschlüsselte Nachricht. 75 * @returns die entschlüsselte Nachricht. 76 */ 77 static hrDecrypt(message) 78 { 79 // Base-64-Decoding. 80 const temp = window.atob(message); 81 82 let decrypted = ''; 83 for (let i = temp.length - 1; i >= 0; i--) 84 { 85 decrypted += temp.charAt(i); 86 } 87 88 let encoder = []; 89 for (let i = 0; i < decrypted.length; i++) 90 { 91 encoder[i] = 255 - decrypted.charCodeAt(i); 92 } 93 94 decrypted = ''; 95 for (let i = 0; i < encoder.length; i++) 96 { 97 decrypted += String.fromCharCode(encoder[i]); 98 } 99 100 return decrypted; 101 }; 102 103 /** 104 * Verschlüsselt eine gegebene Nachricht mithilfe einer Fibonacci-Sequenz zur Modifikation der Zeichenwerte. 105 * Die Fibonacci-Sequenz ist eine Zahlenreihe, in der jede Zahl die Summe der beiden vorhergehenden Zahlen ist, beginnend mit 3 und 5. 106 * 107 * <p><strong>Achtung:</strong></p> 108 * <p>Da hier jeglicher Pseudozufall fehlt und aus wiederholtem Input der selbe Output generiert wird, auf keinen Fall für ernsthafte 109 * Verschlüsselungen nutzen! Das ist KEIN sinnvoller Verschlüsselungsalgorithmus!</p> 110 * 111 * @param message Die zu verschlüsselnde Nachricht. 112 * @return Die verschlüsselte Nachricht als String. 113 */ 114 static hrEncryptFibonacciShift(message) 115 { 116 // Fibonacci-Verschlüsselung: Startet bei 3 & 5 117 let prev = 3; 118 let next = 5; 119 let result = ''; 120 121 for (let curr of message) 122 { 123 // Zeichen aus dem Alphabet von 33 bis 126 124 result += String.fromCharCode(((curr.charCodeAt(0) + next) % 93) + 33); 125 let temp = prev + next; 126 prev = next; 127 next = temp; 128 } 129 130 return result.trim(); 131 } 132 133 /** 134 * Verschlüsselt eine gegebene Nachricht mithilfe abwechselnder Bitverschiebungen.
135 * Bei der Bitverschiebung wird abwechselnd nach links und nach rechts verschoben. 136 * 137 * <p><strong>Achtung:</strong></p> 138 * <p>Da hier jeglicher Pseudozufall fehlt und aus wiederholtem Input der selbe Output generiert wird, auf keinen Fall für ernsthafte 139 * Verschlüsselungen nutzen! Das ist KEIN sinnvoller Verschlüsselungsalgorithmus!</p> 140 * 141 * @param message Die zu verschlüsselnde Nachricht. 142 * @return Die verschlüsselte Nachricht als String. 143 */ 144 static hrEncryptAlternateBitShift(message) 145 { 146 let result = ''; 147 148 // Bit-Shift-Verschiebung: Abwechselnd links und rechts 149 let shiftLeft = true; 150 151 // Schleife über die Nachricht 152 for (let curr of message) 153 { 154 // Verschiebung des Zeichens um eine Stelle. 155 let shiftedChar; 156 if (shiftLeft) 157 shiftedChar = (curr.charCodeAt(0) << 1) & 0x7F; 158 else 159 shiftedChar = (curr.charCodeAt(0) >> 1) & 0x7F; 160 161 // Stellt sicher, dass es druckbare Zeichen sind. 162 result += String.fromCharCode((shiftedChar % 93) + 33); 163 164 // Wechsel der Richtung der Bitverschiebung 165 shiftLeft = !shiftLeft; 166 } 167 168 return result.trim(); 169 } 170 171 /** 172 * Validiert ein Passwort nach bestimmten Passwort-Policies. 173 * 174 * @param password Passwort zum Überprüfen. 175 * @param min Gibt die Mindestlänge des Passwortes an. 176 * @param caseSensitive Gibt an, ob Groß- und Kleinschreibung überprüft werden soll. 177 * @param specials Gibt an, ob in dem Passwort Sonderzeichen vorhanden sein sollten. 178 * @param numbers Gibt an, ob in dem Passwort Zahlen vorkommen sollten. 179 * 180 * @return Ein js-Objekt der Form {min: true/false, caseSensitive: true/false, specials: true/false, numbers: true/false}, 181 * welches angibt, welche Prüfungen gescheitert und welche erfüllt sind. Wurde keine Zeichenkette übergeben (null, 182 * undefined, anderer Type, so wird null zurückgegeben. 183 */ 184 static validatePassword(password, min, caseSensitive, specials, numbers) 185 { 186 let result = null; 187 188 try 189 { 190 if (password != null && password != undefined && typeof(password) === 'string') 191 { 192 result = {}; 193 194 password = password.trim(); 195 196 // Test: Minimum an Zeichen 197 if (min > 0) 198 result.min = (password.length >= min); 199 200 // Test: Groß- und Kleinschreibung 201 if (caseSensitive) 202 result.caseSensitive = (/(?=.*[a-z])(?=.*[A-Z])/.exec(password) != null); 203 204 // Test: Sonderzeichen 205 if (specials) 206 result.specials = (new RegExp(this.PasswordSpecialCharacters).exec(password) != null); 207 208 // Test: Zahl in Passwort 209 if (numbers) 210 result.numbers = (/\d/.exec(password) != null); 211 } 212 } 213 catch (e) 214 { 215 if (typeof(HRLogError) === 'function') 216 HRLogError(e); 217 } 218 219 return result; 220 } 221 222 /** 223 * Diese Funktion dekodiert jede Mail-Adresse die mit dem data-Attribut "data-encrypted" 224 * oder dem von uns eigens entwickelten Tag <hrencoded>...</hrencoded> versehen wurde. 225 * 226 * Letzteres dient insbesondere dazu die indizierten Mail-Adressen in der Volltextsuche zu kennzeichnen. 227 * Diese werden dekodiert indiziert, damit die Volltextsuche diese berücksichtigen kann, 228 * aber kodiert in den Quelltext geschrieben. 229 * 230 * Darüber hinaus werden Mail-Adressen, die im Linktext eines a-Tags vorkommen, ebenfalls mit dem Tag 231 * gekennzeichnet, damit diese hier leichter wieder dekodiert werden können. 232 */ 233 static decryptMailAddresses() 234 { 235 // Mailadressen im href von Links dekodieren. 236 Array.from(document.querySelectorAll("a[data-encrypted]")).forEach(match => { 237 if (match.href.indexOf('mailto:') >= 0) 238 { 239 const mailaddress = match.href.substring(match.href.indexOf('mailto:') + 7, match.href.length).trim(); 240 const decryptedMailaddress = this.hrDecrypt(mailaddress); 241 match.href = `mailto:${decryptedMailaddress}`; 242 match.removeAttribute('data-encrypted'); 243 if (mailaddress == match.innerHTML.trim()) 244 match.innerHTML = decryptedMailaddress; 245 } 246 }); 247 248 // Mailadressen im Tag <hrencrypted>...</hrencrypted> dekodieren. 249 Array.from(document.querySelectorAll("hrencrypted")).forEac
249h(match => { 250 const mailaddress = match.innerHTML.trim(); 251 const decryptedMailaddress = this.hrDecrypt(mailaddress); 252 253 // Das spezielle Tag wird durch die dekodierte Mail als Textknoten ersetzt. 254 match.parentNode.replaceChild(document.createTextNode(decryptedMailaddress), match); 255 }); 256 } 257 258 /** 259 * Erzeugt eine v4-UUID. 260 * 261 * Idee aus: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid?page=1&tab=scoredesc#tab-top 262 * 263 * @return eine v4-UUID oder die aktuellen Millisekunden seit 1.1.1970, falls ein Fehler bei der Generierung auftritt. 264 */ 265 static getUUID() 266 { 267 try 268 { 269 // Die Methode ist nur in sicherem Context, d.h. localhost:8080 270 // oder in Verbindungen über HTTPS nutzbar. Außerhalb dieses Contexts wird eine 271 // alternative Erzeugung gemacht. 272 if (typeof(Crypto.randomUUID) === 'function') 273 return Crypto.randomUUID(); 274 else 275 { 276 return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => 277 (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) 278 ); 279 } 280 } 281 catch (e) 282 { 283 if (typeof(HRLogError) === 'function') 284 HRLogError(e); 285 286 return Math.floor(Math.random() * 1000) + '-' + new Date().getTime(); // Schwacher Ersatz 287 } 288 289 return null; 290 } 291}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.