Warning: openssl_sign() expects parameter 4 to be long - php

I'm trying to generate a JWT for using apple mapkit js. I found a open source code that generates the token which works fine on my local machine. However, I'm getting the following error when I uploaded to my server.
Warning: openssl_sign() expects parameter 4 to be long
The line that's causing the problem is
if (!openssl_sign($payload, $result, $key, OPENSSL_ALGO_SHA256));
<?php
/**
* Copyright 2018 Includable
* Created by Thomas Schoffelen
*/
namespace Mapkit;
/**
* Class JWT
*
* #package Mapkit
*/
class JWT
{
/**
* Generates a JWT token that can be used for MapKit JS or MusicKit authorization.
*
* #param string $private_key Contents of, or path to, private key file
* #param string $key_id Key ID provided by Apple
* #param string $team_id Apple Developer Team Identifier
* #param string $origin Optionally limit header origin
* #param integer $expiry The expiry timeout in seconds (defaults to 3600)
* #return string|false
*/
public static function getToken($private_key, $key_id, $team_id, $origin = null, $expiry = 3600)
{
$header = [
'alg' => 'ES256',
'typ' => 'JWT',
'kid' => $key_id
];
$body = [
'iss' => $team_id,
'iat' => time(),
'exp' => time() + $expiry
];
if ($origin) {
$body['origin'] = $origin;
}
$payload = self::encode(json_encode($header)) . '.' . self::encode(json_encode($body));
if (!$key = openssl_pkey_get_private($private_key)) {
return false;
}
if (!openssl_sign($payload, $result, $key, OPENSSL_ALGO_SHA256)) { //this is the line that's cause a problem
return false;
}
return $payload . '.' . self::encode($result);
}
/**
* URL-safe base64 encoding.
*
* #param string $data
* #return string
*/
private static function encode($data)
{
$encoded = strtr(base64_encode($data), '+/', '-_');
return rtrim($encoded, '=');
}
}

Related

What are the best practices when redirecting users after OAuth 2.0 token renew?

