... sichere Web-Kommunikation

Um einen gesicherten Datenaustausch in Web-Applikationen, gerade zunehmend auch im Kontext von IoT (Internet of Thins), zu gewährleisten gilt es, einige Dinge zu beachten. Anhand der von mir entwickelten Applikation "Interrogato", eine Lernplattform, die es erlaubt Möglichkeiten zu nutzen, ohne Kurse in sog, Klassenräumen wahrnehmen zu müssen (Distance Learning) , um sich auf Prüfungen vorbereiten zu können. Hierfür stellt der "Interrogator" via Web-Services Lernmodule zur Verfügung, in denen Multiple-Choice-Fragen hinterlegt sind. Im Nachfolgenden werden hier die notwendigen Sachverhalte für eine gesicherte Web-Kommunikatiuon skizziert.

Die Datenkommunikation zwischen der in HTML und JAVASCRIPT geschriebenen Web-Applikation und dem Web-Server erfolgt mithilfe der üblichen HTTP-Methode POST zustandslos, d.h. dass der Web-Server keinen Kontext resp. Zustand (z. B. mithilfe von Cookies)über die anwendungsbezogeneInteraktion mit der Web-Applikation, und basiert auf einer Rest(Representational State Transfer)-Architektur, die auch unter der Bezeichnung RESTful API bekannt ist. Der Web-Server stellt sog. Web-Services in Form von Servlets zur Disposition – der verwendete Begriff "Servlets" ist hier nicht als JAVA-Klassen zu verstehen, sondern repräsentiert kleine in PERL geschriebene serverseitige Anwendungsroutinen, die innerhalb des Webservers Anfragen vom Client entgegennehmen und beantworten.

