


Impossible de vérifier la signature ECDSA générée par Go à l'aide de JS
L'éditeur PHP Xiaoxin a rencontré un problème lors de l'utilisation du langage Go pour générer la signature ECDSA, c'est-à-dire que JS ne peut pas être utilisé pour la vérification. La solution à ce problème consiste à ajouter des champs supplémentaires au code Go pour garantir l'exactitude de la signature. En apportant quelques modifications au code Go, nous pouvons résoudre ce problème et permettre à JS de vérifier correctement la signature ECDSA générée par Go. Cet article vous présentera en détail les solutions et étapes spécifiques.
Contenu de la question
J'ai rencontré un petit problème (je suppose qu'il y a une petite chose qui me retient, mais je ne sais pas quoi), comme indiqué dans le titre.
Je vais commencer par décrire ce que je fais, puis fournir tout ce que j'ai.
Aperçu du projet
J'utilise la clé SHA-256
对文件进行哈希处理,并使用 ECDSA P-256
dans mon application mobile pour signer les hachages sur le backend. Et puis cela continue encore et encore. Si l'utilisateur le souhaite, il peut vérifier l'intégrité du fichier en hachant à nouveau le fichier, en recherchant le hachage et en obtenant le hachage, certaines métadonnées et la signature.
Pour vérifier que les données ont été soumises à mon application et non à un tiers (les hachages restent dans la blockchain, mais ce n'est pas important pour ce problème), l'application va tenter de vérifier la signature à l'aide de la clé publique. Cela fonctionne très bien.
Maintenant, je souhaite également ajouter cette option à mon site Web, mais le problème est le suivant. Si j'utilise l'API jsrsasign
或 webcrypto
, ma signature n'est pas valide.
Données
- Exemple de signature :
3045022100f28c29042a6d766810e21f2c0a1839f93140989299cae1d37b49a454373659c802203d0967be0696686414fe2efed3a71bc1639d066 ee127cfb7c0ad369521459d00
- Clé publique :
-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEq6iOuQeIhlhywCjo5yoABGODOJRZ c6/L8XzUYEsocCbc/JHiByGjuB3G9cSU2vUi1HUy5LsCtX2wlHSEObGVBw== -----END PUBLIC KEY-----
- Hash :
bb5dbfcb5206282627254ab23397cda842b082696466f2563503f79a5dccf942
Script
Code JS<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>Code de vérification de l'application (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>Script de génération de clé (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>
Lien vers le script du wrapper de signature : lien
Ma tentative
- J'ai testé en utilisant deux nouvelles paires de clés (et votre bibliothèque) pour signer des exemples de données afin de voir si quelque chose dans la clé est faux et ce n'est pas le cas
- J'ai testé les données signées à l'aide de votre bibliothèque et de ma clé privée et je les ai vérifiées à l'aide de ma clé publique pour voir si ma clé privée est corrompue et ce n'est pas le cas
- J'ai essayé toutes les opérations de l'API Network Encryption sans succès
- J'ai essayé de charger
ECDSA
公钥并使用new KJUR.crypto.ECDSA({"curve":"secp256r1"}).verifyHex(hash,signature,pubKeyHex)
avec les données ci-dessus et cela n'a pas fonctionné (testé uniquement dans la console du navigateur) - J'ai utilisé Firefox et Safari pour voir s'il y avait des différences, mais rien n'a changé
- J'ai essayé de passer le hachage sous forme de chaîne via
sig.updateString(hashData)
mais sans succès - Il y a d'autres petits changements
- En comparant les hachages, les signatures r&s+ sur le site Web et le site Web de l'application, tout se passe comme prévu.
- J'ai suivi l'ensemble du processus du front-end au back-end et aucune donnée n'a changé
Ma dernière tentative était la 4ème car au moins d'après ma compréhension, si vous utilisez la méthode habituelle (ce que j'ai fait dans le script ci-dessus), vos données seront hachées, en ce qui me concerne, ce qui est le contraire de productif, car J'ai déjà la valeur de hachage, donc si elle est hachée deux fois, bien sûr, elle ne correspondra pas. Mais pour des raisons que je ne comprends pas, j'obtiens toujours false comme valeur de retour.
Une dernière réflexion, si vous utilisez la signature P-256
, le problème pourrait-il être que la bibliothèque go ecdsa tronque le message à 32 octets ? Peut-être pas dans JS ?
Solution
La validation en code JavaScript n'est pas compatible avec le code Dart pour deux raisons :
- 首先,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>
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Les principales différences entre Golang et Python sont les modèles de concurrence, les systèmes de type, les performances et la vitesse d'exécution. 1. Golang utilise le modèle CSP, qui convient aux tâches simultanées élevées; Python s'appuie sur le multi-threading et Gil, qui convient aux tâches à forte intensité d'E / S. 2. Golang est un type statique, et Python est un type dynamique. 3. La vitesse d'exécution du langage compilée de Golang est rapide, et le développement du langage interprété par Python est rapide.

