Zum Inhalt springen
ecdsa.com
Ruby

Ruby OpenSSL (openssl gem)

Das openssl-Gem von Ruby, das Binding, auf dem jede Krypto- und JWT-Bibliothek für Ruby aufsetzt: DER-Signaturen, Exceptions statt false bei fehlerhafter Eingabe, unveränderliche Schlüssel unter OpenSSL 3 und ML-DSA über die generische PKey-API.
Lizenz
Ruby OR BSD-2-Clause
Geprüfte Version
4.0.2 ·
Quellcode
github.com/ruby/openssl
Dokumentation
docs.ruby-lang.org/en/master/OpenSSL.html

Kompatibilität auf einen Blick

ECDSAEdDSAPost-Quanten-Verfahren

ECDSA-Kurven
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Formate privater Schlüssel
pkcs8-pempkcs8-derencrypted-pkcs8sec1-pem

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.

Fakten und die Prüfungen dahinter

Nichts im Folgenden ist aus einer Dokumentation abgeschrieben. Das Prüfskript content/libraries/verify/ruby-openssl/run.sh hat Ruby OpenSSL (openssl gem) 4.0.2 unter Ruby 4.0.7, OpenSSL 3.5.7 9 Jun 2026 am 20. Oktober 2026 im Container-Image ruby:4.0.7-slim-trixie ausgeführt; die Seite muss mit seiner Ausgabe übereinstimmen, und jeder Fakt führt die zugehörigen Zeilen auf.

  • Kodierung von ECDSA-Signaturen

    der

    sign(digest, data) liefert ASN.1-DER.

    Von der Prüfung aufgezeichnet
    fact ecdsa-signature-encoding = der
  • ECDSA-Nonce

    random

    Zufällige Nonce: Zwei Signaturen derselben Nachricht unterscheiden sich.

    Von der Prüfung aufgezeichnet
    fact ecdsa-nonce = random
  • Low-S beim Signieren

    either

    s wird nicht normalisiert: Unter 64 Signaturen traten High-S-Werte auf.

    Von der Prüfung aufgezeichnet
    fact ecdsa-low-s-sign = either
  • High-S bei der Prüfung

    accepts-high-s

    verify akzeptierte die Signatur, in der s durch n − s ersetzt war.

    Von der Prüfung aufgezeichnet
    fact ecdsa-low-s-verify = accepts-high-s
  • Was der Signaturaufruf hasht

    hashes-message-or-digest

    sign hasht mit dem übergebenen Digest-Algorithmus; dsa_sign_asn1 signiert einen vorab berechneten Digest, und diese Signatur bestand die Prüfung gegen die Nachricht.

    Von der Prüfung aufgezeichnet
    fact ecdsa-prehash = hashes-message-or-digest
  • Fehlerhafte ECDSA-Signatur

    throws

    Eine rohe r‖s-Signatur und ein 3-Byte-Blob lösen OpenSSL::PKey::PKeyError: EVP_DigestVerify aus; eine gültige Signatur über eine andere Nachricht ergibt false.

    Von der Prüfung aufgezeichnet
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: raw r||s: OpenSSL::PKey::PKeyError: EVP_DigestVerify
    evidence ecdsa-malformed-signature: 3 bytes: OpenSSL::PKey::PKeyError: EVP_DigestVerify
    evidence ecdsa-malformed-signature: wrong message: false
  • ECDSA-Kurven

    P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    Signieren und Prüfen funktionierten mit P-224, P-256, P-384, P-521, secp256k1 und brainpoolP256r1/P384r1/P512r1.

    Von der Prüfung aufgezeichnet
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 und Ed448 über OpenSSL::PKey.generate_key.

    Von der Prüfung aufgezeichnet
    fact eddsa = Ed25519, Ed448
  • Post-Quanten-Algorithmen

    ML-DSASLH-DSA

    Erzeugen, Signieren und Prüfen funktionierten mit ML-DSA-44/65/87 und SLH-DSA-SHA2-128s über die generische API PKey; ML-KEM-Schlüssel lassen sich erzeugen, aber es gibt keine Methode für die Kapselung.

    Von der Prüfung aufgezeichnet
    fact pqc = ML-DSA, SLH-DSA
    evidence pqc: ML-KEM-768 key generation, and whether PKey offers encapsulate: false
  • Formate öffentlicher Schlüssel

    spki-pemspki-der

    SPKI als PEM und DER über OpenSSL::PKey.read; ein bloßer SEC1-Punkt ließ sich erst laden, nachdem er in SPKI verpackt war.

    Von der Prüfung aufgezeichnet
    fact public-key-formats = spki-pem, spki-der
    evidence public-key-formats: a 65-byte SEC1 point wrapped in an SPKI structure by hand, then loaded: ok
  • Formate privater Schlüssel

    pkcs8-pempkcs8-derencrypted-pkcs8sec1-pem

    PKCS#8 als PEM und DER, verschlüsseltes PKCS#8 und SEC1 als PEM; to_pem schreibt EC PRIVATE KEY, private_to_pem schreibt PKCS#8.

    Von der Prüfung aufgezeichnet
    fact private-key-formats = pkcs8-pem, pkcs8-der, encrypted-pkcs8, sec1-pem
    evidence private-key-formats: key.to_pem writes -----BEGIN EC PRIVATE KEY-----; key.private_to_pem writes -----BEGIN PRIVATE KEY-----
  • EC-Schlüsselobjekte veränderbar

    no

    Das Zuweisen von private_key an einen bestehenden Schlüssel löste pkeys are immutable on OpenSSL 3.0 aus.

    Von der Prüfung aufgezeichnet
    fact ec-key-mutable = no
    evidence ec-key-mutable: assigning private_key on an existing EC key: OpenSSL::PKey::PKeyError: pkeys are immutable on OpenSSL 3.0

Verwendung

Signiere als DER, konvertiere in rohes r‖s, veröffentliche nur den öffentlichen Schlüssel und sieh dir an, wie verify bei einer rohen Signatur eine Exception auslöst.
require "openssl"

key = OpenSSL::PKey::EC.generate("prime256v1")
digest = OpenSSL::Digest.new("SHA256")
message = "order #1042 approved"

der = key.sign(digest, message) # ASN.1 DER

# Raw r||s for JWS or WebCrypto: take the two INTEGERs and pad each to 32 bytes.
r, s = OpenSSL::ASN1.decode(der).value.map(&:value)
raw = r.to_s(2).rjust(32, "\0") + s.to_s(2).rjust(32, "\0")

# Publish only the public half: public_to_pem, never to_pem (which writes the private key).
public_key = OpenSSL::PKey.read(key.public_to_pem)
puts "verified: #{public_key.verify(digest, der, message)}, raw bytes: #{raw.bytesize}"

# A raw signature is not "false" here: verify raises OpenSSL::PKey::PKeyError.
begin
  public_key.verify(digest, raw, message)
rescue OpenSSL::PKey::PKeyError => e
  puts "raw signature: #{e.class}"
end

Die Snippet-Testumgebung führt jedes Beispiel unverändert im Container-Image ruby:4.0.7-slim-trixie aus, und der Lauf schlägt fehl, wenn die Ausgabe nicht übereinstimmt.

Befehl
ruby usage.rb
Ausgabe muss passen (regulärer Ausdruck)
verified: true, raw bytes: 64\nraw signature: OpenSSL::PKey::PKeyError

Bibliotheken