Hey ihr,
ich melde mich mit einem Problem, was es ganz schön in sich hat. Ich sitze hier seit 19:00 und finde meinen Fehler nicht
Ich habe einen Server, der auf node.js-Basis läuft. Ich möchte von diesem per request eine Anfrage an eine Webseite senden, die Webseite soll antworten.
Dabei wird ein temporärer RSA-Schlüssel generiert, der öffentliche Schlüssel davon wird an den Webserver gesendet. Um zu beweisen, dass der Server wirklich der Server ist, unterschreibt er den öffentlichen Schlüssel und ein paar andere Daten mit einem Master-Schlüssel, der Client hat den zu den Master-Schlüssel passenden öffentlichen Schlüssel.
Geladen wird der Schüssel beim Start wie folgt:
console.log("[RSA] Found. Importing it...");
AuthHelper.key = new NodeRSA(fs.readFileSync("./setup/private.key"), "pkcs1", {encryptionScheme: "pkcs1", signingScheme: "pkcs1-md5"});
console.log("[RSA] Keys imported!");
Der Code zum Generieren des temporären Schlüssels:
createSignedRequest: function(uuid) {
var time = Math.floor(new Date().getTime() / 1000);
/* Wir erstellen einen temporären Schlüssel, der 1024 Bit groß ist */
var x = new NodeRSA({encryptionScheme: "pkcs1", signingScheme: "pkcs1-md5"});
x.generateKeyPair(1024);
/* Wir packen den öffentlichen Schlüssel in ein Array, zusammen mit einer UUID und der aktuellen Uhrzeit. Dann base64_encode(json_encode($data)) */
var data = new Buffer(JSON.stringify({
key: x.exportKey("public"),
uuid: uuid,
date: time
})).toString('base64');
/* Jetzt unterschreiben wir den Code oben mit unserem Masterschlüssel */
var signature = AuthHelper.key.sign(data).toString('base64');
return new AuthSubHelper(x, data, signature);
}
Alles anzeigen
Die Daten, die er an den Server senden, stimmen mit denen überein, die der Client generiert. Das ganze sieht beispielsweise so aus:
data: eyJrZXkiOiItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLVxuTUlHZU1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTUFEQ0JpQUtCZ0g3cHJCNzFYQmc0K1U5ckZCM2h1eStKcXZCblxuU3IrdUVWM3ZRYlRodXlzWWV1TGErQXlwSTd4MWdVVkFmdUN1VEUvb0pMSDhUTlZtNjlyS2NNcFFWaGRDdVpHS1xuZDFudTY4Ky9LWjFtd1ZSRURGU0QyNlVMSjRKLzBZS2Q4WnRPb2hBMDRlSG9QZEVmd1pObWZHeElpM2tteThnL1xuUjBoVDgrWEFISmo4eTZodkFnTUJBQUU9XG4tLS0tLUVORCBQVUJMSUMgS0VZLS0tLS0iLCJ1dWlkIjoiMTg4ZTg0YjItNTg2Yy00ZmZkLTgzNWMtN2IyNmViM2E0NTRkIiwiZGF0ZSI6MTQ1MjI4ODMwMH0=
signature: NJMGwDjaX3saFNgzlFGSVOOiQ2hNn5iUnHmILULOvElgt7ZRTzysIcmz+GHdgdaYFv4qsWKzpR+kfaDuu9gF6sMl3gSmStP9mZTz4l9ZtbJaWaYbN1m9PXBAdxfDES9Cu07o1UZ/H5W+hLrF5rpcq5xRuBvG/WLTGWQctSZvOrWhNRXl1jidoMrR7TsapqqqImXwRPBTYkrODcFoGv/5UAseanZoM3nad/CyVUSQIUepDMvr5ZHedL8FKddP9GecKrTFM49M6kXoAmfZCuVCVkXbD988Qdy/1KCjbgMeDCp8K//OWpGu0AGMcHiL6Zg1/mxXZz6ajeStyFy/J9pO4Q==
In "data" steckt der temporäre öffentliche Schlüssel, die aktuelle Uhrzeit als *nix-Timestamp und eine UUID, die unwesentlich ist. Signature ist wohl selbsterklärend, das ist einfach nur die Signatur base64-kodiert.
Clientseitig wird das ganze überprüft, doch bei der Überprüfung gibt phpseclib immer false zurück
include_once('Crypt/RSA.php');
$rsa = new Crypt_RSA();
/* Laden des öffentlichen Schlüssels vom Master-Schlüssel */
$rsa->loadKey(file_get_contents(CACHE_ROOT .'root.public.key'));
/* PKCS1 und MD5 */
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$rsa->setHash('md5');
/* Überprüfen, ob $_POST[data] vom Master-Schlüssel unterschrieben wurde */
if(!@$rsa->verify($_POST['data'], base64_decode($_POST['signature']))) {
/* Es kommt immer "false" raus, die Signatur stimmt also nicht */
$logs .= "Error: invalid signature\r\n";
$logs .= "Data: ". $_POST['data'] ."\r\n";
$logs .= "Signature: ". $_POST['signature'] ."\r\n";
file_put_contents(NSNX_CACHE_ROOT .'requests.log', $logs);
die('INVALID_POST_DATA');
}
Alles anzeigen
Die "typischen" Fehler bin ich schon durchgegangen
- Die Daten werden erfolgreich und unverändert übertragen
- Der öffentliche Schlüssel von dem Clienten passt zu dem Masterschlüssel des Servers
- Der Server lädt den privaten Schlüssel erfolgreich
- Der Client lädt den öffentlichen Schlüssel erfolgreich
- Beide benutzen PKCS1 und md5. Einen Umstieg auf PKCS1 sha1 oder PKCS1 sha256 brachte keinen Erfolg.
- Eine direkte Überprüfung der Signatur auf Serverseite ist natürlich erfolgreich
Wenn ich das "@" in Zeile 9 weg nehme, also die entsprechende Fehlermeldung anzeigen lasse, gibt er nur einen Notice, dass die Signatur falsch ist.
error_reporting ist auf E_ALL, er würde mir also eventuelle Fehler anzeigen. Selbes gilt für display_errors.
Benutzt werden vom Server node.js, aktuellste Version, zusammen mit node-rsa.
Der Client benutzt PHP mit der aktuellsten Version von phpseclib.
Die Daten werden über HTTP übertragen, ich nutze dazu request in der aktuellsten Version und eine POST-Anfrage.
Die Post-Anfrage wird wie folgt gesendet:
self.encryptionhelper = AuthHelper.createSignedRequest(self.get("uuid"));
request.post({
"url": "http://url.zur.php.seite/",
"form": {
"data": self.encryptionhelper.getData(),
"signature": self.encryptionhelper.getSignature()
}
}, function(error, response, body) { });
Als Antwort bekommt er "INVALID_POST_DATA" und der Client schreibt in die Log-Datei, dass die Signatur falsch ist.
Kann mir einer einen Denkanstoß geben?
EDIT: Hat sich erledigt. Sollte jemand ähnliche Probleme bekommen, die POST-Daten müssen erst zu UTF8 umgewandelt werden, also:
$_POST['data'] = utf8_encode($_POST['data']);
$_POST['signature'] = utf8_encode($_POST['signature']);