Das Gem openssl wird mit Ruby ausgeliefert und kapselt das OpenSSL des Systems – 3.5.7 im Image ruby:4.0.7, mit der Gem-Version 4.0.2. OpenSSL::PKey::EC#sign(digest, data) erzeugt ASN.1-DER mit zufälliger Nonce und ohne Low-S-Normalisierung, und verify akzeptiert High-S. dsa_sign_asn1 signiert einen Digest, den du bereits berechnet hast.
Fehlerhafte Signaturen lösen eine Exception aus, statt false zurückzugeben: Eine rohe r‖s-Signatur mit 64 Byte oder ein 3-Byte-Blob lösten OpenSSL::PKey::PKeyError: EVP_DigestVerify aus, während eine wohlgeformte Signatur über eine andere Nachricht false ergab. Code, der nur den booleschen Wert prüft, stürzt bei einer Signatur im JWS-Stil ab; fange deshalb PKeyError ab oder konvertiere vorher, wie es das Beispiel tut. Der Konverter DER ⇄ Raw zeigt beide Formen nebeneinander.
Unter OpenSSL 3 sind Schlüssel unveränderlich: Das Zuweisen von private_key an einen bestehenden EC-Schlüssel löste pkeys are immutable on OpenSSL 3.0 aus. Damit funktionieren ältere Tutorials nicht mehr, die einen Schlüssel durch Setzen des Punkts oder des Skalars aufbauen. Erzeuge Schlüssel stattdessen aus DER oder PEM – ein bloßer SEC1-Punkt muss vorher in eine SPKI-Struktur verpackt werden. Achte darauf, welche Methode was schreibt: to_pem schreibt bei einem privaten Schlüssel SEC1 EC PRIVATE KEY samt dem geheimen Schlüsselmaterial, private_to_pem schreibt PKCS#8, und veröffentlichen solltest du die Ausgabe von public_to_pem.
Die unterstützten Kurven richten sich nach OpenSSL: P-224 bis P-521, secp256k1 und die Brainpool-r1-Kurven, dazu Ed25519 und Ed448 über OpenSSL::PKey.generate_key. Mit derselben generischen API wurden Schlüssel für ML-DSA-44/65/87 und SLH-DSA erzeugt, und Signieren und Prüfen funktionierten; ML-KEM-Schlüssel lassen sich erzeugen, aber PKey bietet in 4.0.2 keine Methode für die Kapselung. Für JWTs baut ruby-jwt auf diesem Gem auf.