php / php/php-src

Allow constructing a public-only RSA key with openssl_pkey_new()

Ouverte
#22,702 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Extension: openssl Feature Status: Verified
Langage dominant
C
Étoiles
40.4k
Forks
8.2k
Merge moyen
2 j 13 h
PR mergées (30 j)
96

Description

Description

openssl_pkey_new() can construct a public key directly from raw parameters for every asymmetric key type except RSA:

  • ['ec' => ['curve_name' => …, 'x' => …, 'y' => …]] → public EC key ✅
  • ['ed25519' => ['pub_key' => …]] / ['ed448' => …] → public EdDSA key ✅
  • ['dsa' | 'dh' => ['pub_key' => …]] → public key ✅
  • ['rsa' => ['n' => …, 'e' => …]] → ❌ a private exponent d is mandatory, and the result is always flagged private

So there is no way to obtain an RSA public key object from n and e that openssl_verify() will accept:

// (1) Without d — construction fails outright:
var_dump(openssl_pkey_new(['rsa' => ['n' => $n, 'e' => $e]])); // bool(false)

// (2) With a dummy d — builds, but is flagged private, so verify() refuses it:
$k = openssl_pkey_new(['rsa' => ['n' => $n, 'e' => $e, 'd' => "\x00"]]);
openssl_verify($msg, $sig, $k, OPENSSL_ALGO_SHA256);
// Warning: openssl_verify(): Don't know how to get public key from this private key
// Warning: openssl_verify(): Supplied key param cannot be coerced into a public key

The only workarounds today are to build a throwaway "private" key with a placeholder d, then round-trip it back out via openssl_pkey_get_details($k)['key'] and reload with openssl_pkey_get_public(), or to hand-encode a DER SubjectPublicKeyInfo in userland. Both are awkward for something every other key type supports natively.

Why this is worth adding

a) Consistency — RSA is the only outlier. In ext/openssl, php_openssl_pkey_init_dsa(), _dh(), _ec(), and the curve-25519/448 path all take an is_private out-parameter, derive it from whether private material was supplied, and build public-only keys just fine. php_openssl_pkey_init_rsa() alone (i) hard-requires d, (ii) never computes is_private, and (iii) its caller in openssl_pkey_new() hardcodes is_private = true. Bringing RSA in line with the others is a small, localized change.

b) WebAuthn / COSE key conversion. WebAuthn credential public keys arrive as COSE_Key maps (RFC 9052; RSA parameters per RFC 8230) — i.e. raw n and e bytes for RS256. Relying parties need to turn those into an OpenSSL key purely to verify assertion signatures. For EC2 (ES256) and OKP (EdDSA) keys this is already a clean one-liner via openssl_pkey_new(); for RSA it forces the dummy-d PEM round-trip above or a hand-rolled ASN.1 encoder. Public-only RSA construction would let libraries handle all three COSE key types uniformly, with zero userland ASN.1.

Suggested implementation

Mirror the existing init_dsa/init_ec pattern:

  • give php_openssl_pkey_init_rsa() a bool *is_private out-param;
  • make d optional (require n and e), push OSSL_PKEY_PARAM_RSA_D only when present, and set *is_private = (d != NULL);
  • select EVP_PKEY_PUBLIC_KEY vs EVP_PKEY_KEYPAIR accordingly (v3 backend), and pass d == NULL through to RSA_set0_key() (v1 backend, which already permits it);
  • have the rsa branch of openssl_pkey_new() pass the flag to php_openssl_pkey_object_init(), exactly like the dsa/ec branches.

This is purely additive — keys constructed with d stay flagged private, so no BC break. Happy to send a PR with tests if this is welcome.

PHP Version

master (8.5-dev); behaviour also confirmed on 8.4.23.

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par php_openssl_pkey_init_rsa() et la branche rsa de openssl_pkey_new(), en les comparant aux chemins init_dsa et init_ec existants. Vérifiez que les clés RSA ne contenant que n/e sont acceptées par openssl_verify(), tandis que les clés contenant d restent privées ; ajoutez des tests couvrant les deux cas.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
c, php
Domaine
cryptography
Type d'issue
Fonctionnalité
Difficulté
3/5
Temps estimé
1-2 jours
Activité
Calme
Clarté
Clairement spécifiée
Accessibilité débutants
68/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.