php / php/php-src

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

Abierto
#22,702 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Extension: openssl Feature Status: Verified
Lenguaje dominante
C
Estrellas
40.4k
Forks
8.2k
Merge medio
2 d 13 h
PR fusionados (30 d)
96

Descripción

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.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza en php_openssl_pkey_init_rsa() y en la rama rsa de openssl_pkey_new(), comparándolas con las rutas init_dsa e init_ec existentes. Verifica que las claves RSA que solo contienen n/e sean aceptadas por openssl_verify(), mientras que las claves con d sigan siendo privadas; añade pruebas que cubran ambos casos.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
c, php
Área
cryptography
Tipo de issue
Nueva funcionalidad
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Tranquilo
Claridad
Bien especificado
Aptitud para principiantes
68/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.