SetIntBuffer($Modulus); $publicExponent = new ASNValue(ASNValue::TAG_INTEGER); $publicExponent->SetIntBuffer($PublicExponent); $keySequenceItems = array($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; } function pkcs1_encode($Modulus,$PublicExponent) { //Encode key sequence $modulus = new ASNValue(ASNValue::TAG_INTEGER); $modulus->SetIntBuffer($Modulus); $publicExponent = new ASNValue(ASNValue::TAG_INTEGER); $publicExponent->SetIntBuffer($PublicExponent); $keySequenceItems = array($modulus, $publicExponent); $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE); $keySequence->SetSequence($keySequenceItems); //Encode bit string $bitStringValue = $keySequence->Encode(); return $bitStringValue; } // http://stackoverflow.com/questions/27568570/how-to-convert-raw-modulus-exponent-to-rsa-public-key-pem-format function metopem($m,$e) { $der = pkcs8_encode($m,$e); $key = DerToPem($der,false); return $key; } function pubrsatome($key,&$m,&$e) { require_once('library/asn1.php'); $lines = explode("\n",$key); unset($lines[0]); unset($lines[count($lines)]); $x = base64_decode(implode('',$lines)); $r = ASN_BASE::parseASNString($x); $m = base64url_decode($r[0]->asnData[0]->asnData); $e = base64url_decode($r[0]->asnData[1]->asnData); } function rsatopem($key) { pubrsatome($key,$m,$e); return(metopem($m,$e)); } function pemtorsa($key) { pemtome($key,$m,$e); return(metorsa($m,$e)); } 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); $m = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[0]->asnData); $e = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[1]->asnData); } function metorsa($m,$e) { $der = pkcs1_encode($m,$e); $key = DerToRsa($der); return $key; } function salmon_key($pubkey) { pemtome($pubkey,$m,$e); return 'RSA' . '.' . base64url_encode($m,true) . '.' . base64url_encode($e,true) ; } function convert_salmon_key($key) { if(strstr($key,',')) $rawkey = substr($key,strpos($key,',')+1); else $rawkey = substr($key,5); $key_info = explode('.',$rawkey); $m = base64url_decode($key_info[1]); $e = base64url_decode($key_info[2]); logger('key details: ' . print_r($key_info,true), LOGGER_DATA); $salmon_key = metopem($m,$e); return $salmon_key; } //----------------------------------------------------------------------------- // 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; } } // 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 // 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; $i127){ $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> $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"; } } }