HINWEIS: Für die Veranschaulichung der jeweiligen Sacheverhalte werden sog. Code-Schnipsel sowohl in Javascript als auch in Perl aufgeführt, die leicht, da ohne Berücksichtigung des entsprechenden Kontextes, modifiziert dem Original-Programmcode entnommen wurden. Diese Code-Schnipsel unterliegen dem Copyright by HK Businessconsulting, Hamburg (www.hk-businessconsulting.de) Sie können unter den Bedingungen der GNU Lesser General Public License, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren werden, entweder gemäß Version 3 der Lizenz oder jeder späteren Version. Die Veröffentlichung dieser Code-Schnipsel erfolgt in der Hoffnung, daß sie von Nutzen sein werden, aber OHNE IRGENDEINE GARANTIE, sogar ohne die implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. Details sind zufinden in der GNU Lesser General Public License (siehe http://www.gnu.org/licenses/)

Die verwendete Technik, die auf Seiten des Cient zur Anwendung kommt, ist ein spezielles Formular mit zwei INPUT-Feldern (s. setfield ) i. V. m. zwei Variablen, dem HEADER und PAYLOAD resp. Header und Payload, die zusammen den Daten-Frame ausmachen. Während HEADER der Aufnahme von Steuerungsinformationen dient, werden im Feld PAYLOAD die Nutzdaten hinterlegt. HEADER und PAYLOAD werden vor Versenden in eine Json-Format-Zeichenkette und dann einen Base64-Code (btoa), d.h. die 8-BitBinärdaten werden in eine Zeichenfolge, die nur aus lesbaren, Codepage-unabhängigen ASCII-Zeichen besteht, umgewandelt.

Das Formular wird via Browser an den Webserver über HTTP mit einer POST-Anforderung für die Datenressource /cgi-bin/servrogator.pl abgsendet. Der Webserver erkennt, dass es sich um ein sog. CGI-Script handelt, und führt es aus. Die in den Feldern des Formulars befindlichen Daten stehen dem CGI-Script zur Verarbeitung zur Verfügung.

Javascript Codeschnipsel:

// (c) HK Businessconsulting, Hamburg 
  function setField (Form, Name)
  {
    var field = document.createElement("INPUT");  
    with (field)
    {
      type = "hidden";
      value = '';
      name = Name;
    }
    Form.appendChild(field); 
  }
  
  var Form;
  with (document.body.appendChild(Form = document.createElement("FORM")))
  {
    method = "POST";
    enctype="multipart/form-data";    acceptCharset="utf-8";   // ISO-8859-1 ISO-8859-15 utf-8 
    acceptCharset="utf-8";   // utf-8 
    action = "/cgi-bin/servrogator.pl";
    target = <Iframe-name> //The response is displayed in a named iframe;
    onsubmit=new Function('F',"return false;");
  }
  setField (Form, 'HEADER');
  setField (Form, 'PAYLOAD');
  
  send = function(Header, Payload)
  {  
    if (Header)
    {
      Header.value = btoa(Header,'');    
      Payload.value = Payload ? btoa(Payload,'') : '';
      Form.submit(); 
    }
    else 
      alert("Fatal Error");
  } 
  

Im Web-Server erfolgt die Datenübertragung auf ähnliche Art und Weise mit 2 Variablen ($Header und $Payload), nur dass hier anstelle des Formaulars eine generierte vom Client "angeforderte" HTML-Seite zur Anwendung kommt. Der Client resp. Browser empfängt die HTML-Seite und führt die via Formular an den Web-Server übergebene und die HTML-Seite eingebundene $Callee-Funktion aus, wodurch die Informationen im Header und Payload der Anwendung "Interrogator" zur Verfügung stehen.

Perl Codeschnipsel:

# (c) HK Businessconsulting, Hamburg 
sub sendFrame
{
  my $cgi = CGI->new();
  my ($Target, $Callee, $Header, $Payload) = @_;
  $Header = \encode_base64($Header,'');
  $$Payload = '' unless defined $Payload;
  $Payload = \encode_base64($$Payload,'');
  $| = 1; # flushes buffer STDOUT immediately
  print $cgi->header(-type=>'text/html',-expires=>'now'), $cgi->start_html(
                                        -dtd => '-//W3C//DTD HTML 4.01 Transitional//EN',
                                        -encoding => 'utf-8',
                                        -target => $Target,
                                      );
  print <<ENDSCRIPT;
  
ENDSCRIPT
  print $cgi->end_html;
  $| = 0;   
}

Für den symmetrisch Ende-zu-Ende-verschlüsselten Datenaustausch zwischen der im Browser laufenden Anwendungsinstanz "Interrogator" und der die Servlets bereitstellenden Instanz auf dem Web-Server "Servrogator" wird eine Verbindung resp. Sitzung zwischen Interrogator und Servrogator etabliert und anhand des sog.Diffie-Hellman-Schlüsselaustauschprotokolls, über das der für eine sichere Kommunikation geheime Sitzungsschlüssel vereinbart wird. Eine Ende-zu-Ende-Verschlüsselung wurde hier gewählt, da nicht immer eine auf Transport- resp. Netzwerkebene zugrundeliegende Verschlüsselung der Daten mittels SSL/TLS (Secure Sockets Layer/Transport Layer Security) durch Verwendung von z. B. HTTPS (Hypertext Transfer Protocol Secure) gegeben ist (z. B. in einem Intranet). Die Kommunikation zwischen Interrogator als Dienstbenutzer und Servrogator als Diensterbringer ist in der nachfolgenden Graphik mittels eines Ort-Zeit-Diagrammes und den Dienstprimitiven (auch Dienstelemente genannt) in Anlehnung an OSI (Open System Interconnection; s.auch ITU-T Recommendations X.210 ):

skizziert.

Hierfür ist eine nicht unbedingt geheime, aber sehr große Primzahl p identisch auf beiden Seiten (Interrogator und Servrogator) zu wählen. Gem. RFC3526 wird eine 6144 Bit große Primzahl p folgendermaßen bestimmt:

p := 26144-26080-1+26426014pi+929484
q (einfache Wurzel von p) sei auf beiden Seiten als sog. Generator von p mit 2 gegeben. So ergibt dies die hier genutzte Primzahl p der MODP Gruppe 17 folgendermaßen (s. RFC 3526 "MODP Diffie-Hellman groups for IKE", May 2003) (sedezimale Darstellung):
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
6DCC4024 FFFFFFFF FFFFFFFF

Beide Seiten generieren jeweils eine 16 Bit große Zufallszahl als ihren geheimen PrivateKey und ihren PublicKey dergestalt, dass

PublicKey1:= qPrivatekey1mod p
PublicKey2:= qPrivatekey2mod p

Anhand der PublicKeys und PrivateKeys wird auf beiden Seiten jeweils der gemeinsame und identische 6144 Bit große Sitzungsschlüssel (SessionKey), der der Verschlüsselung der Nutzdaten (Payloads) dient, gebildet:

SessionKey := Publickey1Privatekey2 mod p (auf Seiten des Servrogators)
SessionKey := Publickey2Privatekey1 mod p (auf Seiten des Interrogators)

Für die Generierung wurde auf Seiten des Client die Javascript-Bibliothek "BigInt.js" ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) und auf Seiten des Servers die Perl-Bibliothek "Math::BigInt" (https://perldoc.perl.org/Math::BigInt/) verwendet, um Zahlen größer als 253-1 auf beiden Seiten analog verarbeiten zu können.

Javascript Codeschnipsel:

// (c) HK Businessconsulting, Hamburg 

var GENERATOR = '2';  
var BITSIZE = 16;
var PrivateKey1 = [];
var PublicKey1 = [];
var SessionKey = [];
var Prime = [];
var Generator = [];
 
var getBigRandom = function(BitSize)
{
  var ByteSize = Math.floor(BitSize/8+0.999);
  var array = [];
  while (ByteSize--)
    array.push (Math.floor(Math.random()*255).toString(16)); 
  return array.join(""); 
} 

var PowMod = function (base, exponent, modulus)
{
  result = 1;
  base = base % modulus;
  while (exponent)
  {
    if (exponent % 2 == 1)
      result = (result * base) % modulus;
    exponent = exponent >>> 1;
    base  = (base * base) % modulus;
  }
  return result;
}  
    

Prime = str2bigInt(PRIME, 16, 0);
Generator = str2bigInt(GENERATOR, 16, 0);
PrivateKey1 = str2bigInt(getBigRandom(BITSIZE),16,0);
PublicKey1 = powMod(Generator, PrivateKey, Prime);

-----
-----

buildSessionKey = function (PublicKey)
{ 
  var str = bigInt2str(powMod(str2bigInt(PublicKey, 16, 0), PrivateKey, Prime),16);
  var length = str.length;
  var array = [];
  for (var i=0; i<length; i+=8)
    array.push ("0x"+str.substr(i, 8));
  return SessionKey = array.join(",");    
}
 
SessionKey = buildSessionKey(PublicKey2);

Perl Codeschnipsel:

# (c) HK Businessconsulting, Hamburg 

use Math::BigInt;

use constant 
{
  GENERATOR => '02',  
  BITSIZE   => 16,
};

my $PrivateKey2;
my $PublicKey2;
my $SessionKey;

sub getBigRandom($)
{
  my $ByteSize = int ($_[0]/8+0.999)-1;
  return \join ("", map (sprintf("%02x", rand(255)), 0 .. $ByteSize));
}  
      
sub BigPowMod              
{
  my ($base, $exponent, $modulus) = @_;
  return Math::BigInt->new("0x".$$base)->bmodpow(Math::BigInt->new("0x".$$exponent), Math::BigInt->new("0x".$$modulus))->as_hex();
}  

$PrivateKey2 = getBigRandom(BITSIZE);
$PublicKey2 = BigPowMod(\GENERATOR, $PrivateKey2, \$Prime);

----
----
sub buildSessionKey($$)
{
  my ($PrivateKey, $PublicKey) = @_; 
  my $Str = substr(BigPowMod($PublicKey, $PrivateKey, \$Prime),2);
  my $len = (length($Str)-1)/8;

  return join (",", map ("0x".substr( $Str, $_*8, 8), 0 .. $len));
}

$SessionKey = buildSessionKey($PrivateKey2,$PublicKey1);

Um den Erfolg eines sog. MITM(Man-In-The-Middle)-Angriff zu unterbinden, wird das Diffie-Hellman-Protokoll um eine Authentizierungsmöglichkeit erweitert. Auf beiden Seiten werden jeweils Signaturen, Signature1 auf der einen und Signature2 auf der anderen, gebildet. Hiefür nutzen beide Seiten ein gemeinsames Geheimnis, den Authenifizierer, der beim Laden der entsprechenden Interrrogator-Seite (interrogator.shtml) an den Client übergeben wird. Die Signaturen werden durch Konkatenation von Authentifier und jeweiligem PublicKey mit anschließender Einwegverschlüssselung mittels eines sicheren Hash-Algorithmus (SHA256) hergestellt und an den jeweiligen Kommunikationspartner übermittelt.

Signature1:= SHA256 (Authentifier  PublicKey1) 
Signature2:= SHA256 (Authentifier  PublicKey2) 

Der auf beiden Seiten erzeugte, identische Sessionkey dient i.V.m. einem Verschlüsselungsverfahren der Verschlüssselung der zu übertragenden Nutzdaten (Payload). Die Wahl viel auf das kryptographische Verfahren 3DES (Data Encryption Standard), dass sich Seinerzeit musste sich aufs Altenteil begeben musste und durch andere allg. anerkannte Verfahren wie z. B. AES (Advanced Encryption Standard) abgelöst wurde. Ausschlaggebend hierfür war, dass in zunehmendem Maße von maßgeblichen Institutionen eine Verwendung des DES-Algorithmus, aufgrund fehlender Sicherheitskriterien nicht mehr als opportun für eine Verschlüsselung erachtet wurde. Vorrangige argumentative Faktoren, die immer wieder ins Feld geführt wurden und werden, sind:

Um darzulegen, dass ein der am besten analysierten Verschlüsselungsverfahren. doch noch nicht kategorisch "zum alten Eisen" gehört, soll im Folgenden gezeigt werden, wie mit einfachen Modifikationen den o. g. Schwachpunken begegnet werden kann.

Der DES-Algorithmus gehört zur Klasse der Feistel-Chiffren; beim 3DES (Triple DES) wird zuerst jeder 64-Bit Datenblock Bimittels des DES-Algorithmus und dem Schlüssel K1chiffriert, dann mit K2 dechiffriert und abschließend mit K3 erneut chiffriert:

3DES K1, K2,K3, Bi=DES K3, DES-1 K2, DES K1, Bi  mit 1<i64  K1 K2 K3 K1

Die Entschlüsselung mit 3DES läuft im Wesentlichen analog in umgekehrter Reihenfolge ab:

3DES-1 K1, K2,K3, Bi = DES-1 (K1, DESK2, DES-1K3, Bi mit 1 < i   64  K1 K2 K3 K1

Für jeden 64-Bit-Block Bi wird eine sog. Initialpermutation IP durchgeführt und im Nachgang deren inverse Permutation IP-1, wobei:

IP-1IPBi= Bi 

Initialpermutation und deren inverse Permutation, die die ursprüngliche Reihenfolge der durch IP vertauschten Bits wieder herstellt, verbessern den Verschlüsselungsalgorithmus in Hinblick auf Sicherheit nicht, sondern erhöhen nur den Aufwand für die Implementation sowie den für die Ver- und Entschlüsselung, wodurch eine gleichzeitige Entfernung dieser beiden Permutationen opportun erscheint.

Für die Ver- resp. Entschlüsselung eines 64-Bit-Blocks wird die Feistelchiffre 16 mal durchlaufen. Hierfür werden über ein dem DES-Algorithmus immanenten Verfahren aus den 3 zur Anwendung kommenden Teilschlüsseln K1,K2 und K2 jeweils 16 sog. Rundenschlüssel generiert. Hieraus ist klar, dass nur die 3 Teilschlüssel K1,K2 und K2 mit einer Schlüssellänge von jeweils 56 Bits zur Sicherheit beitragen; der so resultierende Schlüsselraum beträgt:  256×256×256=2168 möglicher Schlüsselkombinationen

Bei einem Meet-in-the-middle-Angriff liegt genau hier, so die landläufige Meinung, die große Schwäche hinsichtlich der Schlüssellänge und damit bzgl. des Schlüsselraumes des 3DES-Algorithmus, denn der Meet-in-the-middle-Angriff greift zwei Schritte des Verfahrens einzeln an und dann werden in einem dritten Schritt, der revers ausgeführt wird, die Ergebnisse verglichen. Nota bene: dem Angreifer, muss,um den Angriff durchführen zu können, sowohl der Plaintext wie auch das Chiffrat bekannt sein!

Ein Angreifer entschlüsselt zunächst einen chiffrierten Block Ci 256 mal mit den 256  möglichen Schlüsselkombinationen des Teilschlüssels K3 und speichert die Ergebnisse Bj' zusammen mit dem jeweils korrelierenden Teilschlüssel K3j ab:

Bj'=DES-1K3j,Ci mit 1j256

Dann werden die 2112 (256×256) Zwischenergebnisse

Zji=DESK2j-1DESK1jPi mit 1j256
mit den jeweiligen Schlüsselkombinationen K1j und K2j (256×256) produziert und jeweils mit den korrelierenden gespeicherten Bj' verglichen. Sobald er eine Übereinstimmung gefunden wird, sind die 3 Teilchlüssel bekannt. Der Gesamtaufwand für das Finden liegt somit also nur bei 256×256+2562112 Transformationen.

Eine Möglichkeit den Schlüsselraum signifikant zu erhöhen, ohne den eigentlichen Ver- resp. Entschlüsselungsalgorithmus zu verändern, ist die Verwendung unabhängiger Rundenschlüssel; d.h. statt die 16 48-Bit-Rundenschlüssel aus einem 56-Bit-Schlüssel K zu generieren, werden die Rundenschlüssel willkürlich gewählt. Dies führt bei 3 Teilschlüsseln mit jeweils 768 Bit (16 x 48 Bit) zu einem rechnerischen Schlüsselraum mit der Größe von  22304  Schlüsseln. Da aber die Sicherheit des hier maßgeblich von der sog. Meet-In-The-Middle-Attacke bestimmt wird, reduziert sich der effektive, d.h. sicherheitsrelevante Schlüsselraum auf ungefähr  21536  (21536 + 2768 21536). Gefordert ist also ein gesamter, aus 3x16 48-Bit-Rundenschlüssel gebildeter Gesamtschlüssel mit der Mindestlänge von 2304 Bit (16×48×3=2304). Als Resultat während des Verbindungsaufbau wurde ein ausreichend großer Sessionkey (6144 Bit) generiert, der hier zur Verschlüsselung der Nutzdaten verwendet wird.

In diesem Zusammenhang sei kurz erwähnt, dass eine Verwendung von 4 Teilschlüsseln den Schlüsselraum nicht signifikant erhöhen würde, da auch hier die Meet-In-The-Middle-Attacke dazu führen würde, dass sich der sicherheitsrelevante Schlüsselraum nur auf  21537  (21536 + 21536). belaufe. Erst eine Verwendung von 5 Teilschlüsseln würde den Schlüsselraum signifikant auf ungefähr  23072  (23072 + 21536 23072) erhöhen.

Die Blockgröße des DES-Algorithmus beträgt 64 Bit; d.h. ein zu verschlüsselnder resp. zu entschlüsselnder Text wird in Blöcke zu jeweils 64 Bit zerlegt und jeder Block wird mit demselben vorgegebenen Schlüssel sowohl chiffriert als auch dechiffriert. Im Betriebsmodus ECB (Electronic Code Book) führt diese Vorgehensweise dazu, dass Blöcke eines Textes mit identischem Inhalt zu einem identischen Chiffrat und damit zu einer eingeschränkten Sicherheit führen. Der hier verwendete Betriebsmodus CBC (Cipher Block Chaining) hingegen versucht, die Sicherheitsdefizite des ECB-Modus zu reduzieren, indem das Chiffrat, also der chiffrierte Block, mit dem nächsten zu verschlüsselnden Klartextblock über eine Exklusiv-Oder-Funktion verknüpft wird und somit die Blöcke miteinander verkettet werden:

Ci=DESK3,DES-1K2,DESK1,PiCi-1

Um den ersten Block zu chiffrieren wird, da kein aktulles Chiffrat vorliegt, ein sog. Initialvektor (IV) eingeführt, der beiden Seiten bekannt sein muss. Dieser IV wird mit dem 1. zu verschlüsselnden Block mittels einer Exklusiv-ODER-Funktion verknüpft:

C1=DESK3,DES-1K2,DESK1,P1IV

Um ein größere Blockgröße nachzuempfinden und den Schwachpunkt des 3SDES-Algorithmus, nämlich der der zu geringen Blockgröße, entgegenzuwirken, werden 2 konsekutive Blöcke logisch zusammengefasst und mit 6 unabhängigen Teilschlüsseln verschlüsselt. Hierdurch resultiert eine Blockgröße von 128 Bits, wodurch sich die Gesamtschlüssellänge auf 4608 Bit (16×48×6=4608) verdoppelt, mit einem Schlüsselraum von 24608; auch diese geforderte Länge liefert der während des Verbindungsaufbaus generierter Sessionkey von 6144 Bit. Gleichzeitig liefert dieser Schlüssel den für den CBC-Betriebsmodus notwendigen Inititalvektor. Denn nach Verbrauch von 4608 Bit für den Schlüssel für die Verschlüsselumg verbleiben noch 1536 Bit, d.h. 48 mögliche Initialvektoren (1536 Bit/64 Bit). Mit oben aufgeführten Modifikationen wird aus dem 3DES-Algorithmus der ADES(Advanced Data Encryption Standard)-Algorithmus, bei dessen Implementierung die Vorlage der BSD-lizensierten DES-Algorithmus-Implementation von Paul Tero (Paul Tero, July 2001 http://www.tero.co.uk/des) sowie die Implementation des SSL-Pakets von Eric Young (Eric Young 1997 https://ftp.nluug.nl/security/coast/libs/libdes/) sehr inspirierend war.

Javascript Codeschnipsel:

// (c) HK Businessconsulting, Hamburg 

function ADES (SessionKey, message, encrypt, iv) 
{ 
  var cbcleft = iv[0];
  var cbcright = iv[1];
  var keys = SessionKey.split(","); 
  var keys_len = SessionKey.length;
  var m=0, pass=0;
  var len = message.length;
  var looping = encrypt ? [[[64, 2], [62, -2], [0, 2]], [[128, 2], [126, -2], [64, 2]]] : [[[30, -2], [32, 2], [94, -2]], [[94, -2], [96, 2], [158, -2]]];
  message += "\0\0\0\0\0\0\0\0"; //pad the message out with null bytes
  var result = [];
  

  var SPtrans_1 = [0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004];
  var SPtrans_2 = [0x80108020,0x80008000,0x8000,0x108020,0x100000,0x20,0x80100020,0x80008020,0x80000020,0x80108020,0x80108000,0x80000000,0x80008000,0x100000,0x20,0x80100020,0x108000,0x100020,0x80008020,0,0x80000000,0x8000,0x108020,0x80100000,0x100020,0x80000020,0,0x108000,0x8020,0x80108000,0x80100000,0x8020,0,0x108020,0x80100020,0x100000,0x80008020,0x80100000,0x80108000,0x8000,0x80100000,0x80008000,0x20,0x80108020,0x108020,0x20,0x8000,0x80000000,0x8020,0x80108000,0x100000,0x80000020,0x100020,0x80008020,0x80000020,0x100020,0x108000,0,0x80008000,0x8020,0x80000000,0x80100020,0x80108020,0x108000];
  var SPtrans_3 = [0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200];
  var SPtrans_4 = [0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080];
  var SPtrans_5 = [0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100];
  var SPtrans_6 = [0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010];
  var SPtrans_7 = [0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002];
  var SPtrans_8 = [0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000];

  
  while (m <len) 
  {
    var temp, temp2, cbcleft2, cbcright2,
        left=((message.charCodeAt(m++)<<24)|(message.charCodeAt(m++)<<16)|(message.charCodeAt(m++)<<8)|message.charCodeAt(m++))&0xffffffff, 
        right=((message.charCodeAt(m++)<<24)|(message.charCodeAt(m++)<<16)|(message.charCodeAt(m++)<<8)|message.charCodeAt(m++))&0xffffffff; 

    if (encrypt) 
      left^=cbcleft, right^=cbcright;
    else 
      cbcleft2=cbcleft, cbcright2=cbcright, cbcleft=left, cbcright=right;
    var iterations = 3;
    while (iterations--)
    {
      var idx=looping[pass][iterations][0], inc=looping[pass][iterations][1], rounds = 16;
      //now go through and perform the encryption or decryption  
      while (rounds--)
      { 
        var right1=(right^(keys[idx % keys_len])), right2 = ((right >>> 4) | (right << 28)) ^ keys[(idx+1) % keys_len];
        temp=left, left=right, right=temp^(SPtrans_2[(right1>>>24)&0x3f]|SPtrans_4[(right1>>>16)&0x3f]|SPtrans_6[(right1>>>8)&0x3f]|SPtrans_8[right1&0x3f]
                                          |SPtrans_1[(right2>>>24)&0x3f]|SPtrans_3[(right2>>>16)&0x3f]|SPtrans_5[(right2>>>8)&0x3f]|SPtrans_7[right2&0x3f]);
        idx+=inc;         
      }
      temp=left, left=right, right=temp; //unreverse left and right
    }   
    pass = pass ? 0 : 1;  
    if (encrypt) 
      cbcleft=left, cbcright=right;
    else 
      left^=cbcleft2, right^=cbcright2;
    
    result.push(String.fromCharCode ((left>>>24), ((left>>>16) & 0xff), ((left>>>8) & 0xff), (left & 0xff), (right>>>24), ((right>>>16) & 0xff), ((right>>>8) & 0xff), (right & 0xff)));

  }

  return result.join("");
}

Perl Codeschnipsel:

# (c) HK Businessconsulting, Hamburg 

sub ADES 
{
  my ($SessionKey, $message, $encrypt, @iv)=@_;   
  my @keys = split(',', $$SessionKey);
  my @msg = unpack 'N*', $$message ."\0\0\0\0\0\0\0\0"; #pad the message out with null bytes
  my ($keys_len, $msg_len, $m) = (scalar @keys, scalar @msg, 0);
  my ($cbcleft, $cbcright) = ($iv[0], $iv[1]);
  my $pass = 0;
  my @result = ();

  my @SPtrans_1 = (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004);
  my @SPtrans_2 = (0x80108020,0x80008000,0x8000,0x108020,0x100000,0x20,0x80100020,0x80008020,0x80000020,0x80108020,0x80108000,0x80000000,0x80008000,0x100000,0x20,0x80100020,0x108000,0x100020,0x80008020,0,0x80000000,0x8000,0x108020,0x80100000,0x100020,0x80000020,0,0x108000,0x8020,0x80108000,0x80100000,0x8020,0,0x108020,0x80100020,0x100000,0x80008020,0x80100000,0x80108000,0x8000,0x80100000,0x80008000,0x20,0x80108020,0x108020,0x20,0x8000,0x80000000,0x8020,0x80108000,0x100000,0x80000020,0x100020,0x80008020,0x80000020,0x100020,0x108000,0,0x80008000,0x8020,0x80000000,0x80100020,0x80108020,0x108000);
  my @SPtrans_3 = (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200);
  my @SPtrans_4 = (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080);
  my @SPtrans_5 = (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100);
  my @SPtrans_6 = (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010);
  my @SPtrans_7 = (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002);
  my @SPtrans_8 = (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000);

  my @parameter = $encrypt ? ([[64, 2], [62, -2], [0, 2]], [[128, 2], [126, -2], [64, 2]]) : ([[30, -2], [32, 2], [94, -2]], [[94, -2], [96, 2], [158, -2]]);


  while ($m < $msg_len) 
  {
    my ($temp, $temp2, $cbcleft2, $cbcright2);
    my ($left, $right) = ($msg[$m++], $msg[$m++]);

    if ($encrypt) 
    {
      $left^=$cbcleft; $right^=$cbcright;
    } 
    else 
    {
      ($cbcleft2, $cbcright2, $cbcleft, $cbcright) = ($cbcleft, $cbcright, $left, $right); 
    }
    my $iterations = 3;
    while ($iterations--)
    {
      my ($idx, $inc, $rounds) = ($parameter[$pass][$iterations][0], $parameter[$pass][$iterations][1], 16); 
      while ($rounds--)
      {
        my ($right1,$right2)=(($right^hex $keys[$idx % $keys_len]), ((($right>>4)|($right<<28))^hex $keys[($idx+1) % $keys_len]));
        ($left, $right)=($right, $left^($SPtrans_2[($right1>>24)&0x3f]|$SPtrans_4[($right1>>16)&0x3f]
                                      |$SPtrans_6[($right1>>8)&0x3f]|$SPtrans_8[$right1&0x3f]
                                      |$SPtrans_1[($right2>>24)&0x3f]|$SPtrans_3[($right2>>16)&0x3f]
                                      |$SPtrans_5[($right2>>8)&0x3f]|$SPtrans_7[$right2&0x3f]));
        $idx+=$inc;          
      }
      ($left, $right) = ($right, $left);
    }
    $pass = $pass ? 0 : 1;  

    if ($encrypt) 
    {
      ($cbcleft, $cbcright) = ($left, $right);
    } 
    else 
    {
      $left^=$cbcleft2; $right^=$cbcright2;
    }

    push (@result,pack('N*',($left, $right)));  
  }  
  return \join("",@result);
}

Literatur:

[Andrew Nash et. al. 2002]: PKI - E-security implementieren; mitp-Verlag, Bonn 2002, ISBN 3-8266-0781-3
[BSI 2021a]: Technische Richtlinie 02102-1 Kryptographische Verfahren: Empfehlungen und Schlüssellängen; Bundesamt für Sicherheit in der Informationstechnik 24. März 2021
[BSI 2021b]: Technische Richtlinie TR-02102-3 Kryptographische Verfahren: Empfehlungen und Schlüssellängen - Teil 3 – Verwendung von Internet Protocol Security (IPsec) und Internet Key Exchange (IKEv2) - Version 2021-01; Bundesamt für Sicherheit in der Informationstechnik 2021
[Helmut Kerner 1989]: Rechnernetze nach ISO-OSI, CCITT, Prentice-Hall, Wien 1989, ISBN 3-900934-10-X
[William Stallings 1995]: Sicherheit im Datennetz, Prentice-Hall, München 1995, ISBN 3-930436-29-9