, aujourd'hui, de Blog, il est, comme promis, "down the Rabbit Hole" et nous traitons avec les Rouages de la MiFare Badges Integrated Contactless Cards (PICC) Série. Nous partons de l'Particularités, et de regarder et les Fonctions en détail.
De nombreux Card Reader- Projets, avec le MF522 Chipset comme Base nous l' Arduino que j'ai personnellement déjà vu, utilisez malheureusement ni la Sécurité, ni dans les Possibilités de ce que nous Mifare Classic Carte de Fonctionnalités.
Ces Projets se limitent à l'librement pour chaque Lecteur de cartes et Téléphones lisible ID Unique universel (UUID) de la Carte et une Liste des permis de l'UUID contre à vérifier. Est-ce inclus dans la Liste, la Carte est considérée comme valide.
Dans nos Parties précédentes , nous faire de ce Principe d'Utilisation. C'est ce que nous avons aujourd'hui, notre Blog de modifier et de Données sécurisés sur notre MiFare Classic Carte.
Par ce Projet partagé de Type "MiFare Classic" (s'il vous plaît également à cette Remarque), nous pouvons propre, de nous donnée des Informations, à partir de la Carte mémoire et contre non autorisée de lire ou de modifier les protéger.
Pour ce faire, le Fabricant a déjà sur la Carte 16 Secteurs (0-15) 4 * 16 Octets présents dans lesquelles, à l'Exception du Secteur 0 , 3 * 16 Octets libres peuvent être décrits. 16 Octets de chaque Secteur, Secteur Trailer appelé et pour le Rangement des 2 Sektorenschlüssel et Zugriffsmatrixdefinition utilisé.
Les 16 Octets d'un Secteur Trailer sont réparties comme suit:
6 Octets - premier Sektorenschlüssel
6 Octets - deuxième Sektorenschlüssel
4 Octets - Zugriffsberechtigungsdefinition
Nous voulons sur notre Carte maintenant le Nom et le Prénom de nous un mot de passe crypté. Pour ce faire, nous définissons un Secteur (1-15) sur la Constante ... pour nous et pour ce faire, utilisez le souhaitez. J'ai dans le Code Sektorenblock 1 est sélectionné.
Le Prénom est prévu au second 16 Octets de la Zone, et le Nom de famille dans le troisième 16 Octets Champ. La première de 16 Octets Champ est réservé pour des Extensions ultérieures.
Qui avons-nous besoin d'une Schlüsseldefinition, nous avons dans la Structure MiFareClassicKeyTable déposer.
S'il vous plaît modifier ce Bloc (2x 6 Octets) lors de la Réplique nécessairement avec salle de Clé et les stocke à vous dans un Endroit sûr, sinon n'importe qui, ce Projet connaît dans le Code en Clair, ladite Clé de votre Carte lire et propre valide! Cartes pour votre contrôle de périphérique peut créer.
Petit Spoiler: en plus, nous, ce matériel de base lors de la Suite de la Série à nouveau besoin. Merci de continuer à respecter, de la Carte décrit et d'options de change. Ainsi, la Carte est peut-être pas pour d'autres Utilisations utilisable!
Nous invitons, après Modification, du matériel de clé suivants Code sur nos ESP élevé:
#include <SPI.h> #include <MFRC522.h> #include <ESP8266WiFi.h> //#include <WiFiClient.h> #include <ESP8266WebServer.h> #include <ESP8266mDNS.h> #include <EEPROM.h> #include <FS.h> // Include the SPIFFS library #define RST_PIN 5 // SPI Broche Reset (D1 Sortie) #define RELAIS_PIN 16 // Relais (D0 Sortie) [Active LOW] - Internes LED, à proximité du Port USB #define SS_PIN 15 // SPI Slave Select Pin #define RGBLED_R 2 // Rouge (D4 Sortie) #define RGBLED_G 0 // Vert (D3 Sortie des internes LED sur le Module ESP #define RGBLED_B 4 // Bleu (D2 Sortie) #define WiFiPwdLen 25 // Maximum de longueur de mot de passe WiFi #define STANameLen 20 // Maximale WiFi SSIDlänge #define ESPHostNameLen 20 // Nombre Maximal de Caractères ESPHostName #define KEYA true // Définition du drapeau PICC #define KEYB faux // Définition du drapeau PICC #define LED_BUILTIN 16 #define PIN_WIRE_SDA 4 #define PIN_WIRE_SCL 5 #define USED_Sector 1 // Secteur de carte utilisé pour les données d'authentification et de configuration (valide: 1 à 15) ADC_MODE(ADC_TOUT); // Configurez l'entrée analogique A0 sur externe. ADC_TOUT (pour tension externe), ADC_VCC (pour tension système). MFRC522 mfrc522(SS_PIN, RST_PIN); // Créer une instance du MFRC522 MFRC522::MIFARE_Key clé; ESP8266WebServer serveur(80); // Créer une instance de serveur Web struct WiFiEEPromData { char ESPHostName[ESPHostNameLen]; char APSTAName[STANAMELEN]; // Nom de la STATION à connecter, si défini char WiFiPwd[WiFiPwdLen]; // WiFiPAssword, si défini char ConfigValid[3]; // Si Config est Vaild, le tag "TK" est requis " }; struct MiFareClassicKeyTable { octet Key_A[6] = {0x22,0x44,0xFA,0xAB,0x90,0x11}; // Veuillez changer la clé de la carte PICC. octet Key_B[6] = {0xFE,0xE1,0xAA,0x3D,0xDF,0x37}; // Veuillez changer la clé de la carte PICC. char ConfigValid[3]; // Si Config est Vaild, le tag "TK" est requis " }; MiFareClassicKeyTable MiFareClassicKey; WiFiEEPromData MyWiFiConfig; // Variables utilisées mondialement bool Résultat = faux; bool LearnNewCard = faux; bool EraseCard = faux; bool ExpirationDateActive = faux; String Nom de famille; String Givenname; String ExpirationDate; String temp; non signé longue SessionID; non signé longue PCD_ServiceCall_Handler = 0; non signé longue PCD_WatchDog_Handler = 0; uint8_t DataBuffer[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; nul configuration() { pinMode(RST_PIN,SORTIE); digitalWrite(RST_PIN,ÉLEVÉ); pinMode(RELAIS_PIN,SORTIE); pinMode(RGBLED_R,SORTIE); pinMode(RGBLED_G,SORTIE); pinMode(RGBLED_B,SORTIE); digitalWrite(RELAIS_PIN,ÉLEVÉ); // relais inactif SetRGBLed(255,0,255,faux); // La couleur LED pourpre commence l'initialisation Série.commencer(115200); // Initialise la communication série avec le PC avec 115200 bauds Série.println(""); temp = "ATSN:"+ String(ESP.getChipId()); Série.println(temp); // Serial.print ("Valeur ADC:"); Serial.println (analogRead (A0)); SPI.commencer(); // Initialiser la communication SPI PCDHardReset(); SessionID = millis(); Résultat = startWiFiClient(); InitalizeHTTPServer(); SetRGBLed(0,0,255,faux); // L'initialisation de la couleur bleue de la LED est terminée //ESP.wdtEnable(WDTO_4S); // Démarrer le chien de garde } // ******************* Démarrer les fonctions d'aide / d'optimisation ************************ ******** nul SetRGBLed(octet RedValue,octet GreenValue,octet BlueValue,booléen SlowFade) // Fonction de contrôle de la led RGB { digitalWrite(RGBLED_R,FAIBLE); digitalWrite(RGBLED_G,FAIBLE); digitalWrite(RGBLED_B,FAIBLE); si (RedValue == 255) { digitalWrite(RGBLED_R,ÉLEVÉ); } si (GreenValue == 255) { digitalWrite(RGBLED_G,ÉLEVÉ); } si (BlueValue == 255) { digitalWrite(RGBLED_B,ÉLEVÉ); } } // ******************* Stop Helper / Fonctions d'optimisation ************************ ********* // ******************* Démarrer les fonctions Serveur Web ************************** ***************** // Les routines de base des cookies sont basées sur un extrait de GIT: //https://github.com/esp8266/ESPWebServer/blob/master/examples/SimpleAuthentification/SimpleAuthentification.ino bool is_authentified() { si (serveur.hasHeader("Cookie")){ // Cookie trouvé temp = serveur.en-tête("Cookie"); //Serial.println(temp); String SessionStr = String(ESP.getChipId()) + "=" + String(SessionID); rendement(); si (temp.indexOf(SessionStr) != -1) { // Authentification Web réussie temp = ""; retour vrai; } } // Échec de l'authentification Web temp = ""; SessionID = millis(); retour faux; } nul handleLogin(){ String msg; // String cookie = server.header ("Cookie"); //Serial.println(cookie); si (serveur.hasArg("DÉCONNECTER")){ // Utilisateur de déconnexion; serveur.sendHeader("Emplacement","/login"); server.sendHeader("Cache-Control","no-cache"); SessionID = millis(); temp = String(ESP.getChipId()) + "= NA ; HttpOnly ; SameSite=Strict"; server.sendHeader("Set-Cookie",temp); temp = ""; serveur.send(301); yield(); return; } if (serveur.hasArg("USERNAME") && server.hasArg("PASSWORD")){ temp = String(ESP.getChipId()); if (serveur.arg("USERNAME") == , "admin" && server.arg("PASSWORD") == temp ){ serveur.sendHeader("Location","/"); server.sendHeader("Cache-Control","no-cache"); SessionID = millis(); temp = String(ESP.getChipId()) + "=" + String(Idsession) + "; HttpOnly ; SameSite=Stricte"; serveur.sendHeader("Set-Cookie",temp); temp = ""; serveur.envoyer(301); rendement(); de retour; } msg = "<script>alert('Falscher Benutzername oder falsches mot de passe !');</script>"; } CSS_Header_Template(); rendement(); temp = "<head><title>Login</title></head><body><DIV ALIGN=CENTER>"; serveur.sendContent(temp); temp = "<h2> " Anmeldung un Kartenleser RC522</h2><corps><br><br><table border=0 bgcolor=black><tr><th><DIV ALIGN=RIGHT>"; serveur.sendContent(temp); temp = "<form action='/login' method='post'>Benutzername: <input type=text Name='nom d'utilisateur' Size=17 required><br>"; serveur.sendContent(temp); temp = "mot de passe: <input type=password Name= "PASSWORD" Size=17 required><br><br><br><button type= "submit""; serveur.sendContent(temp); temp = "nom='Login_Button' value='1' style='height: 30px; width: 100px' >Login</bouton><br></th></tr></form></DIV></table>"; serveur.sendContent(temp); temp = "<br><SMALL>Damit die " Anmeldung funktioniert, sind Cookies für diese Webseite zu erlauben.</SMALL>"; serveur.sendContent(temp); temp = msg + "</DIV></body></HTML>"; serveur.sendContent(temp); temp = ""; } vide handleNotFound() { SessionID = millis(); temp = "Page nicht gefunden.\n\n"; temp += "URI: "; temp += serveur.uri(); temp += "\nMethod: "; temp += (serveur.méthode() == HTTP_GET)?"GET",:"POST"; temp += "\nArguments: "; temp += serveur.args(); temp += "\n"; pour (u_int8_t je=0; je<serveur.args(); je++){ temp += " " + serveur.argName(je) + ": " + serveur.arg(i) + "\n"; } rendement(); serveur.envoyer(404, "text/plain", temp); temp = ""; } vide handleErasePICC() { si (!is_authentified()) { serveur.sendHeader("Emplacement","/login"); serveur.sendHeader("Cache-Control","no-cache"); serveur.envoyer(301); rendement(); de retour; } CSS_Header_Template(); rendement(); temp = "<head><title>Kartenleser RC522</title></head><body>"; serveur.sendContent(temp); HtmlNavStructure(); temp = "<script>alert('Bitte JETZT die zu löschende Karte vor den Leser halten!');</script>"; serveur.sendContent(temp); rendement(); SetRGBLed(0,255,255,false); //Led Couleur cyan Programmierungsmodus EraseCard = vrai; temp = "</body></html>"; serveur.sendContent(temp); serveur.client().arrêter(); temp = ""; } vide handleNewPICC() { si (!is_authentified()) { serveur.sendHeader("Emplacement","/login"); serveur.sendHeader("Cache-Control","no-cache"); serveur d'.envoyer(301); de retour; } si (serveur.hasArg("Nom de famille") && serveur.hasArg("Givenname")) { Nom = du serveur.arg("Nom de famille"); Givenname = serveur.arg("Givenname"); ExpirationDate = serveur.arg("ExpDate"); si (serveur.hasArg("ExpDateOption")) { ExpirationDateActive = vrai; } d'autre { ExpirationDateActive = faux; } temp = "<script>alert('Bitte JETZT die neue Karte vor den Leser halten!');</script>"; serveur.sendContent(temp); SetRGBLed(255,255,0,false); //Led Couleur Gelb Programmierungsmodus LearnNewCard = vrai; rendement(); de retour; } CSS_Header_Template(); rendement(); temp = "<head><title>Kartenleser RC522</title></head><body>"; serveur.sendContent(temp); HtmlNavStructure(); temp = ""; temp = "<br><br><br><br><table border=0 ALIGN=CENTER><th>"; serveur.sendContent(temp); temp = "<table border=1 bgcolor = black><form action='/newPICC' method='post'>"; serveur.sendContent(temp); temp = "<tr><th>Karteninhaber:<br><div ALIGN=RIGHT>"; serveur.sendContent(temp); temp = "Vorname: <input type=text Name='Nom' Size=17 maxlenght=16 espace réservé='Max' required><br>"; serveur.sendContent(temp); temp = "Nachname: <input type=text Name='Prénom' Size=17 maxlenght=16 placeholder='Smith' required><br>"; serveur.sendContent(temp); temp = "</div></th><th>Kartenmetadaten:<br><DIV ALIGN=RIGHT>"; serveur.sendContent(temp); temp = "<input Name='ExpDateOption' TYPE=checkbox VALUE=1 >Ablaufdatum:<input type= "date" Name='ExpDate' Size = 17 >"; serveur.sendContent(temp); temp = "<br><th><tr><th></table><br>"; serveur.sendContent(temp); temp = "<button type='submit' name='NewCard' value='1' style='height: 30px; width: 200px' >Neue carte à puce erstellen</bouton>"; serveur.sendContent(temp); temp = "<br / ></form></tr></th></table>"; serveur.sendContent(temp); temp = "</body></html>"; serveur.sendContent(temp); serveur.client().arrêter(); de rendement(); temp = ""; } vide handleRoot() { si (!is_authentified()){ serveur.sendHeader("Emplacement","/login"); serveur.sendHeader("Cache-Control","no-cache"); serveur d'.envoyer(301); de retour; } // Contenu HTML CSS_Header_Template(); rendement(); temp = "<head><title>Kartenleser RC522</title></head><body>"; serveur.sendContent(temp); HtmlNavStructure(); temp = "<div ALIGN=CENTER><br><br><br><br><BIG>Willkommen auf der Smartkartenleser RC522 Site.</BIG><br>"; serveur.sendContent(temp); temp = "Resetgrund: " + Chaîne(ESP.getResetReason()) + "<br>"; serveur.sendContent(temp); temp = "Freier Heapspeicher: " + Chaîne(ESP.getFreeHeap()) + " Octets<br>"; serveur.sendContent(temp); temp = "Int. Flash: " + Chaîne(ESP.getFlashChipRealSize()) + " Octets<br>"; serveur.sendContent(temp); Résultat = mfrc522.PCD_PerformSelfTest(); mfrc522.PCD_Init(); // Initialisiere MFRC522 Lesemodul mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max); // Setzt Antenne auf max. Empfang mfrc522.PCD_AntennaOn(); de rendement(); si le (Résultat) {temp = "RC522 PCD-Statut: OK<br>"; } d'autre {temp = "RC522 PCD-Statut: Fehler!<br>"; } serveur.sendContent(temp); temp = CPU ID: " + Chaîne(ESP.getChipId()) + " @ " + Chaîne(ESP.getCpuFreqMHz()) + " MHz<br>"; serveur.sendContent(temp); temp = "<br / >Sie sind erfolgreich angemeldet !<br><br><form action='/login' method= "get">"; serveur.sendContent(temp); temp = "<button type='submit' name='DÉCONNEXION' value='OUI' style='height: 30px; width: 200px' >Déconnexion</bouton>"; serveur.sendContent(temp); temp = "</form></div></body></html>"; serveur.sendContent(temp); si (serveur.hasArg("Reboot") ) // Redémarrage du Système { //ESP.wdtFeed(); ESP.wdtDisable(); temp = "<script>alert('Das System startet JETZT neu.');</script>"; serveur.sendContent(temp); serveur.client().arrêter(); de rendement(); temp = ""; ESP.réinitialiser le(); délai(4000); } de serveur de.client().arrêter(); temp = ""; } vide CSS_Header_Template() // Formatvorlage für alle internen ESP de nos sites web. https://wiki.selfhtml.org/wiki/CSS { serveur.setContentLength(CONTENT_LENGTH_UNKNOWN); temp = ""; serveur.envoyer (200, "text/html", temp); temp = "<!DOCTYPE HTML PUBLIC "- /- //W3C//DTD HTML 4.01 Transitional//EN "><html lang= " de " ><meta charset='UTF-8'>"; serveur.sendContent(temp); temp = "<style type='text/css'>*{margin: 0;padding: 0;}body{background:black;color:darkorchid;font-size: 16px;"; serveur.sendContent(temp); temp = "font-family: sans-serif,arial;}.nav{width: 1300px;height: 30px;margin: 0 auto;border-radius: 5px;}"; serveur.sendContent(temp); temp = "ul li{list-style: none;width: 200px;line-height: 60px;position: relative;fond: darkorchid;"; serveur.sendContent(temp); temp = "box-shadow: 0px 2px 5px 0px gris;text-align: center;float: left;background-color: #010000;}ul li ul{"; serveur.sendContent(temp); temp = "position: absolute;}.nav > ul > li:nth-of-type(1){border-radius: 5px 0px 0px 5px;}.nav > ul > li:nth-of-type(5)"; serveur.sendContent(temp); temp = "{border-radius: 0px 5px 5px 0px;}ul li a{color: rgb(182, 18, 18);width: 200px;height: 58px;display: inline-block;"; serveur.sendContent(temp); temp = "text-decoration: none;}ul li a:hover{font-weight: bold;border-bottom: 2px solid #fff;}ul li ul{display: none;}"; serveur.sendContent(temp); temp = ".nav ul li:hover ul{display: block;}.fa{margin-right: 5px;}.conteneur{width: 1000px;height: 200px;"; serveur.sendContent(temp); temp = "margin: 0 auto;padding:20px 20px;}@media screen and (max-width: 480px){header{width: 100%;}"; serveur.sendContent(temp); temp = ".nav{display: none;width: 100%;height: auto;}ul li{width: 100%;float: none;}ul li a{width: 100%;"; serveur.sendContent(temp); temp = "display: block;}ul li ul{position: static;}ul li ul li a{background: #222;}.fa-liste.modifier{display: block;}"; serveur.sendContent(temp); temp = ".conteneur{width: 100%;height: auto;}body{overflow-x:hidden;}}</style>"; serveur.sendContent(temp); temp = ""; } vide HtmlNavStructure() { temp = "<div class= "menu" ><nav class='nav'><ul>"; serveur.sendContent(temp); temp = "<li><a href='#'>Système</a>"; serveur.sendContent(temp); temp = "<ul><li><a href="/fr">d'Information</a></li>"; serveur.sendContent(temp); temp = "<li><a href="/fr/?Reboot=YES">Neustart</a></li>"; serveur.sendContent(temp); temp = "</ul>"; serveur.sendContent(temp); temp = "</li><li><a href='#'>CCIP</a>"; serveur.sendContent(temp); temp = "<ul><li><a href="/fr/newPICC">Neue Karte erstellen</a></li>"; serveur.sendContent(temp); temp = "<li><a href="/fr/erasePICC">Karte löschen</a></li></ul>"; serveur.sendContent(temp); temp = "</li>"; //temp = "</li><li><a href='#'>Ereignisprotokoll</a></li>"; serveur.sendContent(temp); temp = "</ul></nav></div>"; serveur.sendContent(temp); temp = ""; } vide InitalizeHTTPServer() { bool initok = faux; const char * headerkeys[] = {"User-Agent","Cookie"} ; //en-Tête zum Tracken size_t headerkeyssize = sizeof(headerkeys)/sizeof(char*); //en-Tête zum Tracken serveur.sur("/", handleRoot); serveur.sur("/login", handleLogin); serveur.sur("/newPICC", handleNewPICC); serveur.sur("/erasePICC", handleErasePICC); serveur.onNotFound ( handleNotFound ); serveur.collectHeaders(headerkeys, headerkeyssize );// Serveur anweisen, diese zu Tracken serveur de.commencer(); / serveur Web/ start } // ******************* Fin des Fonctions de Serveur web ********************************************* // ******************* Les Fonctions De Démarrage WiFi De Gestion ************************************* // Funktion von https://www.az-delivery.de/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/wps-mit-dem-esp8266?ls=de bool startWPS() { bool wpsSuccess = WiFi.beginWPSConfig(); si(wpsSuccess) { // Muss nicht immer erfolgreich heißen! Nach einem Délai ist die SSID leer Chaîne newSSID = WiFi.SSID(); si(newSSID.longueur() > 0) { // Nur wenn eine SSID gefunden wurde waren wir erfolgreich rendement de la(); Série.println("ATWPS:OK"); saveCredentials(); // Enregistrer les informations d'Identification dans la mémoire EEPROM } d'autre { Série.println("ATWPS:NOK"); } } retour wpsSuccess; } bool startWiFiClient() { bool WiFiClientStarted = faux; size_t A0_ADCValue = 0; octet je = 0; octet connRes = 0; Série.setDebugOutput(false); // Zu Debugzwecken aktivieren. WiFi.nom d'hôte("CrdRdr41667"); WiFi.softAPdisconnect(vrai); WiFi.déconnecter(); le WiFi.en mode(WIFI_STA); si(loadCredentials()) { WiFi.commencer(MyWiFiConfig.APSTAName, MyWiFiConfig.WiFiPwd); tout (( connRes != 3 ) et( connRes != 4 ) et (j' != 30)) //si connRes == 0 "IDLE_STATUS - changement Statius" { j'++; // Serial.print("."); // Connexion vorgang auf der seriellen Schnittstelle beobachten //ESP.wdtFeed(); delay(500); rendement(); connRes = WiFi.waitForConnectResult(); } si (connRes == 4 ) { // si le mot de passe est incorrect Série.println("ATWIFI:PWDERR"); WiFi.déconnecter(); } si (connRes == 6 ) { // module n'est pas configuré en mode station de Série.println("ATWIFI:STAERR"); WiFi.déconnecter(); } } si(WiFi.statut() == WL_CONNECTED) { //ESP.wdtFeed(); Serial.print("de l'AIPRP:"); Série.println(WiFi.localIP()); WiFi.setAutoReconnect(vrai); // Définir si le module va tenter de vous reconnecter à un point d'accès dans le cas où il est déconnecté. // Le programme d'installation MDNS répondeur si (!MDNS.commencer("CrdRdr41667")) { Série.println("ATMDNS:NOK"); } d'autre { MDNS.addService("http",, "tcp", 80); } WiFiClientStarted = vrai; } d'autre { A0_ADCValue = analogRead(A0); //Wir waren nicht erfolgreich, daher starten wir WPS, wenn WPS Dégustateur un A0 während des Réinitialise gedrückt ist si (A0_ADCValue > 499) { si(startWPS()) { //ESP.wdtFeed(); delay(500); WiFi.déconnecter(); le WiFi.en mode(WIFI_STA); WiFi.commencer(WiFi.SSID().c_str(), WiFi.psk().c_str()); //ESP.wdtFeed(); WiFiClientStarted = vrai; } d'autre { WiFiClientStarted = faux; WiFi.déconnecter(); } } d'autre { WiFi.débrancher(); } } //WiFi.printDiag(de Série); // Zu Debugzwecken aktivieren. retour WiFiClientStarted; } // ******************* FIN des Fonctions WiFi de Gestion ************************************* // ******************* les Fonctions de Démarrage Store WiFi informations d'Identification dans la mémoire EEPROM ****************** bool loadCredentials() { bool RetValue; EEPROM.commencer(512); EEPROM.obtenir(0,MyWiFiConfig); EEPROM.fin(); si (la Chaîne(MyWiFiConfig.ConfigValid) == "SAVOIRS traditionnels") { RetValue = vrai; } d'autre { RetValue = false; // Paramètres WLAN nicht gefunden. } //ESP.wdtFeed(); return RetValue; } vide saveCredentials() // Speichere WLAN informations d'identification de l'auf EEPROM { size_t je; pour (je = 0 ; je < sizeof(MyWiFiConfig) ; j'++) // Loeschen der alten Konfiguration { EEPROM.écrire(je, 0); } pour (je = 0 ; je < STANameLen ; j'++) // Loeschen der alten Konfiguration { MyWiFiConfig.WiFiPwd[je] = 0; } pour (je = 0 ; je < WiFiPwdLen ; j'++) // Loeschen der alten Konfiguration { MyWiFiConfig.APSTAName[je] = 0; } temp = WiFi.SSID().c_str(); je = temp.longueur de(); temp.toCharArray(MyWiFiConfig.APSTAName,je+1); temp = WiFi.psk().c_str(); je = temp.longueur de(); temp.toCharArray(MyWiFiConfig.WiFiPwd,je+1); temp = ""; strncpy(MyWiFiConfig.ConfigValid , "SAVOIRS traditionnels", sizeof(MyWiFiConfig.ConfigValid) ); EEPROM.commencer(512); EEPROM.mettre(0, MyWiFiConfig); EEPROM.commettre(); EEPROM.fin(); //ESP.wdtFeed(); } // ******************* FIN des Fonctions StoreCredentialsto EEPROM *************************** // ******************* les Fonctions de Démarrage CardServices ************************************* void PCDHardReset() { digitalWrite(RST_PIN,FAIBLE); retard(200); digitalWrite(RST_PIN,HAUTE); mfrc522.PCD_Reset(); mfrc522.PCD_Init(); // Initialisiere MFRC522 Lesemodul mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max); // Setzt Antenne auf max. Empfang mfrc522.PCD_AntennaOn(); } boolean CardAuthenticate(boolean ABKey, octet Secteur de l',octet ikey[6]) { const byte sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63}; octet situation; situation = 0; pour (int un = 0; a < 6;une++) { clé.keyByte[un] = ikey[un]; } // la Clé Un si (ABKey) { situation = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, sectorkeytable[Secteur], &clé, &(mfrc522.uid)); si la (situation != MFRC522::STATUS_OK) { Série.println("ATAUTH:ERR_A"); return false; } } // Touche B d'autre si (pas ABKey) { situation = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B,sectorkeytable[Secteur], &clé, &(mfrc522.uid)); si la (situation != MFRC522::STATUS_OK) { Série.println("ATAUTH:ERR_B"); return false; } } return true; } // WriteData . utilise la Variable Globale DataBuffer pour le Retour de Données boolean CardDataWrite(octet Secteur de l',octet du bloc,d'octets de la valeur[16]) { de l'octet d'état; octet writevector; octet sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63}; writevector = Secteur de * 4 + bloc -1 ; pour les (octets un = 0; un < 16; un++) { si (writevector == sectorkeytable[un]) { // Serial.println("NAK"); return false; } } statut = mfrc522.MIFARE_Write(writevector, valeur, 16); si (le statut de != MFRC522::STATUS_OK) { Série.println("ATPCD:W_ERR"); //Série".println(mfrc522.GetStatusCodeName(status)); return false; } else { // Serial.print("OK"); return true; } } // Lecture de Données utilise la Variable Globale DataBuffer pour le Retour de Données boolean CardDataRead(octet Secteur de l',octet du bloc) { d'octets statusi; octet readvector; const byte sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63}; octets de taille = 18; readvector = Secteur de * 4 + bloc -1 ; pour les (octets un = 0; un < 16; un++) { si (readvector == sectorkeytable[une]) { Série.println("ATPCD:R_ERR"); return false; } } statusi = mfrc522.MIFARE_Read(readvector, DataBuffer, &de taille); si (statusi != MFRC522::STATUS_OK) { Série.println("ATPCD:R_ERR"); return false; } else { return true; } } boolean ResetCardToDefault() { octet ikey[16]; octet d'état,je; octet writevector; const byte sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63}; writevector = sectorkeytable[USED_Sector]; si (CardAuthenticate(KEYB,USED_Sector,MiFareClassicKey.Key_B)) // Secteur Autenticate pour l'Accès en ÉCRITURE { pour (je = 0; je <= 16; je++) { DataBuffer[je] = 0; } si (!(CardDataWrite(USED_Sector,1,DataBuffer))) { retourner false; } pour (je = 0; je <= 16; je++) { DataBuffer[je] = 0; } si (!(CardDataWrite(USED_Sector,2,DataBuffer))) { retourner false; } pour (je = 0; je <= 16; je++) { DataBuffer[je] = 0; } si (!(CardDataWrite(USED_Sector,3,DataBuffer))) { retourner false;} } pour (byte i = 0; i <= 16; i++) { ikey[j'] = 255; } //Chargement de la Clé par Défaut pour tous les Secteurs ikey[6] = 0xFF; // Paramètre par Défaut pour l'Accès Bits ikey[7] = 0x07; // ikey[8] = 0x80; // ikey[9] = 0x69; statut = mfrc522.MIFARE_Write(writevector, ikey, 16); si (le statut de != MFRC522::STATUS_OK) { return false; } retour vrai; } boolean SetSectorAccessControl (octet Secteur de l',octet Akey[6],octet Clef[6]) { octet ikey[16]; octet d'état; octet writevector; const byte sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63}; writevector = sectorkeytable[Secteur]; ikey[0] = Akey[0]; ikey[1] = Akey[1]; ikey[2] = Akey[2]; ikey[3] = Akey[3]; ikey[4] = Akey[4]; ikey[5] = Akey[5]; ikey[6] = 0x78; // Bloc de Données 0 À 3 Conditions d'Accès: Touche B / écriture de la Clé de Lecture ikey[7] = 0x77; // la CLÉ UN & TOUCHE B & Acces Bits Écrire:B Clé / Clé d'Un Accès en Lecture Bits ikey[8] = 0x88; // Calculatrice: http://calc.gmss.ru/Mifare1k/ ikey[9] = 0x69; // Fixateur Wert - > par défaut hex 69 ikey[10] = Clef[0]; ikey[11] = Clef[1]; ikey[12] = Clef[2]; ikey[13] = Clef[3]; ikey[14] = Clef[4]; ikey[15] = Clef[5]; statut = mfrc522.MIFARE_Write(writevector, ikey, 16); si (le statut de != MFRC522::STATUS_OK) { Série.println("ATPCD:W_KEY_ERR"); return false; }else { return true; } } boolean CheckforDefaultCardKey () { octet tkey[6]; boolean CardResult; octet readvector; octet statusi; const byte sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63}; octets de taille = 18; pour les (byte i = 0; i <= 6; i++) { tkey[j'] = 255; } //Chargement de la Clé par Défaut pour tous les Secteurs CardResult = vrai; si (!CardAuthenticate(KEYA,USED_Sector,tkey)) { CardResult = faux; }; readvector = sectorkeytable[USED_Sector]; statusi = mfrc522.MIFARE_Read(readvector, DataBuffer, &de taille); si (statusi != MFRC522::STATUS_OK) { CardResult = false; } //if (!((DataBuffer[7] = 0x07) & (DataBuffer[7] = 0x80))) { CardResult = false; }; retour CardResult; } boolean WriteNewMiFareClassicPICC () { octet tkey[6]; octet j'ai,un; booléen CardResult; si (CheckforDefaultCardKey()) { pour (je = 0; je <= 6; je++) { tkey[j'] = 255; } //Chargement de la Clé par Défaut pour tous les Secteurs pour (je = 0; je <= 16; je++) { DataBuffer[j'] = 0; } // Effacer la Variable Tampon CardResult = vrai; si (CardAuthenticate(KEYA,USED_Sector,tkey)) // Secteur Autenticate { // Serial.println("Auth Sec 0 OK"); si le (Nom de famille.de la longueur() > 15) { d'une = 15; } autre personne { un = Nom de famille.longueur();} si le (Nom de famille.de la longueur() > 0) { pour (je = 0; je <= 16; je++) { DataBuffer[je] = 0; } pour (je = 0; je <= un; je++) { DataBuffer[je] = Nom[je]; } si (!(CardDataWrite(USED_Sector,2,DataBuffer))) { CardResult = false; } //Le secteur 0 du Bloc 2 Vorname mit la Clé Un schreiben } si (Givenname.la longueur() > 15) { d'une = 15; } autre personne { un = Prénom.longueur(); } si (Givenname.la longueur() > 0) { pour (je = 0; je <= 16; je++) { DataBuffer[je] = 0; } pour (je = 0; je <= un; je++) { DataBuffer[je] = Givenname[je]; } si (!(CardDataWrite(USED_Sector,3,DataBuffer))) { CardResult = false; } //Le secteur 0 du Bloc 3 Nachname mit la Clé Un schreiben } si (!(SetSectorAccessControl (USED_Sector,MiFareClassicKey.Key_A,MiFareClassicKey.Key_B))) { CardResult = false; } // (octets par Secteur,byte Akey[6],byte Clef[6]) } d'autre { CardResult = faux; retour CardResult; } } d'autre { CardResult = faux; retour CardResult; } si (CardResult) { //Serial.println("PICC écrite"); CardResult = true; } else { //Serial.println("PICC n'est pas vide"); CardResult = faux; } rendement(); de retour CardResult; } boolean ReadMiFareClassicPICC () { boolean CardResult; octet j'ai,un ; CardResult = vrai; si (CardAuthenticate(KEYA,USED_Sector,MiFareClassicKey.Key_A)) // Secteur Autenticate avec la Touche de LECTURE d'Un { Givenname = "aaaaaaaaaaaaaaaa"; //espace Réservé Nom de famille = "aaaaaaaaaaaaaaaa"; //espace Réservé pour (je = 0; je < 18; je++) { DataBuffer[j'] = 0; } // Effacer la Variable Tampon si (CardDataRead(USED_Sector,2)) // Feld Vorname auslesen { pour (je = 0; je < 16; je++) { Nom[je] = char(DataBuffer[j']); } } else { return false; } pour (je = 0; je < 18; je++) { DataBuffer[j'] = 0; } // Effacer la Variable Tampon si (CardDataRead(USED_Sector,3)) // Feld Nachname auslesen { pour (je = 0; je < 16; je++) { Givenname[j'] = char(DataBuffer[j']); } } else { return false; } } else { return false; } Série.d'impression ("ATAUTH_S:"); Série.println (Nom); de Série.de l'impression ("ATAUTH_G:"); de Série.println (Givenname); return true; } void CardServer() { #define PCD_Poll_Interval 400 #define PCD_Watchdog_Interval 60000 if (millis() - PCD_ServiceCall_Handler >= PCD_Poll_Interval) { PCD_ServiceCall_Handler = millis(); if (mfrc522.PICC_IsNewCardPresent()) // PICC = proximity integrated circuit card = carte à Puce sans contact { mfrc522.PICC_ReadCardSerial(); yield(); // Distinction selon le type de carte // 0x08 pour MIFARE Classic 1K // 0x18 pour MIFARE Classic 4K // 0x11 pour MIFARE PLUS if (mfrc522.uid.sak == 0x08 || mfrc522.uid.sak == 0x18) { // MiFare_Classic_Processor START (mfrc522.uid.sak); // exécuter Seulement si Une Mifare Classic Carte devant le Lecteur a été maintenu. octet tkey[6]; for (octets i = 0; i <= 6; i++) { tkey[i] = 255; } //Load Default Key for all Sectors if(LearnNewCard) // la nouvelle Carte doit être initialisée. { if (WriteNewMiFareClassicPICC()) { SetRGBLed(0,255,0,false); } else { SetRGBLed(255,0,0,false); } } else if (EraseCard) // les données Cartographiques sont supprimés. { if (ResetCardToDefault()) { SetRGBLed(0,255,0,false); } else { SetRGBLed(255,0,0,false); } } else { if (ReadMiFareClassicPICC()) { // Carte valide ! bool PinState= digitalRead(RELAIS_PIN); PinState = !PinState; digitalWrite(RELAIS_PIN, PinState); SetRGBLed(0,255,0,false); //Led Vert } else { SetRGBLed(255,0,0,false); } } LearnNewCard = false; // MiFare_Classic_Processor STOP (mfrc522.uid.sak); } else if (mfrc522.uid.sak == 0x00) // Mifare Ultralight { SetRGBLed(255,0,0,false); } else { SetRGBLed(255,0,0,false); //Serial.print("PICC Type not supported. Type:"); //Serial.println(mfrc522.uid.sak); //Extension: éventuellement d'autres Types de cartes } mfrc522.PCD_StopCrypto1(); mfrc522.PICC_HaltA(); delay(2000); SetRGBLed(0,0,255,false); //Led Couleur Bleu Lecteur est en État } } if (millis() - PCD_WatchDog_Handler >= PCD_Watchdog_Interval) { PCD_WatchDog_Handler = millis(); Result = mfrc522.PCD_PerformSelfTest(); yield(); mfrc522.PCD_Init(); // on Initialise MFRC522 module de lecture de nouveau mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max); // Définit l'Antenne de max. Réception mfrc522.PCD_AntennaOn(); yield(); if (!(Result)) { PCDHardReset(); Serial.println("ATPCD:ERR_H"); } } } // ******************* Stop Functions CardServices ************************************* void loop() // Boucle principale { CardServer(); // Lecteur de carte et les Demandes spécifiques de modifier yield(); serveur.handleClient(); // Webserveranfragen modifier //ESP.wdtFeed(); // Watchdog remise à zéro. Désactiver "wdt_disable();" }
La connexion WI-fi gratuite les données de connexion sont conservées, comme nous, dans le précédent Sketch dans une Mémoire non volatile ont passé.
Comment fonctionne maintenant, mais maintenant l'Authentification de la Carte?
Bref, essaie de Kartenlesermodul lors de la présentation d'une Carte de Type MiFare Classic (ceci est Préalablement Vérifié) le Secteur qui valide Clés lire, lire.
L'Action est réussie, les champs nom, Prénom et Nom de famille lu, et la Carte est reconnue comme valide. (Relais est commuté) Est l'Authentification n'aboutit pas ou est la mauvaise Clé, la Carte comme nulle est rejetée.
La Distinction entre "Carte en cours de validité ou Carte non valide" à ce trait, nous les Avantages suivants:
possibilité d'utiliser simultanément plusieurs Cartes à un Lecteur est en droit, sans chaque Carte au Lecteur l'Avance par UUID pour la faire connaître.
Possibilité d'utiliser simultanément plusieurs lecteurs de cartes avec une Carte de dessert sans chaque Lecteur de cartes, chaque Carte déjà à l'Avance par UUID connaît.
Par la Variation de la Sektorendefinition ou du matériel de clé permet de "Berechtigungskreise" sont formés.
Nous pouvons maintenant créer notre première carte de pêche. Pour cela nous allons, lors de la connexion sur le Menu "PICC", puis "créer une nouvelle Carte" et cliquez dessus. Nous voyons à la Page suivante:
Nous donner un Avant et un Nom de famille dans les Champs prévus à cet effet, puis appuyez sur le Bouton "Nouvelle carte à Puce de la création"
, La LED jaune est. Nous avons maintenant un VIDE!! Mifare Classic Carte devant le Lecteur et attendre un peu jusqu'à ce que la LED devienne verte. C'est tout! À partir de maintenant, nous sommes, cette Carte est désormais autorisé de la Carte à notre Lecteur à utiliser.
Nous voulons cette Carte invalide faire, nous allons, après le Login sur le Menu PICC, puis, sous "effacer la carte" et cliquez dessus. -- Après le Message:
nous considérons l'autorisation préalable Carte devant le Lecteur. Ce n'est ensuite pas valide.
Amusez-vous à Construire nous jusqu'à la prochaine Partie.