From eeb1040cd2bbae52bf60adacd05c01bc30ad32a1 Mon Sep 17 00:00:00 2001 From: NReib <120319811+NReib@users.noreply.github.com> Date: Fri, 27 Mar 2026 12:06:46 +0100 Subject: [PATCH 1/2] fix: RSA from JWK sometimes returns empty Instance #589 addresses specification difference between JWK (or JWA) and ASN1 integers. --- src/JWK.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/JWK.php b/src/JWK.php index d5175b21..182e3ef9 100644 --- a/src/JWK.php +++ b/src/JWK.php @@ -240,6 +240,15 @@ private static function createPemFromModulusAndExponent( ): string { $mod = JWT::urlsafeB64Decode($n); $exp = JWT::urlsafeB64Decode($e); + + // Correct encoding for ASN1, as ints are represented as unsigned in jwk + // but signed in ASN1. Prepending null byte makes it unsigned + if(\strlen($mod) > 0 && \ord($mod[0]) >= 128){ + $mod = \chr(0) . $mod; + } + if(\strlen($exp) > 0 && \ord($exp[0]) >= 128){ + $exp = \chr(0) . $exp; + } $modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod); $publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp); From 50a3ef0a5ea4ee61765ac8609f2878e127361ed1 Mon Sep 17 00:00:00 2001 From: NReib <120319811+NReib@users.noreply.github.com> Date: Fri, 27 Mar 2026 12:20:57 +0100 Subject: [PATCH 2/2] format --- src/JWK.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/JWK.php b/src/JWK.php index 182e3ef9..e083c224 100644 --- a/src/JWK.php +++ b/src/JWK.php @@ -240,15 +240,14 @@ private static function createPemFromModulusAndExponent( ): string { $mod = JWT::urlsafeB64Decode($n); $exp = JWT::urlsafeB64Decode($e); - - // Correct encoding for ASN1, as ints are represented as unsigned in jwk - // but signed in ASN1. Prepending null byte makes it unsigned - if(\strlen($mod) > 0 && \ord($mod[0]) >= 128){ - $mod = \chr(0) . $mod; - } - if(\strlen($exp) > 0 && \ord($exp[0]) >= 128){ - $exp = \chr(0) . $exp; - } + // Correct encoding for ASN1, as ints are represented as unsigned in jwk + // but signed in ASN1. Prepending null byte makes it unsigned. + if (\strlen($mod) > 0 && \ord($mod[0]) >= 128) { + $mod = \chr(0) . $mod; + } + if (\strlen($exp) > 0 && \ord($exp[0]) >= 128) { + $exp = \chr(0) . $exp; + } $modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod); $publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);