Merge pull request #9184 from nupplaphil/task/asn1_composer
Replace library/asn1.php & ASNValue with phpseclib
This commit is contained in:
commit
fe545cef74
|
@ -63,7 +63,8 @@
|
|||
"npm-asset/moment": "^2.24",
|
||||
"npm-asset/perfect-scrollbar": "0.6.16",
|
||||
"npm-asset/textcomplete": "^0.18.2",
|
||||
"npm-asset/typeahead.js": "^0.11.1"
|
||||
"npm-asset/typeahead.js": "^0.11.1",
|
||||
"phpseclib/phpseclib": "^2.0"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
|
|
107
composer.lock
generated
107
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "7d1fe40c28d815b56d0b5cb323860b26",
|
||||
"content-hash": "ffe94190e166cebf80601fc3d6d26be0",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asika/simple-console",
|
||||
|
@ -2474,6 +2474,111 @@
|
|||
"homepage": "http://pear.php.net/package/Text_LanguageDetect",
|
||||
"time": "2020-05-17T12:19:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "2.0.29",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "497856a8d997f640b4a516062f84228a772a48a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/497856a8d997f640b4a516062f84228a772a48a8",
|
||||
"reference": "497856a8d997f640b4a516062f84228a772a48a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phing/phing": "~2.7",
|
||||
"phpunit/phpunit": "^4.8.35|^5.7|^6.0",
|
||||
"squizlabs/php_codesniffer": "~2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
|
||||
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
|
||||
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
|
||||
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"phpseclib/bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"phpseclib\\": "phpseclib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jim Wigginton",
|
||||
"email": "terrafrost@php.net",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Patrick Monnerat",
|
||||
"email": "pm@datasphere.ch",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andreas Fischer",
|
||||
"email": "bantu@phpbb.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Hans-Jürgen Petrich",
|
||||
"email": "petrich@tronic-media.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "graham@alt-three.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
|
||||
"homepage": "http://phpseclib.sourceforge.net",
|
||||
"keywords": [
|
||||
"BigInteger",
|
||||
"aes",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"blowfish",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"encryption",
|
||||
"rsa",
|
||||
"security",
|
||||
"sftp",
|
||||
"signature",
|
||||
"signing",
|
||||
"ssh",
|
||||
"twofish",
|
||||
"x.509",
|
||||
"x509"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/terrafrost",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpseclib",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-09-08T04:24:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pragmarx/google2fa",
|
||||
"version": "v5.0.0",
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
<?php
|
||||
//-----------------------------------------------------------------------------
|
||||
// ASNValue class by A.Oliinyk
|
||||
// contact@pumka.net
|
||||
//-----------------------------------------------------------------------------
|
||||
class ASNValue
|
||||
{
|
||||
const TAG_INTEGER = 0x02;
|
||||
const TAG_BITSTRING = 0x03;
|
||||
const TAG_SEQUENCE = 0x30;
|
||||
|
||||
public $Tag;
|
||||
public $Value;
|
||||
|
||||
function __construct($Tag=0x00, $Value='')
|
||||
{
|
||||
$this->Tag = $Tag;
|
||||
$this->Value = $Value;
|
||||
}
|
||||
|
||||
function Encode()
|
||||
{
|
||||
//Write type
|
||||
$result = chr($this->Tag);
|
||||
|
||||
//Write size
|
||||
$size = strlen($this->Value);
|
||||
if ($size < 127) {
|
||||
//Write size as is
|
||||
$result .= chr($size);
|
||||
}
|
||||
else {
|
||||
//Prepare length sequence
|
||||
$sizeBuf = self::IntToBin($size);
|
||||
|
||||
//Write length sequence
|
||||
$firstByte = 0x80 + strlen($sizeBuf);
|
||||
$result .= chr($firstByte) . $sizeBuf;
|
||||
}
|
||||
|
||||
//Write value
|
||||
$result .= $this->Value;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function Decode(&$Buffer)
|
||||
{
|
||||
//Read type
|
||||
$this->Tag = self::ReadByte($Buffer);
|
||||
|
||||
//Read first byte
|
||||
$firstByte = self::ReadByte($Buffer);
|
||||
|
||||
if ($firstByte < 127) {
|
||||
$size = $firstByte;
|
||||
}
|
||||
else if ($firstByte > 127) {
|
||||
$sizeLen = $firstByte - 0x80;
|
||||
//Read length sequence
|
||||
$size = self::BinToInt(self::ReadBytes($Buffer, $sizeLen));
|
||||
}
|
||||
else {
|
||||
throw new Exception("Invalid ASN length value");
|
||||
}
|
||||
|
||||
$this->Value = self::ReadBytes($Buffer, $size);
|
||||
}
|
||||
|
||||
protected static function ReadBytes(&$Buffer, $Length)
|
||||
{
|
||||
$result = substr($Buffer, 0, $Length);
|
||||
$Buffer = substr($Buffer, $Length);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected static function ReadByte(&$Buffer)
|
||||
{
|
||||
return ord(self::ReadBytes($Buffer, 1));
|
||||
}
|
||||
|
||||
protected static function BinToInt($Bin)
|
||||
{
|
||||
$len = strlen($Bin);
|
||||
$result = 0;
|
||||
for ($i=0; $i<$len; $i++) {
|
||||
$curByte = self::ReadByte($Bin);
|
||||
$result += $curByte << (($len-$i-1)*8);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected static function IntToBin($Int)
|
||||
{
|
||||
$result = '';
|
||||
do {
|
||||
$curByte = $Int % 256;
|
||||
$result .= chr($curByte);
|
||||
|
||||
$Int = ($Int - $curByte) / 256;
|
||||
} while ($Int > 0);
|
||||
|
||||
$result = strrev($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function SetIntBuffer($Value)
|
||||
{
|
||||
if (strlen($Value) > 1) {
|
||||
$firstByte = ord($Value[0]);
|
||||
if ($firstByte & 0x80) { //first bit set
|
||||
$Value = chr(0x00) . $Value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->Value = $Value;
|
||||
}
|
||||
|
||||
function GetIntBuffer()
|
||||
{
|
||||
$result = $this->Value;
|
||||
if (ord($result[0]) == 0x00) {
|
||||
$result = substr($result, 1);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function SetInt($Value)
|
||||
{
|
||||
$Value = self::IntToBin($Value);
|
||||
|
||||
$this->SetIntBuffer($Value);
|
||||
}
|
||||
|
||||
function GetInt()
|
||||
{
|
||||
$result = $this->GetIntBuffer();
|
||||
$result = self::BinToInt($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function SetSequence($Values)
|
||||
{
|
||||
$result = '';
|
||||
foreach ($Values as $item) {
|
||||
$result .= $item->Encode();
|
||||
}
|
||||
|
||||
$this->Value = $result;
|
||||
}
|
||||
|
||||
function GetSequence()
|
||||
{
|
||||
$result = array();
|
||||
$seq = $this->Value;
|
||||
while (strlen($seq)) {
|
||||
$val = new ASNValue();
|
||||
$val->Decode($seq);
|
||||
$result[] = $val;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
292
library/asn1.php
292
library/asn1.php
|
@ -1,292 +0,0 @@
|
|||
<?php
|
||||
|
||||
// ASN.1 parsing library
|
||||
// Attribution: http://www.krisbailey.com
|
||||
// license: unknown
|
||||
// modified: Mike Macgrivin mike@macgirvin.com 6-oct-2010 to support Salmon auto-discovery
|
||||
// modified: Tobias Diekershoff 28-jul-2016 adding an intval in line 162 to make PHP7 happy
|
||||
// from openssl public keys
|
||||
|
||||
|
||||
class ASN_BASE {
|
||||
public $asnData = null;
|
||||
private $cursor = 0;
|
||||
private $parent = null;
|
||||
|
||||
public static $ASN_MARKERS = array(
|
||||
'ASN_UNIVERSAL' => 0x00,
|
||||
'ASN_APPLICATION' => 0x40,
|
||||
'ASN_CONTEXT' => 0x80,
|
||||
'ASN_PRIVATE' => 0xC0,
|
||||
|
||||
'ASN_PRIMITIVE' => 0x00,
|
||||
'ASN_CONSTRUCTOR' => 0x20,
|
||||
|
||||
'ASN_LONG_LEN' => 0x80,
|
||||
'ASN_EXTENSION_ID' => 0x1F,
|
||||
'ASN_BIT' => 0x80,
|
||||
);
|
||||
|
||||
public static $ASN_TYPES = array(
|
||||
1 => 'ASN_BOOLEAN',
|
||||
2 => 'ASN_INTEGER',
|
||||
3 => 'ASN_BIT_STR',
|
||||
4 => 'ASN_OCTET_STR',
|
||||
5 => 'ASN_NULL',
|
||||
6 => 'ASN_OBJECT_ID',
|
||||
9 => 'ASN_REAL',
|
||||
10 => 'ASN_ENUMERATED',
|
||||
13 => 'ASN_RELATIVE_OID',
|
||||
48 => 'ASN_SEQUENCE',
|
||||
49 => 'ASN_SET',
|
||||
19 => 'ASN_PRINT_STR',
|
||||
22 => 'ASN_IA5_STR',
|
||||
23 => 'ASN_UTC_TIME',
|
||||
24 => 'ASN_GENERAL_TIME',
|
||||
);
|
||||
|
||||
function __construct($v = false)
|
||||
{
|
||||
if (false !== $v) {
|
||||
$this->asnData = $v;
|
||||
if (is_array($this->asnData)) {
|
||||
foreach ($this->asnData as $key => $value) {
|
||||
if (is_object($value)) {
|
||||
$this->asnData[$key]->setParent($this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_object($this->asnData)) {
|
||||
$this->asnData->setParent($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setParent($parent)
|
||||
{
|
||||
if (false !== $parent) {
|
||||
$this->parent = $parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will take the markers and types arrays and
|
||||
* dynamically generate classes that extend this class for each one,
|
||||
* and also define constants for them.
|
||||
*/
|
||||
public static function generateSubclasses()
|
||||
{
|
||||
define('ASN_BASE', 0);
|
||||
foreach (self::$ASN_MARKERS as $name => $bit)
|
||||
self::makeSubclass($name, $bit);
|
||||
foreach (self::$ASN_TYPES as $bit => $name)
|
||||
self::makeSubclass($name, $bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for generateSubclasses()
|
||||
*/
|
||||
public static function makeSubclass($name, $bit)
|
||||
{
|
||||
define($name, $bit);
|
||||
eval("class ".$name." extends ASN_BASE {}");
|
||||
}
|
||||
|
||||
/**
|
||||
* This function reset's the internal cursor used for value iteration.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->cursor = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function catches calls to get the value for the type, typeName, value, values, and data
|
||||
* from the object. For type calls we just return the class name or the value of the constant that
|
||||
* is named the same as the class.
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if ('type' == $name) {
|
||||
// int flag of the data type
|
||||
return constant(get_class($this));
|
||||
} elseif ('typeName' == $name) {
|
||||
// name of the data type
|
||||
return get_class($this);
|
||||
} elseif ('value' == $name) {
|
||||
// will always return one value and can be iterated over with:
|
||||
// while ($v = $obj->value) { ...
|
||||
// because $this->asnData["invalid key"] will return false
|
||||
return is_array($this->asnData) ? $this->asnData[$this->cursor++] : $this->asnData;
|
||||
} elseif ('values' == $name) {
|
||||
// will always return an array
|
||||
return is_array($this->asnData) ? $this->asnData : array($this->asnData);
|
||||
} elseif ('data' == $name) {
|
||||
// will always return the raw data
|
||||
return $this->asnData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ASN.1 binary string.
|
||||
*
|
||||
* This function takes a binary ASN.1 string and parses it into it's respective
|
||||
* pieces and returns it. It can optionally stop at any depth.
|
||||
*
|
||||
* @param string $string The binary ASN.1 String
|
||||
* @param int $level The current parsing depth level
|
||||
* @param int $maxLevel The max parsing depth level
|
||||
* @return ASN_BASE The array representation of the ASN.1 data contained in $string
|
||||
*/
|
||||
public static function parseASNString($string=false, $level=1, $maxLevels=false){
|
||||
if (!class_exists('ASN_UNIVERSAL'))
|
||||
self::generateSubclasses();
|
||||
if ($level>$maxLevels && $maxLevels)
|
||||
return array(new ASN_BASE($string));
|
||||
$parsed = array();
|
||||
$endLength = strlen($string);
|
||||
$bigLength = $length = $type = $dtype = $p = 0;
|
||||
while ($p<$endLength){
|
||||
$type = ord($string[$p++]);
|
||||
$dtype = ($type & 192) >> 6;
|
||||
if ($type==0){ // if we are type 0, just continue
|
||||
} else {
|
||||
$length = ord($string[$p++]);
|
||||
if (($length & ASN_LONG_LEN)==ASN_LONG_LEN){
|
||||
$tempLength = 0;
|
||||
for ($x=0; $x<($length & (ASN_LONG_LEN-1)); $x++){
|
||||
$tempLength = @ord($string[$p++]) + ($tempLength * 256);
|
||||
}
|
||||
$length = $tempLength;
|
||||
}
|
||||
$data = substr($string, $p, intval($length));
|
||||
$parsed[] = self::parseASNData($type, $data, $level, $maxLevels);
|
||||
$p = $p + $length;
|
||||
}
|
||||
}
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ASN.1 field value.
|
||||
*
|
||||
* This function takes a binary ASN.1 value and parses it according to it's specified type
|
||||
*
|
||||
* @param int $type The type of data being provided
|
||||
* @param string $data The raw binary data string
|
||||
* @param int $level The current parsing depth
|
||||
* @param int $maxLevels The max parsing depth
|
||||
* @return mixed The data that was parsed from the raw binary data string
|
||||
*/
|
||||
public static function parseASNData($type, $data, $level, $maxLevels){
|
||||
$type = $type%50; // strip out context
|
||||
switch ($type){
|
||||
default:
|
||||
return new ASN_BASE($data);
|
||||
case ASN_BOOLEAN:
|
||||
return new ASN_BOOLEAN((bool)$data);
|
||||
case ASN_INTEGER:
|
||||
return new ASN_INTEGER(strtr(base64_encode($data),'+/','-_'));
|
||||
case ASN_BIT_STR:
|
||||
return new ASN_BIT_STR(self::parseASNString($data, $level+1, $maxLevels));
|
||||
case ASN_OCTET_STR:
|
||||
return new ASN_OCTET_STR($data);
|
||||
case ASN_NULL:
|
||||
return new ASN_NULL(null);
|
||||
case ASN_REAL:
|
||||
return new ASN_REAL($data);
|
||||
case ASN_ENUMERATED:
|
||||
return new ASN_ENUMERATED(self::parseASNString($data, $level+1, $maxLevels));
|
||||
case ASN_RELATIVE_OID: // I don't really know how this works and don't have an example :-)
|
||||
// so, lets just return it ...
|
||||
return new ASN_RELATIVE_OID($data);
|
||||
case ASN_SEQUENCE:
|
||||
return new ASN_SEQUENCE(self::parseASNString($data, $level+1, $maxLevels));
|
||||
case ASN_SET:
|
||||
return new ASN_SET(self::parseASNString($data, $level+1, $maxLevels));
|
||||
case ASN_PRINT_STR:
|
||||
return new ASN_PRINT_STR($data);
|
||||
case ASN_IA5_STR:
|
||||
return new ASN_IA5_STR($data);
|
||||
case ASN_UTC_TIME:
|
||||
return new ASN_UTC_TIME($data);
|
||||
case ASN_GENERAL_TIME:
|
||||
return new ASN_GENERAL_TIME($data);
|
||||
case ASN_OBJECT_ID:
|
||||
return new ASN_OBJECT_ID(self::parseOID($data));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ASN.1 OID value.
|
||||
*
|
||||
* This takes the raw binary string that represents an OID value and parses it into its
|
||||
* dot notation form. example - 1.2.840.113549.1.1.5
|
||||
* look up OID's here: http://www.oid-info.com/
|
||||
* (the multi-byte OID section can be done in a more efficient way, I will fix it later)
|
||||
*
|
||||
* @param string $data The raw binary data string
|
||||
* @return string The OID contained in $data
|
||||
*/
|
||||
public static function parseOID($string){
|
||||
$ret = floor(ord($string[0])/40).".";
|
||||
$ret .= (ord($string[0]) % 40);
|
||||
$build = array();
|
||||
$cs = 0;
|
||||
|
||||
for ($i=1; $i<strlen($string); $i++){
|
||||
$v = ord($string[$i]);
|
||||
if ($v>127){
|
||||
$build[] = ord($string[$i])-ASN_BIT;
|
||||
} elseif ($build){
|
||||
// do the build here for multibyte values
|
||||
$build[] = ord($string[$i])-ASN_BIT;
|
||||
// you know, it seems there should be a better way to do this...
|
||||
$build = array_reverse($build);
|
||||
$num = 0;
|
||||
for ($x=0; $x<count($build); $x++){
|
||||
$mult = $x==0?1:pow(256, $x);
|
||||
if ($x+1==count($build)){
|
||||
$value = ((($build[$x] & (ASN_BIT-1)) >> $x)) * $mult;
|
||||
} else {
|
||||
$value = ((($build[$x] & (ASN_BIT-1)) >> $x) ^ ($build[$x+1] << (7 - $x) & 255)) * $mult;
|
||||
}
|
||||
$num += $value;
|
||||
}
|
||||
$ret .= ".".$num;
|
||||
$build = array(); // start over
|
||||
} else {
|
||||
$ret .= ".".$v;
|
||||
$build = array();
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function printASN($x, $indent=''){
|
||||
if (is_object($x)) {
|
||||
echo $indent.$x->typeName."\n";
|
||||
if (ASN_NULL == $x->type) return;
|
||||
if (is_array($x->data)) {
|
||||
while ($d = $x->value) {
|
||||
echo self::printASN($d, $indent.'. ');
|
||||
}
|
||||
$x->reset();
|
||||
} else {
|
||||
echo self::printASN($x->data, $indent.'. ');
|
||||
}
|
||||
} elseif (is_array($x)) {
|
||||
foreach ($x as $d) {
|
||||
echo self::printASN($d, $indent);
|
||||
}
|
||||
} else {
|
||||
if (preg_match('/[^[:print:]]/', $x)) // if we have non-printable characters that would
|
||||
$x = base64_encode($x); // mess up the console, then print the base64 of them...
|
||||
echo $indent.$x."\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -21,11 +21,13 @@
|
|||
|
||||
namespace Friendica\Module;
|
||||
|
||||
use ASN_BASE;
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
use Friendica\Util\Crypto;
|
||||
use Friendica\Util\Strings;
|
||||
use phpseclib\File\ASN1;
|
||||
|
||||
/**
|
||||
* prints the public RSA key of a user
|
||||
|
@ -49,18 +51,10 @@ class PublicRSAKey extends BaseModule
|
|||
throw new BadRequestException();
|
||||
}
|
||||
|
||||
$lines = explode("\n", $user['spubkey']);
|
||||
unset($lines[0]);
|
||||
unset($lines[count($lines)]);
|
||||
|
||||
$asnString = base64_decode(implode('', $lines));
|
||||
$asnBase = ASN_BASE::parseASNString($asnString);
|
||||
|
||||
$m = $asnBase[0]->asnData[1]->asnData[0]->asnData[0]->asnData;
|
||||
$e = $asnBase[0]->asnData[1]->asnData[0]->asnData[1]->asnData;
|
||||
Crypto::pemToMe($user['spubkey'], $modulus, $exponent);
|
||||
|
||||
header('Content-type: application/magic-public-key');
|
||||
echo 'RSA' . '.' . $m . '.' . $e;
|
||||
echo 'RSA' . '.' . Strings::base64UrlEncode($modulus, true) . '.' . Strings::base64UrlEncode($exponent, true);
|
||||
|
||||
exit();
|
||||
}
|
||||
|
|
|
@ -229,7 +229,7 @@ class Salmon
|
|||
*/
|
||||
public static function salmonKey($pubkey)
|
||||
{
|
||||
Crypto::pemToMe($pubkey, $m, $e);
|
||||
return 'RSA' . '.' . Strings::base64UrlEncode($m, true) . '.' . Strings::base64UrlEncode($e, true);
|
||||
Crypto::pemToMe($pubkey, $modulus, $exponent);
|
||||
return 'RSA' . '.' . Strings::base64UrlEncode($modulus, true) . '.' . Strings::base64UrlEncode($exponent, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,12 @@
|
|||
|
||||
namespace Friendica\Util;
|
||||
|
||||
use ASN_BASE;
|
||||
use ASNValue;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use phpseclib\Crypt\RSA;
|
||||
use phpseclib\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Crypto class
|
||||
|
@ -65,97 +65,6 @@ class Crypto
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string $Der der formatted string
|
||||
* @param bool $Private key type optional, default false
|
||||
* @return string
|
||||
*/
|
||||
private static function DerToPem($Der, $Private = false)
|
||||
{
|
||||
//Encode:
|
||||
$Der = base64_encode($Der);
|
||||
//Split lines:
|
||||
$lines = str_split($Der, 65);
|
||||
$body = implode("\n", $lines);
|
||||
//Get title:
|
||||
$title = $Private ? 'RSA PRIVATE KEY' : 'PUBLIC KEY';
|
||||
//Add wrapping:
|
||||
$result = "-----BEGIN {$title}-----\n";
|
||||
$result .= $body . "\n";
|
||||
$result .= "-----END {$title}-----\n";
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $Der der formatted string
|
||||
* @return string
|
||||
*/
|
||||
private static function DerToRsa($Der)
|
||||
{
|
||||
//Encode:
|
||||
$Der = base64_encode($Der);
|
||||
//Split lines:
|
||||
$lines = str_split($Der, 64);
|
||||
$body = implode("\n", $lines);
|
||||
//Get title:
|
||||
$title = 'RSA PUBLIC KEY';
|
||||
//Add wrapping:
|
||||
$result = "-----BEGIN {$title}-----\n";
|
||||
$result .= $body . "\n";
|
||||
$result .= "-----END {$title}-----\n";
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $Modulus modulo
|
||||
* @param string $PublicExponent exponent
|
||||
* @return string
|
||||
*/
|
||||
private static function pkcs8Encode($Modulus, $PublicExponent)
|
||||
{
|
||||
//Encode key sequence
|
||||
$modulus = new ASNValue(ASNValue::TAG_INTEGER);
|
||||
$modulus->SetIntBuffer($Modulus);
|
||||
$publicExponent = new ASNValue(ASNValue::TAG_INTEGER);
|
||||
$publicExponent->SetIntBuffer($PublicExponent);
|
||||
$keySequenceItems = [$modulus, $publicExponent];
|
||||
$keySequence = new ASNValue(ASNValue::TAG_SEQUENCE);
|
||||
$keySequence->SetSequence($keySequenceItems);
|
||||
//Encode bit string
|
||||
$bitStringValue = $keySequence->Encode();
|
||||
$bitStringValue = chr(0x00) . $bitStringValue; //Add unused bits byte
|
||||
$bitString = new ASNValue(ASNValue::TAG_BITSTRING);
|
||||
$bitString->Value = $bitStringValue;
|
||||
//Encode body
|
||||
$bodyValue = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" . $bitString->Encode();
|
||||
$body = new ASNValue(ASNValue::TAG_SEQUENCE);
|
||||
$body->Value = $bodyValue;
|
||||
//Get DER encoded public key:
|
||||
$PublicDER = $body->Encode();
|
||||
return $PublicDER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $Modulus modulo
|
||||
* @param string $PublicExponent exponent
|
||||
* @return string
|
||||
*/
|
||||
private static function pkcs1Encode($Modulus, $PublicExponent)
|
||||
{
|
||||
//Encode key sequence
|
||||
$modulus = new ASNValue(ASNValue::TAG_INTEGER);
|
||||
$modulus->SetIntBuffer($Modulus);
|
||||
$publicExponent = new ASNValue(ASNValue::TAG_INTEGER);
|
||||
$publicExponent->SetIntBuffer($PublicExponent);
|
||||
$keySequenceItems = [$modulus, $publicExponent];
|
||||
$keySequence = new ASNValue(ASNValue::TAG_SEQUENCE);
|
||||
$keySequence->SetSequence($keySequenceItems);
|
||||
//Encode bit string
|
||||
$bitStringValue = $keySequence->Encode();
|
||||
return $bitStringValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $m modulo
|
||||
* @param string $e exponent
|
||||
|
@ -163,85 +72,46 @@ class Crypto
|
|||
*/
|
||||
public static function meToPem($m, $e)
|
||||
{
|
||||
$der = self::pkcs8Encode($m, $e);
|
||||
$key = self::DerToPem($der, false);
|
||||
return $key;
|
||||
$rsa = new RSA();
|
||||
$rsa->loadKey([
|
||||
'e' => new BigInteger($e, 256),
|
||||
'n' => new BigInteger($m, 256)
|
||||
]);
|
||||
return $rsa->getPublicKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key key
|
||||
* @param string $m modulo reference
|
||||
* @param object $e exponent reference
|
||||
* Transform RSA public keys to standard PEM output
|
||||
*
|
||||
* @param string $key A RSA public key
|
||||
*
|
||||
* @return string The PEM output of this key
|
||||
*/
|
||||
public static function rsaToPem(string $key)
|
||||
{
|
||||
$rsa = new RSA();
|
||||
$rsa->setPublicKey($key);
|
||||
|
||||
return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the modulo and exponent reference from a public PEM key
|
||||
*
|
||||
* @param string $key public PEM key
|
||||
* @param string $modulus (ref) modulo reference
|
||||
* @param string $exponent (ref) exponent reference
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function pubRsaToMe($key, &$m, &$e)
|
||||
public static function pemToMe(string $key, &$modulus, &$exponent)
|
||||
{
|
||||
$lines = explode("\n", $key);
|
||||
unset($lines[0]);
|
||||
unset($lines[count($lines)]);
|
||||
$x = base64_decode(implode('', $lines));
|
||||
$rsa = new RSA();
|
||||
$rsa->loadKey($key);
|
||||
$rsa->setPublicKey();
|
||||
|
||||
$r = ASN_BASE::parseASNString($x);
|
||||
|
||||
$m = Strings::base64UrlDecode($r[0]->asnData[0]->asnData);
|
||||
$e = Strings::base64UrlDecode($r[0]->asnData[1]->asnData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key key
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function rsaToPem($key)
|
||||
{
|
||||
self::pubRsaToMe($key, $m, $e);
|
||||
return self::meToPem($m, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key key
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function pemToRsa($key)
|
||||
{
|
||||
self::pemToMe($key, $m, $e);
|
||||
return self::meToRsa($m, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key key
|
||||
* @param string $m modulo reference
|
||||
* @param string $e exponent reference
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function pemToMe($key, &$m, &$e)
|
||||
{
|
||||
$lines = explode("\n", $key);
|
||||
unset($lines[0]);
|
||||
unset($lines[count($lines)]);
|
||||
$x = base64_decode(implode('', $lines));
|
||||
|
||||
$r = ASN_BASE::parseASNString($x);
|
||||
|
||||
if (isset($r[0])) {
|
||||
$m = Strings::base64UrlDecode($r[0]->asnData[1]->asnData[0]->asnData[0]->asnData);
|
||||
$e = Strings::base64UrlDecode($r[0]->asnData[1]->asnData[0]->asnData[1]->asnData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $m modulo
|
||||
* @param string $e exponent
|
||||
* @return string
|
||||
*/
|
||||
private static function meToRsa($m, $e)
|
||||
{
|
||||
$der = self::pkcs1Encode($m, $e);
|
||||
$key = self::DerToRsa($der);
|
||||
return $key;
|
||||
$modulus = $rsa->modulus->toBytes();
|
||||
$exponent = $rsa->exponent->toBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -282,13 +152,13 @@ class Crypto
|
|||
|
||||
/**
|
||||
* Encrypt a string with 'aes-256-cbc' cipher method.
|
||||
*
|
||||
*
|
||||
* Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/include/crypto.php
|
||||
*
|
||||
*
|
||||
* @param string $data
|
||||
* @param string $key The key used for encryption.
|
||||
* @param string $iv A non-NULL Initialization Vector.
|
||||
*
|
||||
*
|
||||
* @return string|boolean Encrypted string or false on failure.
|
||||
*/
|
||||
private static function encryptAES256CBC($data, $key, $iv)
|
||||
|
@ -298,13 +168,13 @@ class Crypto
|
|||
|
||||
/**
|
||||
* Decrypt a string with 'aes-256-cbc' cipher method.
|
||||
*
|
||||
*
|
||||
* Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/include/crypto.php
|
||||
*
|
||||
*
|
||||
* @param string $data
|
||||
* @param string $key The key used for decryption.
|
||||
* @param string $iv A non-NULL Initialization Vector.
|
||||
*
|
||||
*
|
||||
* @return string|boolean Decrypted string or false on failure.
|
||||
*/
|
||||
private static function decryptAES256CBC($data, $key, $iv)
|
||||
|
@ -312,42 +182,6 @@ class Crypto
|
|||
return openssl_decrypt($data, 'aes-256-cbc', str_pad($key, 32, "\0"), OPENSSL_RAW_DATA, str_pad($iv, 16, "\0"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a string with 'aes-256-ctr' cipher method.
|
||||
*
|
||||
* Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/include/crypto.php
|
||||
*
|
||||
* @param string $data
|
||||
* @param string $key The key used for encryption.
|
||||
* @param string $iv A non-NULL Initialization Vector.
|
||||
*
|
||||
* @return string|boolean Encrypted string or false on failure.
|
||||
*/
|
||||
private static function encryptAES256CTR($data, $key, $iv)
|
||||
{
|
||||
$key = substr($key, 0, 32);
|
||||
$iv = substr($iv, 0, 16);
|
||||
return openssl_encrypt($data, 'aes-256-ctr', str_pad($key, 32, "\0"), OPENSSL_RAW_DATA, str_pad($iv, 16, "\0"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a string with 'aes-256-ctr' cipher method.
|
||||
*
|
||||
* Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/include/crypto.php
|
||||
*
|
||||
* @param string $data
|
||||
* @param string $key The key used for decryption.
|
||||
* @param string $iv A non-NULL Initialization Vector.
|
||||
*
|
||||
* @return string|boolean Decrypted string or false on failure.
|
||||
*/
|
||||
private static function decryptAES256CTR($data, $key, $iv)
|
||||
{
|
||||
$key = substr($key, 0, 32);
|
||||
$iv = substr($iv, 0, 16);
|
||||
return openssl_decrypt($data, 'aes-256-ctr', str_pad($key, 32, "\0"), OPENSSL_RAW_DATA, str_pad($iv, 16, "\0"));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/include/crypto.php
|
||||
|
|
6
tests/datasets/crypto/rsa/diaspora-public-pem
Normal file
6
tests/datasets/crypto/rsa/diaspora-public-pem
Normal file
|
@ -0,0 +1,6 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDReSjW7O4u4tK+UGKwogyw4Dok
|
||||
j1Z4f70INc4CTlHk2sngzTa3uMzk1EU+9nYigqMfI1/DYoSCC0ZqikvZVGkrMJj6
|
||||
khM7orTasR4Av9Sn54rOQaM+raUC3JXd9AdkdXx1IBC71cAXVqIg/ERCrrUpxDxc
|
||||
E6VXs4mFWpDHJ4q01QIDAQAB
|
||||
-----END PUBLIC KEY-----
|
1
tests/datasets/crypto/rsa/diaspora-public-rsa-base64
Normal file
1
tests/datasets/crypto/rsa/diaspora-public-rsa-base64
Normal file
|
@ -0,0 +1 @@
|
|||
LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tDQpNSUdKQW9HQkFORjVLTmJzN2k3aTByNVFZckNpRExEZ09pU1BWbmgvdlFnMXpnSk9VZVRheWVETk5yZTR6T1RVDQpSVDcyZGlLQ294OGpYOE5paElJTFJtcUtTOWxVYVNzd21QcVNFenVpdE5xeEhnQy8xS2ZuaXM1Qm96NnRwUUxjDQpsZDMwQjJSMWZIVWdFTHZWd0JkV29pRDhSRUt1dFNuRVBGd1RwVmV6aVlWYWtNY25pclRWQWdNQkFBRT0NCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0
|
|
@ -21,6 +21,8 @@
|
|||
*/
|
||||
namespace Friendica\Util;
|
||||
|
||||
use phpseclib\Crypt\RSA;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class CryptoTest extends TestCase
|
||||
|
@ -32,7 +34,7 @@ class CryptoTest extends TestCase
|
|||
private function assertRandomInt($min, $max)
|
||||
{
|
||||
global $phpMock;
|
||||
$phpMock['random_int'] = function($mMin, $mMax) use ($min, $max) {
|
||||
$phpMock['random_int'] = function ($mMin, $mMax) use ($min, $max) {
|
||||
$this->assertEquals($min, $mMin);
|
||||
$this->assertEquals($max, $mMax);
|
||||
return 1;
|
||||
|
@ -51,6 +53,62 @@ class CryptoTest extends TestCase
|
|||
$this->assertEquals(8, strlen($test));
|
||||
$this->assertEquals(11111111, $test);
|
||||
}
|
||||
|
||||
public function dataRsa()
|
||||
{
|
||||
return [
|
||||
'diaspora' => [
|
||||
'key' => file_get_contents(__DIR__ . '/../../datasets/crypto/rsa/diaspora-public-rsa-base64'),
|
||||
'expected' => file_get_contents(__DIR__ . '/../../datasets/crypto/rsa/diaspora-public-pem'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataRsa
|
||||
*/
|
||||
public function testPubRsaToMe(string $key, string $expected)
|
||||
{
|
||||
$this->assertEquals($expected, Crypto::rsaToPem(base64_decode($key)));
|
||||
}
|
||||
|
||||
|
||||
public function dataPEM()
|
||||
{
|
||||
return [
|
||||
'diaspora' => [
|
||||
'key' => file_get_contents(__DIR__ . '/../../datasets/crypto/rsa/diaspora-public-pem'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataPEM
|
||||
*/
|
||||
public function testPemToMe(string $key)
|
||||
{
|
||||
Crypto::pemToMe($key, $m, $e);
|
||||
|
||||
$expectedRSA = new RSA();
|
||||
$expectedRSA->loadKey([
|
||||
'e' => new BigInteger($e, 256),
|
||||
'n' => new BigInteger($m, 256)
|
||||
]);
|
||||
|
||||
$this->assertEquals($expectedRSA->getPublicKey(), $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataPEM
|
||||
*/
|
||||
public function testMeToPem(string $key)
|
||||
{
|
||||
Crypto::pemToMe($key, $m, $e);
|
||||
|
||||
$checkKey = Crypto::meToPem($m, $e);
|
||||
|
||||
$this->assertEquals($key, $checkKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue