Skip to content

Commit

Permalink
Refactor DER parsing functions
Browse files Browse the repository at this point in the history
  • Loading branch information
kelvinmo committed Sep 21, 2024
1 parent e2fdc61 commit d6c98ab
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 68 deletions.
91 changes: 47 additions & 44 deletions src/SimpleJWT/Keys/ECKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,30 +163,7 @@ public function __construct($data, string $format, ?string $password = null, ?st

$seq = $der->decode($binary);

$version = $seq->getChildAt(0)->getValue();
if ($version != 1) throw new KeyException('Invalid private key version: ' . $version);

$d = $seq->getChildAt(1)->getValue();

$curve_oid = $seq->getChildWithTag(0)->getValue();
$curve = self::getCurveNameFromOID($curve_oid);
if ($curve == null) throw new KeyException('Unrecognised EC parameter: ' . $curve_oid);

$len = self::$curves[$curve]['len'];

$point = $seq->getChildWithTag(1)->getValue();
if (strlen($point) != $len + 1) throw new KeyException('Incorrect private key length: ' . strlen($point));

if (ord($point[0]) != 0x04) throw new KeyException('Invalid private key'); // W

$x = substr($point, 1, $len / 2);
$y = substr($point, 1 + $len / 2);

$jwk['kty'] = self::KTY;
$jwk['crv'] = $curve;
$jwk['d'] = Util::base64url_encode($d);
$jwk['x'] = Util::base64url_encode($x);
$jwk['y'] = Util::base64url_encode($y);
$jwk = self::parseASN1PrivateKey($seq);
} elseif (preg_match(Key::PEM_PKCS8_PRIVATE, $data, $matches)) {
/** @var string $binary */
$binary = base64_decode($matches[1]);
Expand All @@ -204,29 +181,10 @@ public function __construct($data, string $format, ?string $password = null, ?st
$curve = self::getCurveNameFromOID($curve_oid);
if ($curve == null) throw new KeyException('Unrecognised EC parameter: ' . $curve_oid);

$len = self::$curves[$curve]['len'];

$private_octet_string = $seq->getChildAt(2)->getValue();
$private_seq = $der->decode($private_octet_string);

$version = $private_seq->getChildAt(0)->getValue();
if ($version != 1) throw new KeyException('Invalid private key version: ' . $version);

$d = $private_seq->getChildAt(1)->getValue();

$point = $private_seq->getChildWithTag(1)->getValue();
if (strlen($point) != $len + 1) throw new KeyException('Incorrect private key length: ' . strlen($point));

if (ord($point[0]) != 0x04) throw new KeyException('Invalid private key'); // W

$x = substr($point, 1, $len / 2);
$y = substr($point, 1 + $len / 2);

$jwk['kty'] = self::KTY;
$jwk['crv'] = $curve;
$jwk['d'] = Util::base64url_encode($d);
$jwk['x'] = Util::base64url_encode($x);
$jwk['y'] = Util::base64url_encode($y);
$jwk = self::parseASN1PrivateKey($private_seq, $curve);
} else {
throw new KeyException('Unrecognised key format');
}
Expand Down Expand Up @@ -395,6 +353,51 @@ protected function getThumbnailMembers(): array {
return ['crv', 'kty', 'x', 'y'];
}

/**
* Parses an EC private key in DER form.
*
* An EC private key is encoded using the ECPrivateKey type as per SEC 1.
*
* @param ASN1Value $seq the ASN.1 sequence to parse
* @param string $curve the name of the elliptic curve. If null, this will be
* read from the sequence
* @return array<string, mixed> the parsed private key data
* @throws KeyException if an error occurs in parsing the key
*/
protected static function parseASN1PrivateKey(ASN1Value $seq, $curve = null): array {
$version = $seq->getChildAt(0)->getValue();
if ($version != 1) throw new KeyException('Invalid private key version: ' . $version);

$d = $seq->getChildAt(1)->getValue();

if ($curve == null) {
$curve_oid_param = $seq->getChildWithTag(0);
if ($curve_oid_param == null) throw new KeyException('Missing EC curve parameter');
$curve_oid = $curve_oid_param->getValue();
$curve = self::getCurveNameFromOID($curve_oid);
if ($curve == null) throw new KeyException('Unrecognised EC parameter: ' . $curve_oid);
}

if (!isset(self::$curves[$curve])) throw new KeyException('Curve not found');
$len = self::$curves[$curve]['len'];

$point = $seq->getChildWithTag(1)->getValue();
if (strlen($point) != $len + 1) throw new KeyException('Incorrect private key length: ' . strlen($point));

if (ord($point[0]) != 0x04) throw new KeyException('Invalid private key'); // W

$x = substr($point, 1, $len / 2);
$y = substr($point, 1 + $len / 2);

return [
'kty' => self::KTY,
'crv' => $curve,
'd' => Util::base64url_encode($d),
'x' => Util::base64url_encode($x),
'y' => Util::base64url_encode($y)
];
}

private static function getCurveNameFromOID(string $curve_oid): ?string {
foreach (self::$curves as $crv => $params) {
if ($params['oid'] == $curve_oid) return $crv;
Expand Down
52 changes: 28 additions & 24 deletions src/SimpleJWT/Keys/RSAKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,7 @@ public function __construct($data, string $format, ?string $password = null, ?st

$seq = $der->decode($binary);

$version = $seq->getChildAt(0)->getValue();
if ($version != 0) throw new KeyException('Unsupported RSA private key version');

$jwk['kty'] = self::KTY;
$jwk['n'] = Util::base64url_encode($seq->getChildAt(1)->getValueAsUIntOctets());
$jwk['e'] = Util::base64url_encode($seq->getChildAt(2)->getValueAsUIntOctets());
$jwk['d'] = Util::base64url_encode($seq->getChildAt(3)->getValueAsUIntOctets());
$jwk['p'] = Util::base64url_encode($seq->getChildAt(4)->getValueAsUIntOctets());
$jwk['q'] = Util::base64url_encode($seq->getChildAt(5)->getValueAsUIntOctets());
$jwk['dp'] = Util::base64url_encode($seq->getChildAt(6)->getValueAsUIntOctets());
$jwk['dq'] = Util::base64url_encode($seq->getChildAt(7)->getValueAsUIntOctets());
$jwk['qi'] = Util::base64url_encode($seq->getChildAt(8)->getValueAsUIntOctets());
$jwk = self::parseASN1PrivateKey($seq);
} elseif (preg_match(Key::PEM_PKCS8_PRIVATE, $data, $matches)) {
/** @var string $binary */
$binary = base64_decode($matches[1]);
Expand All @@ -135,18 +124,7 @@ public function __construct($data, string $format, ?string $password = null, ?st
$private_octet_string = $seq->getChildAt(2)->getValue();
$private_seq = $der->decode($private_octet_string);

$version = $private_seq->getChildAt(0)->getValue();
if ($version != 0) throw new KeyException('Unsupported RSA private key version');

$jwk['kty'] = self::KTY;
$jwk['n'] = Util::base64url_encode($private_seq->getChildAt(1)->getValueAsUIntOctets());
$jwk['e'] = Util::base64url_encode($private_seq->getChildAt(2)->getValueAsUIntOctets());
$jwk['d'] = Util::base64url_encode($private_seq->getChildAt(3)->getValueAsUIntOctets());
$jwk['p'] = Util::base64url_encode($private_seq->getChildAt(4)->getValueAsUIntOctets());
$jwk['q'] = Util::base64url_encode($private_seq->getChildAt(5)->getValueAsUIntOctets());
$jwk['dp'] = Util::base64url_encode($private_seq->getChildAt(6)->getValueAsUIntOctets());
$jwk['dq'] = Util::base64url_encode($private_seq->getChildAt(7)->getValueAsUIntOctets());
$jwk['qi'] = Util::base64url_encode($private_seq->getChildAt(8)->getValueAsUIntOctets());
$jwk = self::parseASN1PrivateKey($private_seq);
} else {
throw new KeyException('Unrecognised key format');
}
Expand Down Expand Up @@ -221,6 +199,32 @@ protected function getThumbnailMembers(): array {
// https://tools.ietf.org/html/rfc7638#section-3.2
return ['e', 'kty', 'n'];
}

/**
* Parses an RSA private key in DER form.
*
* An RSA private key is encoded using the RSAPrivateKey type as per PKCS#1
*
* @param ASN1Value $seq the ASN.1 sequence to parse
* @return array<string, mixed> the parsed private key data
* @throws KeyException if an error occurs in parsing the key
*/
protected static function parseASN1PrivateKey(ASN1Value $seq): array {
$version = $seq->getChildAt(0)->getValue();
if ($version != 0) throw new KeyException('Unsupported RSA private key version');

return [
'kty' => self::KTY,
'n' => Util::base64url_encode($seq->getChildAt(1)->getValueAsUIntOctets()),
'e' => Util::base64url_encode($seq->getChildAt(2)->getValueAsUIntOctets()),
'd' => Util::base64url_encode($seq->getChildAt(3)->getValueAsUIntOctets()),
'p' => Util::base64url_encode($seq->getChildAt(4)->getValueAsUIntOctets()),
'q' => Util::base64url_encode($seq->getChildAt(5)->getValueAsUIntOctets()),
'dp' => Util::base64url_encode($seq->getChildAt(6)->getValueAsUIntOctets()),
'dq' => Util::base64url_encode($seq->getChildAt(7)->getValueAsUIntOctets()),
'qi' => Util::base64url_encode($seq->getChildAt(8)->getValueAsUIntOctets())
];
}
}

?>

0 comments on commit d6c98ab

Please sign in to comment.