I have implemented an Mautic API in a website. I use OAuth 2.0 to authenticate the communications between the two. The problem that I have is that I must renew the token from time to time, and in order to do that I have to provide a callback URL, I figured that I just have to use http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI] as my callback URL, that way, when the authentication or renew is done the user would be redirected to the last called URL. The problem is that sometimes the user is being redirected to the login page of the API to authorize the integration. That, to the best of my knowledge, should be done by me, only once.
In short, how can I avoid my users being shown the API authentication screen?
I haven't finished the integration yet; I still must work on some security issues.
The class responsible for doing that is right below:
<?php
use Mautic\Auth\ApiAuth;
use Mautic\MauticApi;
class Mautic
{
private static $instance;
private $publicKey;
private $secretKey;
private $callback;
private $baseURL;
private $Api;
private $ApiURL;
private $auth;
private $token;
private $companyName;
public function __construct()
{
$config = $this->getConfig();
$this->publicKey = $config['publicKey'];
$this->secretKey = $config['secretKey'];
$this->baseURL = $config['baseURL'];
$this->companyName = $config['companyName'];
$this->Api = new MauticApi();
$this->ApiURL = $this->baseURL . '/api/';
if (!$this->isTokenValid()) {
$this->getToken();
}
}
/**
* Read the config file "mautic.json" located in the root directory and returns an array with config values
*
* #return array
*/
private function getConfig(): array
{
return $this->getJSON('mautic.json');
}
/**
* Instantiates a new API class
*
* #param string $apiName
* #return object
*/
private function setApi(string $apiName): object
{
if(!$this->auth){
$this->getToken();
}
return $this->Api->newApi($apiName, $this->auth, $this->ApiURL);
}
/**
* Retorna la instancia de la clase actual
*
* #return object
*/
public static function getInstance(): object
{
if (!self::$instance)
self::$instance = new self();
return self::$instance;
}
public function isTokenValid(): bool
{
$oldTokenExpiration = $this->checkForToken()['expires'];
if (time() >= $oldTokenExpiration) {
return false;
}
return true;
}
private function getToken($accessToken = null, $tokenExpiration = null, $refreshToken = null)
{
if ($previousToken = $this->checkForToken()) {
$settings['accessToken'] = $previousToken['access_token'];
$settings['accessTokenExpires'] = $previousToken['expires'];
$settings['refreshToken'] = $previousToken['refresh_token'];
}
$settings = [
'baseUrl' => $this->baseURL, // Base URL of the Mautic instance
'version' => 'OAuth2', // Version of the OAuth
'clientKey' => $this->publicKey, // Client/Consumer key from Mautic
'clientSecret' => $this->secretKey, // Client/Consumer secret key from Mautic
'callback' => "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"
];
if (isset($accessToken) && isset($tokenExpiration) && isset($refreshToken)) {
}
$initAuth = new ApiAuth();
$auth = $initAuth->newAuth($settings);
// Initiate process for obtaining an access token; this will redirect the user to the authorize endpoint and/or set the tokens when the user is redirected back after granting authorization
if ($auth->validateAccessToken()) {
if ($auth->accessTokenUpdated()) {
$accessTokenData = $auth->getAccessTokenData();
$this->storeToken($accessTokenData);
$this->auth = $auth;
$this->token = $accessTokenData['access_token'];
return $this->auth;
}
}
}
private function storeToken($accessTokenData)
{
$tokenInfo = json_encode($accessTokenData);
file_put_contents("token.json", $tokenInfo);
}
/**
* Read the file "token.json" located in the root directory and returns an array with any passed token values
*
* #return array
*/
private function checkForToken(): array
{
return $this->getJSON('token.json');
}
/**
* Reads a JSON file and returns its key and values as an array
*
* #param string $filePath
* #return array
*/
private function getJSON($filePath): array
{
if (!file_exists($filePath)) {
return false;
}
$oldToken = file_get_contents($filePath);
$oldToken = json_decode($oldToken);
return (array) $oldToken;
}
/**
* Creates a new contact
*
* #param string $name
* #param string $phone
* #param string $email
* #param string $companyName
* #return array
*/
public function createContact(string $name, string $phone, string $email, int $companyName = null): array
{
if ($companyName == null) {
$companyName = $this->getConfig()['companyName'];
}
$data = array(
'firstname' => $name,
'phone' => $phone,
'email' => $email,
'company' => $companyName,
'ipAddress' => $_SERVER['REMOTE_ADDR'],
'overwriteWithBlank' => true,
);
$contactApi = $this->setApi('contacts');
$newContact = $contactApi->create($data);
return $newContact;
}
/**
* Retorna los datos de un contacto
*
* #param int $contactId
* #return object
*/
public function getContact(int $contactId): object
{
return json_decode($this->curlGET("contacts", array($contactId)));
}
/**
* Ejecuta una requisición GET al servidor de la API
*
* #param string $APIMethod
* #param array $dataToSend
* #return string
*/
private function curlGET(string $APIMethod, array $dataToSend = array()): string
{
$dataToSend["access_token"] = $this->token;
$baseURL = $this->ApiURL . $APIMethod;
$curl = curl_init();
$curlOptions = array(
CURLOPT_URL => $baseURL . '?' . http_build_query($dataToSend),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSL_VERIFYPEER => false
);
curl_setopt_array($curl, $curlOptions);
$returnedData = curl_exec($curl);
if (!$returnedData) {
return curl_error($curl);
} else {
curl_close($curl);
return $returnedData;
}
}
}
The problem seems to be re-authentication. Once you authenticate successfully you should not need to do that again and again.
You get Token, Token Expires and Refresh Token when the process is complete. Here's complete example(https://tutorialsjoint.com/mautic-rest-api/).
Once you have the token and you are checking if token is expired, you should use refresh token to obtain fresh access token. For some reason if your refresh token becomes invalid then only you need to re-authenticate, that usually happens when you change the client credentials.
In your code I see you are doing authentication But can't see refresh token call, that should be your issue here.

How to pass storeUrl to default Magento oauth script?

