


Die von Go generierte ECDSA-Signatur kann nicht mit JS überprüft werden
Der PHP-Editor Xiaoxin ist auf ein Problem gestoßen, als er die Go-Sprache zum Generieren der ECDSA-Signatur verwendete. Das heißt, JS kann nicht zur Überprüfung verwendet werden. Die Lösung für dieses Problem besteht darin, dem Go-Code einige zusätzliche Felder hinzuzufügen, um die Richtigkeit der Signatur sicherzustellen. Durch einige Änderungen am Go-Code können wir dieses Problem lösen und JS in die Lage versetzen, die von Go generierte ECDSA-Signatur korrekt zu überprüfen. In diesem Artikel werden Ihnen die konkreten Lösungen und Schritte im Detail vorgestellt.
Frageninhalt
Ich bin auf ein kleines Problem gestoßen (ich gehe davon aus, dass es eine Kleinigkeit gibt, die mich zurückhält, aber ich weiß nicht was), wie im Titel angegeben.
Ich werde damit beginnen, zu skizzieren, was ich tue, und dann alles bereitstellen, was ich habe.
Projektübersicht
Ich verwende die SHA-256
对文件进行哈希处理,并使用 ECDSA P-256
-Taste in meiner mobilen App, um die Hashes im Backend zu signieren. Und dann geht das immer weiter. Wenn der Benutzer möchte, kann er die Integrität der Datei überprüfen, indem er die Datei erneut hasht, den Hash nachschlägt und den Hash, einige Metadaten und die Signatur abruft.
Um zu überprüfen, ob die Daten an meine Anwendung und nicht an Dritte übermittelt wurden (die Hashes bleiben in der Blockchain, aber das ist für dieses Problem nicht wichtig), versucht die Anwendung, die Signatur mithilfe des öffentlichen Schlüssels zu überprüfen. Das funktioniert sehr gut.
Jetzt möchte ich diese Option auch zu meiner Website hinzufügen, aber das Problem besteht. Wenn ich die jsrsasign
或 webcrypto
API verwende, ist meine Signatur ungültig.
Daten
- Signaturbeispiel:
3045022100f28c29042a6d766810e21f2c0a1839f93140989299cae1d37b49a454373659c802203d0967be0696686414fe2efed3a71bc1639d066 ee127cfb7c0ad369521459d00
- Öffentlicher Schlüssel:
-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEq6iOuQeIhlhywCjo5yoABGODOJRZ c6/L8XzUYEsocCbc/JHiByGjuB3G9cSU2vUi1HUy5LsCtX2wlHSEObGVBw== -----END PUBLIC KEY-----
- Hash:
bb5dbfcb5206282627254ab23397cda842b082696466f2563503f79a5dccf942
Skript
JS-Code<code>const validHash = document.getElementById("valid-hash"); const locationEmbedded = document.getElementById("location-embedded") const signatureValid = document.getElementById("valid-sig") const fileSelector = document.getElementById('file-upload'); const mcaptchaToken = document.getElementById("mcaptcha__token") const submission = document.getElementById("submission") let publicKey; fileSelector.addEventListener("change", (event) => { document.getElementsByClassName("file-upload-label")[0].innerHTML = event.target.files[0].name }) submission.addEventListener('click', async (event) => { let token = mcaptchaToken.value if (token == null || token == "") { alert("Please activate the Captcha!") return } const fileList = fileSelector.files; if (fileList[0]) { const file = fileList[0] const fileSize = file.size; let fileData = await readBinaryFile(file) let byteArray = new Uint8Array(fileData); const bytes = await hashFile(byteArray) try { let resp = await callApi(toHex(bytes), token) validHash.innerHTML = "\u2713" const mediainfo = await MediaInfo({ format: 'object' }, async (mediaInfo) => { // Taken from docs mediaInfo.analyzeData(() => file.size, (chunkSize, offset) => { return new Promise((resolve, reject) => { const reader = new FileReader() reader.onload = (event) => { if (event.target.error) { reject(event.target.error) } resolve(new Uint8Array(event.target.result)) } reader.readAsArrayBuffer(file.slice(offset, offset + chunkSize)) }) }) try { let tags = mediaInfo.media.track[0].extra latitude = tags.LATITUDE longitude = tags.LONGITUDE if (latitude && longitude) { locationEmbedded.innerHTML = "\u2713" } else { locationEmbedded.innerHTML = "\u2717" } } catch (e) { locationEmbedded.innerHTML = "\u2717" } }) if (publicKey == undefined) { let req = await fetch("/publickey") if (req.ok) { publicKey = await req.text() } else { throw "Could not get public key" } } let signature = resp.data.comment if (signature == null || signature == "") { throw "No signature found" } //const timeStamps = resp.data.timestamps const hashString = resp.data.hash_string console.log(hashString) if (hashString !== toHex(bytes)) { validHash.innerHTML = "\u2717" } else { validHash.innerHTML = "\u2713" } const result = await validateSignature(publicKey, signature, hashString) console.log("Valid signature: " + result) if (result) { signatureValid.innerHTML = "\u2713" } else { signatureValid.innerHTML = "\u2717" } mcaptchaToken.value = "" } catch (e) { alert("Error: " + e) window.location.reload() } } else { alert("No file selected"); } }); function toHex(buffer) { return Array.prototype.map.call(buffer, x => ('00' + x.toString(16)).slice(-2)).join(''); } async function callApi(hash, token) { const url = "/verify"; let resp = await fetch(url, { headers: { "X-MCAPTCHA-TOKEN": token }, method: "POST", body: JSON.stringify({ hash: hash }) }) if (resp.ok) { return await resp.json(); } else { if (resp.status == 401) { throw resp.status } else { console.log(resp) throw "Your hash is either invalid or has not been submitted via the Decentproof App!" } } } async function hashFile(byteArray) { let hashBytes = await window.crypto.subtle.digest('SHA-256', byteArray); return new Uint8Array(hashBytes) } async function validateSignature(key, signature,hashData) { const importedKey = importPublicKey(key) const sig = new KJUR.crypto.Signature({"alg": "SHA256withECDSA"}); sig.init(importedKey) sig.updateHex(hashData); return sig.verify(signature) } function readBinaryFile(file) { return new Promise((resolve, reject) => { var fr = new FileReader(); fr.onload = () => { resolve(fr.result) }; fr.readAsArrayBuffer(file); }); } function importPublicKey(pem) { console.log(pem) return KEYUTIL.getKey(pem); } function hexToBytes(hex) { for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16)); return new Uint8Array(bytes); } </code>Anwendungsbestätigungscode (Flutter Dart)
<code>import 'dart:convert'; import 'package:convert/convert.dart'; import 'dart:typed_data'; import 'package:basic_utils/basic_utils.dart'; import 'package:decentproof/features/verification/interfaces/ISignatureVerifcationService.dart'; import 'package:pointycastle/asn1/asn1_parser.dart'; import 'package:pointycastle/asn1/primitives/asn1_integer.dart'; import 'package:pointycastle/signers/ecdsa_signer.dart'; class SignatureVerificationService implements ISignatureVerificationService { late final ECPublicKey pubKey; SignatureVerificationService() { pubKey = loadAndPrepPubKey(); } final String pemPubKey = """ -----BEGIN EC PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEq6iOuQeIhlhywCjo5yoABGODOJRZ c6/L8XzUYEsocCbc/JHiByGjuB3G9cSU2vUi1HUy5LsCtX2wlHSEObGVBw== -----END EC PUBLIC KEY----- """; ECSignature loadAndConvertSignature(String sig) { //Based on: https://github.com/bcgit/pc-dart/issues/159#issuecomment-1105689978 Uint8List bytes = Uint8List.fromList(hex.decode(sig)); ASN1Parser p = ASN1Parser(bytes); //Needs to be dynamic or otherwise throws odd errors final seq = p.nextObject() as dynamic; ASN1Integer ar = seq.elements?[0] as ASN1Integer; ASN1Integer as = seq.elements?[1] as ASN1Integer; BigInt r = ar.integer!; BigInt s = as.integer!; return ECSignature(r, s); } ECPublicKey loadAndPrepPubKey() { return CryptoUtils.ecPublicKeyFromPem(pemPubKey); } @override bool verify(String hash, String sig) { ECSignature convertedSig = loadAndConvertSignature(sig); final ECDSASigner signer = ECDSASigner(); signer.init(false, PublicKeyParameter<ECPublicKey>(loadAndPrepPubKey())); Uint8List messageAsBytes = Uint8List.fromList(utf8.encode(hash)); return signer.verifySignature(messageAsBytes, convertedSig); } } </code>Skript zur Schlüsselgenerierung (Go)
<code>package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/x509" "encoding/pem" "flag" "fmt" "os" ) func main() { var outPutDir string var outPutFileName string flag.StringVar(&outPutDir, "out", "./", "Output directory") flag.StringVar(&outPutFileName, "name", "key", "Output file name e.g key, my_project_key etc. Adding .pem is not needed") flag.Parse() key, err := generateKeys() if err != nil { fmt.Printf("Something went wrong %d", err) return } err = saveKeys(key, outPutDir, outPutFileName) if err != nil { fmt.Printf("Something went wrong %d", err) return } fmt.Printf("Keys generated and saved to %s%s.pem and %spub_%s.pem", outPutDir, outPutFileName, outPutDir, outPutFileName) } func generateKeys() (*ecdsa.PrivateKey, error) { return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } func saveKeys(key *ecdsa.PrivateKey, outPutDir string, outPutFileName string) error { bytes, err := x509.MarshalECPrivateKey(key) if err != nil { return err } privBloc := pem.Block{Type: "EC PRIVATE KEY", Bytes: bytes} privKeyFile, err := os.Create(outPutDir + outPutFileName + ".pem") if err != nil { return err } defer privKeyFile.Close() err = pem.Encode(privKeyFile, &privBloc) if err != nil { return err } bytes, err = x509.MarshalPKIXPublicKey(&key.PublicKey) pubBloc := pem.Block{Type: "EC Public KEY", Bytes: bytes} pubKeyFile, err := os.Create(outPutDir + "pub_" + outPutFileName + ".pem") if err != nil { return err } defer pubKeyFile.Close() err = pem.Encode(pubKeyFile, &pubBloc) if err != nil { return err } return nil } </code>
Link zum Signatur-Wrapper-Skript: Link
Mein Versuch
- Ich habe mit zwei neuen Schlüsselpaaren (und Ihrer Bibliothek) getestet, um einige Beispieldaten zu signieren, um zu sehen, ob etwas im Schlüssel falsch ist und nicht
- Ich habe die signierten Daten mit Ihrer Bibliothek und meinem privaten Schlüssel getestet und sie mit meinem öffentlichen Schlüssel überprüft, um festzustellen, ob mein privater Schlüssel beschädigt ist und dies nicht der Fall ist
- Ich habe alles mit der Network Encryption API versucht, aber kein Erfolg
- Ich habe versucht,
ECDSA
公钥并使用new KJUR.crypto.ECDSA({"curve":"secp256r1"}).verifyHex(hash,signature,pubKeyHex)
mit den oben genannten Daten zu laden, aber es hat nicht funktioniert (nur in der Browserkonsole getestet) - Ich habe Firefox und Safari verwendet, um zu sehen, ob es Unterschiede gibt, aber es hat sich nichts geändert
- Ich habe versucht, den Hash als String über
sig.updateString(hashData)
zu übergeben, aber ohne Erfolg - Es gibt noch einige weitere kleinere Änderungen
- Beim Vergleich von Hashes, R&S+Signaturen auf der Website und der App-Website ist alles wie erwartet.
- Ich habe den gesamten Prozess vom Front-End bis zum Backend verfolgt und es haben sich keine Daten geändert
Mein letzter Versuch war der vierte, denn zumindest nach meinem Verständnis werden Ihre Daten, wenn Sie die normale Methode verwenden (was ich im obigen Skript getan habe), gehasht, was das Gegenteil von produktiv ist, weil Ich habe den Hash-Wert bereits, wenn er also zweimal gehasht wird, stimmt er natürlich nicht überein. Aber aus Gründen, die ich nicht verstehe, erhalte ich immer noch false als Rückgabewert.
Ein letzter Gedanke: Könnte das Problem bei Verwendung der P-256
-Signatur darin bestehen, dass die go ecdsa-Bibliothek die Nachricht auf 32 Bytes kürzt? Vielleicht nicht so in JS?
Workaround
Die Validierung im JavaScript-Code ist aus zwei Gründen nicht mit Dart-Code kompatibel:
- 首先,JavaScript代码使用
KJUR.crypto.Signature ()
,它隐式对数据进行哈希处理。由于数据已经被散列,这会导致双重散列。在 Dart 方面,不会发生隐式哈希(因为ECDSASigner()
)。
为了避免 JavaScript 端的隐式哈希并与 Dart 代码兼容,KJUR.crypto.ECDSA()
可以用来代替KJUR.crypto.Signature()
。 - 其次,JavaScript 代码中的
updateHex()
对十六进制编码的哈希值执行十六进制解码,而在 Dart 代码中,十六进制编码的哈希值是 UTF-8 编码的。
为了与 Dart 代码兼容,十六进制编码的哈希值在 JavaScript 代码中也必须采用 UTF-8 编码。
以下 JavaScript 代码解决了这两个问题:
(async () => { var spki = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEq6iOuQeIhlhywCjo5yoABGODOJRZ c6/L8XzUYEsocCbc/JHiByGjuB3G9cSU2vUi1HUy5LsCtX2wlHSEObGVBw== -----END PUBLIC KEY-----`; var pubkey = KEYUTIL.getKey(spki).getPublicKeyXYHex() var pubkeyHex = '04' + pubkey.x + pubkey.y var msgHashHex = ArrayBuffertohex(new TextEncoder().encode("bb5dbfcb5206282627254ab23397cda842b082696466f2563503f79a5dccf942").buffer) // var msgHashHex = ArrayBuffertohex(new TextEncoder().encode("bb5dbfcb5206282627254ab23397cda8").buffer); // works also since only the first 32 bytes are considered for P-256 var sigHex = "3045022100f28c29042a6d766810e21f2c0a1839f93140989299cae1d37b49a454373659c802203d0967be0696686414fe2efed3a71bc1639d066ee127cfb7c0ad369521459d00" var ec = new KJUR.crypto.ECDSA({'curve': 'secp256r1'}) var verified = ec.verifyHex(msgHashHex, sigHex, pubkeyHex) console.log("Verification:", verified) })();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsrsasign/10.4.0/jsrsasign-all-min.js"></script>
Das obige ist der detaillierte Inhalt vonDie von Go generierte ECDSA-Signatur kann nicht mit JS überprüft werden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

GoimpactsDevelopmentPositivyThroughSpeed, Effizienz und DiasMlitication.1) Geschwindigkeit: Gocompilesquickandrunseffiction, idealforlargeProjects

C eignet sich besser für Szenarien, in denen eine direkte Kontrolle der Hardware -Ressourcen und hohe Leistungsoptimierung erforderlich ist, während Golang besser für Szenarien geeignet ist, in denen eine schnelle Entwicklung und eine hohe Parallelitätsverarbeitung erforderlich sind. 1.Cs Vorteil liegt in den nahezu Hardware-Eigenschaften und hohen Optimierungsfunktionen, die für leistungsstarke Bedürfnisse wie die Spieleentwicklung geeignet sind. 2. Golangs Vorteil liegt in seiner präzisen Syntax und der natürlichen Unterstützung, die für die Entwicklung einer hohen Parallelitätsdienste geeignet ist.

Golang zeichnet sich in praktischen Anwendungen aus und ist für seine Einfachheit, Effizienz und Parallelität bekannt. 1) Die gleichzeitige Programmierung wird über Goroutinen und Kanäle implementiert, 2) Flexibler Code wird unter Verwendung von Schnittstellen und Polymorphismen geschrieben, 3) Vereinfachen Sie die Netzwerkprogrammierung mit NET/HTTP -Paketen, 4) Effiziente gleichzeitige Crawler erstellen, 5) Debuggen und Optimierung durch Tools und Best Practices.

Zu den Kernmerkmalen von GO gehören die Müllsammlung, statische Verknüpfung und Unterstützung der Parallelität. 1. Das Parallelitätsmodell von GO -Sprache realisiert eine effiziente gleichzeitige Programmierung durch Goroutine und Kanal. 2. Schnittstellen und Polymorphismen werden durch Schnittstellenmethoden implementiert, so dass verschiedene Typen einheitlich verarbeitet werden können. 3. Die grundlegende Verwendung zeigt die Effizienz der Funktionsdefinition und des Aufrufs. 4. In der fortgeschrittenen Verwendung bieten Scheiben leistungsstarke Funktionen der dynamischen Größenänderung. 5. Häufige Fehler wie Rassenbedingungen können durch Getest-Race erkannt und gelöst werden. 6. Leistungsoptimierung wiederverwenden Objekte durch Sync.Pool, um den Druck der Müllabfuhr zu verringern.

Go Language funktioniert gut beim Aufbau effizienter und skalierbarer Systeme. Zu den Vorteilen gehören: 1. hohe Leistung: Kompiliert in den Maschinencode, schnelle Laufgeschwindigkeit; 2. gleichzeitige Programmierung: Vereinfachen Sie Multitasking durch Goroutinen und Kanäle; 3. Einfachheit: präzise Syntax, Reduzierung der Lern- und Wartungskosten; 4. plattform: Unterstützt die plattformübergreifende Kompilierung, einfache Bereitstellung.

Verwirrt über die Sortierung von SQL -Abfragenergebnissen. Während des Lernens von SQL stoßen Sie häufig auf einige verwirrende Probleme. Vor kurzem liest der Autor "Mick-SQL Basics" ...

Die Beziehung zwischen Technologiestapelkonvergenz und Technologieauswahl in der Softwareentwicklung, der Auswahl und dem Management von Technologiestapeln ist ein sehr kritisches Problem. In letzter Zeit haben einige Leser vorgeschlagen ...


Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

SublimeText3 Linux neue Version
SublimeText3 Linux neueste Version

Dreamweaver Mac
Visuelle Webentwicklungstools

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

mPDF
mPDF ist eine PHP-Bibliothek, die PDF-Dateien aus UTF-8-codiertem HTML generieren kann. Der ursprüngliche Autor, Ian Back, hat mPDF geschrieben, um PDF-Dateien „on the fly“ von seiner Website auszugeben und verschiedene Sprachen zu verarbeiten. Es ist langsamer und erzeugt bei der Verwendung von Unicode-Schriftarten größere Dateien als Originalskripte wie HTML2FPDF, unterstützt aber CSS-Stile usw. und verfügt über viele Verbesserungen. Unterstützt fast alle Sprachen, einschließlich RTL (Arabisch und Hebräisch) und CJK (Chinesisch, Japanisch und Koreanisch). Unterstützt verschachtelte Elemente auf Blockebene (wie P, DIV),

VSCode Windows 64-Bit-Download
Ein kostenloser und leistungsstarker IDE-Editor von Microsoft