Golang est généralement plus lent que C, mais Golang présente plus d'avantages dans l'efficacité de programmation et de développement simultanée: 1) Le modèle de collecte et de concurrence de Golang de Golang le fait bien fonctionner dans des scénarios à haute concurrence; 2) C obtient des performances plus élevées grâce à la gestion manuelle de la mémoire et à l'optimisation matérielle, mais a une complexité de développement plus élevée.

Golang est largement utilisé dans le cloud computing et DevOps, et ses avantages résident dans la simplicité, l'efficacité et les capacités de programmation simultanées. 1) Dans le cloud computing, Golang gère efficacement les demandes simultanées via les mécanismes de goroutine et de canal. 2) Dans DevOps, les fonctionnalités de compilation rapide de Golang et de plate-forme en font le premier choix pour les outils d'automatisation.

Golang et C ont chacun leurs propres avantages dans l'efficacité du rendement. 1) Golang améliore l'efficacité par le goroutine et la collecte des ordures, mais peut introduire un temps de pause. 2) C réalise les hautes performances grâce à la gestion et à l'optimisation manuelles, mais les développeurs doivent faire face aux fuites de mémoire et à d'autres problèmes. Lors du choix, vous devez considérer les exigences du projet et la pile de technologies d'équipe.

Golang convient plus à des tâches de concurrence élevées, tandis que Python présente plus d'avantages dans la flexibilité. 1. Golang gère efficacement la concurrence par le goroutine et le canal. 2. Python repose sur le filetage et l'asyncio, qui est affecté par GIL, mais fournit plusieurs méthodes de concurrence. Le choix doit être basé sur des besoins spécifiques.

Les différences de performance entre Golang et C se reflètent principalement dans la gestion de la mémoire, l'optimisation de la compilation et l'efficacité du temps d'exécution. 1) Le mécanisme de collecte des ordures de Golang est pratique mais peut affecter les performances, 2) la gestion manuelle de C et l'optimisation du compilateur sont plus efficaces dans l'informatique récursive.

ChooseGolangForHighPerformanceAnd Concurrence, IdealForBackendServices andNetworkProgramming; selectPythonForrapidDevelopment, dataScience et MachineLearningDuetOtsSertilityAnStensiveLibrarary.

Golang et Python ont chacun leurs propres avantages: Golang convient aux performances élevées et à la programmation simultanée, tandis que Python convient à la science des données et au développement Web. Golang est connu pour son modèle de concurrence et ses performances efficaces, tandis que Python est connu pour sa syntaxe concise et son écosystème de bibliothèque riche.


Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Version Mac de WebStorm
Outils de développement JavaScript utiles

SublimeText3 Linux nouvelle version
Dernière version de SublimeText3 Linux

Télécharger la version Mac de l'éditeur Atom
L'éditeur open source le plus populaire

SublimeText3 version anglaise
Recommandé : version Win, prend en charge les invites de code !

Adaptateur de serveur SAP NetWeaver pour Eclipse
Intégrez Eclipse au serveur d'applications SAP NetWeaver.