I'm trying to create my first Magento 2 extension and integration and I've been following the guide in their docs here. All good so far, I've completed the auth handshake, stored all the required keys for api requests and can make requests back to my extension fine.
Looking at the OauthClient.php script provided at the foot of the tutorial, the url is hardcoded like so:
return new Uri('http://magento.host/oauth/token/request'); and the tutorial advises you to "Change the instances of http://magento.host in this example to a valid base URL."
<?php
use OAuth\Common\Consumer\Credentials;
use OAuth\Common\Http\Client\ClientInterface;
use OAuth\Common\Http\Exception\TokenResponseException;
use OAuth\Common\Http\Uri\Uri;
use OAuth\Common\Http\Uri\UriInterface;
use OAuth\Common\Storage\TokenStorageInterface;
use OAuth\OAuth1\Service\AbstractService;
use OAuth\OAuth1\Signature\SignatureInterface;
use OAuth\OAuth1\Token\StdOAuth1Token;
use OAuth\OAuth1\Token\TokenInterface;
class OauthClient extends AbstractService
{
/** #var string|null */
protected $_oauthVerifier = null;
public function __construct(
Credentials $credentials,
ClientInterface $httpClient = null,
TokenStorageInterface $storage = null,
SignatureInterface $signature = null,
UriInterface $baseApiUri = null
) {
if (!isset($httpClient)) {
$httpClient = new \OAuth\Common\Http\Client\StreamClient();
}
if (!isset($storage)) {
$storage = new \OAuth\Common\Storage\Session();
}
if (!isset($signature)) {
$signature = new \OAuth\OAuth1\Signature\Signature($credentials);
}
parent::__construct($credentials, $httpClient, $storage, $signature, $baseApiUri);
}
/**
* #return UriInterface
*/
public function getRequestTokenEndpoint()
{
return new Uri('http://magento.host/oauth/token/request');
}
/**
* Returns the authorization API endpoint.
*
* #throws \OAuth\Common\Exception\Exception
*/
public function getAuthorizationEndpoint()
{
throw new \OAuth\Common\Exception\Exception(
'Magento REST API is 2-legged. Current operation is not available.'
);
}
/**
* Returns the access token API endpoint.
*
* #return UriInterface
*/
public function getAccessTokenEndpoint()
{
return new Uri('http://magento.host/oauth/token/access');
}
/**
* Parses the access token response and returns a TokenInterface.
*
* #param string $responseBody
* #return TokenInterface
*/
protected function parseAccessTokenResponse($responseBody)
{
return $this->_parseToken($responseBody);
}
/**
* Parses the request token response and returns a TokenInterface.
*
* #param string $responseBody
* #return TokenInterface
* #throws TokenResponseException
*/
protected function parseRequestTokenResponse($responseBody)
{
$data = $this->_parseResponseBody($responseBody);
if (isset($data['oauth_verifier'])) {
$this->_oauthVerifier = $data['oauth_verifier'];
}
return $this->_parseToken($responseBody);
}
/**
* Parse response body and create oAuth token object based on parameters provided.
*
* #param string $responseBody
* #return StdOAuth1Token
* #throws TokenResponseException
*/
protected function _parseToken($responseBody)
{
$data = $this->_parseResponseBody($responseBody);
$token = new StdOAuth1Token();
$token->setRequestToken($data['oauth_token']);
$token->setRequestTokenSecret($data['oauth_token_secret']);
$token->setAccessToken($data['oauth_token']);
$token->setAccessTokenSecret($data['oauth_token_secret']);
$token->setEndOfLife(StdOAuth1Token::EOL_NEVER_EXPIRES);
unset($data['oauth_token'], $data['oauth_token_secret']);
$token->setExtraParams($data);
return $token;
}
/**
* Parse response body and return data in array.
*
* #param string $responseBody
* #return array
* #throws \OAuth\Common\Http\Exception\TokenResponseException
*/
protected function _parseResponseBody($responseBody)
{
if (!is_string($responseBody)) {
throw new TokenResponseException("Response body is expected to be a string.");
}
parse_str($responseBody, $data);
if (null === $data || !is_array($data)) {
throw new TokenResponseException('Unable to parse response.');
} elseif (isset($data['error'])) {
throw new TokenResponseException("Error occurred: '{$data['error']}'");
}
return $data;
}
/**
* #override to fix since parent implementation from lib not sending the oauth_verifier when requesting access token
* Builds the authorization header for an authenticated API request
*
* #param string $method
* #param UriInterface $uri the uri the request is headed
* #param \OAuth\OAuth1\Token\TokenInterface $token
* #param $bodyParams array
* #return string
*/
protected function buildAuthorizationHeaderForAPIRequest(
$method,
UriInterface $uri,
TokenInterface $token,
$bodyParams = null
) {
$this->signature->setTokenSecret($token->getAccessTokenSecret());
$parameters = $this->getBasicAuthorizationHeaderInfo();
if (isset($parameters['oauth_callback'])) {
unset($parameters['oauth_callback']);
}
$parameters = array_merge($parameters, ['oauth_token' => $token->getAccessToken()]);
$parameters = array_merge($parameters, $bodyParams);
$parameters['oauth_signature'] = $this->signature->getSignature($uri, $parameters, $method);
$authorizationHeader = 'OAuth ';
$delimiter = '';
foreach ($parameters as $key => $value) {
$authorizationHeader .= $delimiter . rawurlencode($key) . '="' . rawurlencode($value) . '"';
$delimiter = ', ';
}
return $authorizationHeader;
}
}
My Question is how do I pass the url from the store that the integration has been set up on back as a variable in here?(I have it stored in my db)
Thanks for taking the time to take a look.
For anyone who might come across this in future after getting the saved data from the table in my db, I added the Url into the call to make the new class on the checklogin.php script in the documentation:
$oAuthClient = new OauthClient($credentials,$magentoBaseUrl);
And then in OauthClient.php, I added the url to the construct and updated the getRequestTokenEndpoint method and the getAcessTokenEndpoint:
public function __construct(
Credentials $credentials,
$magentoBaseUrl = null,
ClientInterface $httpClient = null,
TokenStorageInterface $storage = null,
SignatureInterface $signature = null,
UriInterface $baseApiUri = null
) {
if (!isset($httpClient)) {
$httpClient = new \OAuth\Common\Http\Client\CurlClient();
}
if (!isset($storage)) {
$storage = new \OAuth\Common\Storage\Session();
}
if (!isset($signature)) {
$signature = new \OAuth\OAuth1\Signature\Signature($credentials);
}
if (!isset($magentoBaseUrl)) {
die();
}
$this->magentoBaseUrl = $magentoBaseUrl;
parent::__construct($credentials, $httpClient, $storage, $signature, $baseApiUri);
}
...
/**
* #return UriInterface
*/
public function getRequestTokenEndpoint()
{
return new Uri($this->magentoBaseUrl.'/oauth/token/request');
}
...
/**
* Returns the access token API endpoint.
*
* #return UriInterface
*/
public function getAccessTokenEndpoint()
{
return new Uri($this->magentoBaseUrl.'/oauth/token/access');
}

Upload Twitter video error (PHP). API response: Segments do not add up to provided total file size

I'm trying to upload video to Twitter using API and tmhOAuth for requests however I'm getting an error from Twitter: "Segments do not add up to provided total file size.".
I checked chunks sizes with size provided in INIT command they are equal. Could you please help me find solution?
Here is a solution which I used as a basis
Here is my code:
/**
* #param string $pathToVideo
*
* #return string
*
* #throws UploadException
* #throws \RuntimeException
*/
public function uploadVideo($pathToVideo)
{
//execute upload init and get media ID form init command
$mediaId = $this->init($pathToVideo);
//uploading video
$this->append($pathToVideo, $mediaId);
//finalize uploading
$code = $this->finalize($mediaId);
}
/**
* #param string $mediaId
*
* #return int
*/
private function finalize($mediaId)
{
return $this->sendPostRequest(['command' => 'FINALIZE', 'media_id' => $mediaId]);
}
/**
* #param string $pathToVideo
* #param string $mediaId
*
* #throws UploadException
*/
private function append($pathToVideo, $mediaId)
{
//read video file
$fp = fopen($pathToVideo, 'r');
//segment counter
$segmentId = 0;
$uploadedBytes = 0;
while (! feof($fp)) {
$chunk = fread($fp, self::CHUNK_LIMIT);
$uploadedBytes += strlen($chunk);
$code = $this->sendPostRequest([
'command' => 'APPEND',
'media_id' => $mediaId,
'media' => $chunk,
'segment_index' => $segmentId,
], true);
if (!in_array($code, [Response::HTTP_CONTINUE, Response::HTTP_NO_CONTENT], true)) {
throw new UploadException(sprintf(
"Uploading Twitter video failed during APPEND. Returned code %d.\nPath to video %s.\nResponse: %s",
$code,
$pathToVideo,
$this->tmhOAuth->response['response']
));
}
$segmentId++;
}
fclose($fp);
}
/**
* #param string $pathToVideo
*
* #return string
*
* #throws UploadException
*/
private function init($pathToVideo)
{
$fileSize = filesize($pathToVideo);
$code = $this->sendPostRequest([
'command' => 'INIT',
'total_bytes' => $fileSize,
'media_type' => 'video/mp4',
]);
$response = $this->tmhOAuth->response['response'];
if ($this->isSuccessCode($code)) {
//return media ID form init command
return json_decode($response)->media_id_string;
}
throw new UploadException(sprintf(
"Uploading Twitter video failed during INIT. Returned code %d.
Path to video %s, file size %d.\nEndpoint: " . $this->uploadUrl . "\nResponse %s",
$code,
$pathToVideo,
$fileSize,
$response
));
}
/**
* #param array $options
*
* #return int
*/
private function sendPostRequest($options, $isMultipart = false)
{
return $this->tmhOAuth->request(
'POST',
$this->uploadUrl,
$options,
true,
$isMultipart
);
}
I found solution. When I set CHUNK_LIMIT to 25000 it start working. I tried to use 50000 but it was fail at the end.
Twitter stated here, the limit for a chunk is 5MB. Somehow, when we limit the chunk to 5MB, the upload will fail. From my calculation, use 2MB of size for CHUNK_LIMIT is enough to upload video up to 512MB.

create rest-full login API in that i want to generate jwt token valid for certain time only

Libraries code I get from https://github.com/b3457m0d3/JWT-CodeIgniter
and I have to create login API in that i want to generate token valid for certain time only.
**comtroller
<?php
require APPPATH.'libraries/REST_Controller.php';
class Comx extends REST_Controller
{
public function __construct()
{
parent::__construct();
$this->load->library('session');
$this->load->helper('cookie');
$this->load->library('jwt');
}
public function generate_token($user_id)
{
//$this->load->library("jwt");
$CONSUMER_KEY = '124578';
$CONSUMER_SECRET = '456123';
$CONSUMER_TTL = 60;
var_dump( $this->jwt->encode(array(
'consumerKey'=>$CONSUMER_KEY,
'userId'=>$user_id,
'issuedAt'=>date(DATE_ISO8601, strtotime("now")),
'ttl'=>$CONSUMER_TTL
), $CONSUMER_SECRET));
}
**
// application/ libraries/jwt.php but this code give me error on line 89 of controller Undefined property: Comx::$JWT ?is it can not load library
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/**
* JSON Web Token implementation
*
* Minimum implementation used by Realtime auth, based on this spec:
* http://self-issued.info/docs/draft-jones-json-web-token-01.html.
*
* #author Neuman Vong <neuman#twilio.com>
* #minor changes for codeigniter <b3457m0d3#interr0bang.net>
*/
class JWT
{
/**
* #param string $jwt The JWT
* #param string|null $key The secret key
* #param bool $verify Don't skip verification process
*
* #return object The JWT's payload as a PHP object
*/
public function decode($jwt, $key = null, $verify = true)
{
$tks = explode('.', $jwt);
if (count($tks) != 3) {
throw new UnexpectedValueException('Wrong number of segments');
}
list($headb64, $payloadb64, $cryptob64) = $tks;
if (null === ($header = $this->jsonDecode($this->urlsafeB64Decode($headb64)))
) {
throw new UnexpectedValueException('Invalid segment encoding');
}
if (null === $payload = $this->jsonDecode($this->urlsafeB64Decode($payloadb64))
) {
throw new UnexpectedValueException('Invalid segment encoding');
}
$sig = $this->urlsafeB64Decode($cryptob64);
if ($verify) {
if (empty($header->alg)) {
throw new DomainException('Empty algorithm');
}
if ($sig != $this->sign("$headb64.$payloadb64", $key, $header->alg)) {
throw new UnexpectedValueException('Signature verification failed');
}
}
return $payload;
}
/**
* #param object|array $payload PHP object or array
* #param string $key The secret key
* #param string $algo The signing algorithm
*
* #return string A JWT
*/
public function encode($payload, $key, $algo = 'HS256')
{
$header = array('typ' => 'jwt', 'alg' => $algo);
$segments = array();
$segments[] = $this->urlsafeB64Encode($this->jsonEncode($header));
$segments[] = $this->urlsafeB64Encode($this->jsonEncode($payload));
$signing_input = implode('.', $segments);
$signature = $this->sign($signing_input, $key, $algo);
$segments[] = $this->urlsafeB64Encode($signature);
return implode('.', $segments);
}
/**
* #param string $msg The message to sign
* #param string $key The secret key
* #param string $method The signing algorithm
*
* #return string An encrypted message
*/
public function sign($msg, $key, $method = 'HS256')
{
$methods = array(
'HS256' => 'sha256',
'HS384' => 'sha384',
'HS512' => 'sha512',
);
if (empty($methods[$method])) {
throw new DomainException('Algorithm not supported');
}
return hash_hmac($methods[$method], $msg, $key, true);
}
/**
* #param string $input JSON string
*
* #return object Object representation of JSON string
*/
public function jsonDecode($input)
{
$obj = json_decode($input);
if (function_exists('json_last_error') && $errno = json_last_error()) {
$this->handleJsonError($errno);
}
else if ($obj === null && $input !== 'null') {
throw new DomainException('Null result with non-null input');
}
return $obj;
}
/**
* #param object|array $input A PHP object or array
*
* #return string JSON representation of the PHP object or array
*/
public function jsonEncode($input)
{
$json = json_encode($input);
if (function_exists('json_last_error') && $errno = json_last_error()) {
$this->handleJsonError($errno);
}
else if ($json === 'null' && $input !== null) {
throw new DomainException('Null result with non-null input');
}
return $json;
}
/**
* #param string $input A base64 encoded string
*
* #return string A decoded string
*/
public function urlsafeB64Decode($input)
{
$remainder = strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
/**
* #param string $input Anything really
*
* #return string The base64 encode of what you passed in
*/
public function urlsafeB64Encode($input)
{
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}
/**
* #param int $errno An error number from json_last_error()
*
* #return void
*/
private function handleJsonError($errno)
{
$messages = array(
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON'
);
throw new DomainException(isset($messages[$errno])
? $messages[$errno]
: 'Unknown JSON error: ' . $errno
);
}
}

Ignore Slash while using encryption in codeigniter

I have a problem to encrypt/decrypt the email, i just send a link on mail like this
http://www.domain.com/mycontroller/myfunction/McvBsce........etc
The last Segment is actually a encrypted email id, I decrypt this email and update stus in my db when user click on this link.All done right.
Problem: When url like this
http://www.domain.com/mycontroller/myfunction/McvB/sce
It shows 404 error, because slash included in the generated encryption key.How can i ignore the slash while it generate the encryption, that's my main problem, rest are working fine.
You need to use this class, include this file in your applicatio/libraries folder, I had the same issue:
class MY_Encrypt extends CI_Encrypt
{
/**
* Encodes a string.
*
* #param string $string The string to encrypt.
* #param string $key[optional] The key to encrypt with.
* #param bool $url_safe[optional] Specifies whether or not the
* returned string should be url-safe.
* #return string
*/
function encode($string, $key="", $url_safe=TRUE)
{
$ret = parent::encode($string, $key);
if ($url_safe)
{
$ret = strtr(
$ret,
array(
'+' => '.',
'=' => '-',
'/' => '~'
)
);
}
return $ret;
}
/**
* Decodes the given string.
*
* #access public
* #param string $string The encrypted string to decrypt.
* #param string $key[optional] The key to use for decryption.
* #return string
*/
function decode($string, $key="")
{
$string = strtr(
$string,
array(
'.' => '+',
'-' => '=',
'~' => '/'
)
);
return parent::decode($string, $key);
}
}
Credit goes to the codeigniter forum, from where I got this.

Categories