Skip to content

Commit 2c39e4a

Browse files
authored
Merge pull request #146 from Nexmo/signature-secret
Add support for hmac related request signing
2 parents a09f3fb + 51e959b commit 2c39e4a

File tree

6 files changed

+85
-12
lines changed

6 files changed

+85
-12
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,26 @@ $client->message()->search($message);
151151
echo "The body of the message was: " . $message->getBody();
152152
```
153153

154+
### Signing a Message
155+
156+
The SMS API supports the ability to sign messages by generating and adding a signature using a "Signature Secret" rather than your API secret. The algorithms supported are:
157+
158+
* `md5hash1`
159+
* `md5`
160+
* `sha1`
161+
* `sha256`
162+
* `sha512`
163+
164+
Both your application and Nexmo need to agree on which algorithm is used. In the [dashboard](https://dashboard.nexmo.com), visit your account settings page and under "API Settings" you can select the algorithm to use. This is also the location where you will find your "Signature Secret" (it's different from the API secret).
165+
166+
Create a client using these credentials and the algorithm to use, for example:
167+
168+
```php
169+
$client = new Nexmo\Client(new Nexmo\Client\Credentials\SignatureSecret(API_KEY, API_SECRET, 'sha256'));
170+
```
171+
172+
Using this client, your SMS API messages will be sent as signed messages.
173+
154174
### Starting a Verification
155175

156176
Nexmo's [Verify API][doc_verify] makes it easy to prove that a user has provided their own phone number during signup,

src/Client.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public static function signRequest(RequestInterface $request, SignatureSecret $c
157157
$content = $body->getContents();
158158
$params = json_decode($content, true);
159159
$params['api_key'] = $credentials['api_key'];
160-
$signature = new Signature($params, $credentials['signature_secret']);
160+
$signature = new Signature($params, $credentials['signature_secret'], $credentials['signature_method']);
161161
$body->rewind();
162162
$body->write(json_encode($signature->getSignedParams()));
163163
break;
@@ -168,7 +168,7 @@ public static function signRequest(RequestInterface $request, SignatureSecret $c
168168
$params = [];
169169
parse_str($content, $params);
170170
$params['api_key'] = $credentials['api_key'];
171-
$signature = new Signature($params, $credentials['signature_secret']);
171+
$signature = new Signature($params, $credentials['signature_secret'], $credentials['signature_method']);
172172
$params = $signature->getSignedParams();
173173
$body->rewind();
174174
$body->write(http_build_query($params, null, '&'));
@@ -177,7 +177,7 @@ public static function signRequest(RequestInterface $request, SignatureSecret $c
177177
$query = [];
178178
parse_str($request->getUri()->getQuery(), $query);
179179
$query['api_key'] = $credentials['api_key'];
180-
$signature = new Signature($query, $credentials['signature_secret']);
180+
$signature = new Signature($query, $credentials['signature_secret'], $credentials['signature_method']);
181181
$request = $request->withUri($request->getUri()->withQuery(http_build_query($signature->getSignedParams())));
182182
break;
183183
}

src/Client/Credentials/SignatureSecret.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ class SignatureSecret extends AbstractCredentials implements CredentialsInterfac
1616
* @param string $key API Key
1717
* @param string $signature_secret Signature Secret
1818
*/
19-
public function __construct($key, $signature_secret)
19+
public function __construct($key, $signature_secret, $method='md5hash')
2020
{
2121
$this->credentials['api_key'] = $key;
2222
$this->credentials['signature_secret'] = $signature_secret;
23+
$this->credentials['signature_method'] = $method;
2324
}
2425
}

src/Client/Signature.php

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
namespace Nexmo\Client;
1010

11+
use Nexmo\Client\Exception\Exception;
12+
1113
class Signature
1214
{
1315
/**
@@ -28,7 +30,7 @@ class Signature
2830
* @param array $params
2931
* @param $secret
3032
*/
31-
public function __construct(array $params, $secret)
33+
public function __construct(array $params, $secret, $signatureMethod)
3234
{
3335
$this->params = $params;
3436
$this->signed = $params;
@@ -51,11 +53,25 @@ public function __construct(array $params, $secret)
5153
//create base string
5254
$base = '&'.urldecode(http_build_query($signed));
5355

54-
//append the secret
55-
$base .= $secret;
56+
$this->signed['sig'] = $this->sign($signatureMethod, $base, $secret);
57+
}
5658

57-
//create hash
58-
$this->signed['sig'] = md5($base);
59+
protected function sign($signatureMethod, $data, $secret) {
60+
switch($signatureMethod) {
61+
case 'md5hash':
62+
// md5hash needs the secret appended
63+
$data .= $secret;
64+
return md5($data);
65+
break;
66+
case 'md5':
67+
case 'sha1':
68+
case 'sha256':
69+
case 'sha512':
70+
return hash_hmac($signatureMethod, $data, $secret);
71+
break;
72+
default:
73+
throw new Exception('Unknown signature algorithm: '.$signatureMethod.'. Expected: md5hash, md5, sha1, sha256, or sha512');
74+
}
5975
}
6076

6177
/**
@@ -117,4 +133,4 @@ public function __toString()
117133
{
118134
return $this->getSignature();
119135
}
120-
}
136+
}

test/Client/SignatureTest.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,46 @@
1010

1111
use Nexmo\Client\Signature;
1212
use PHPUnit\Framework\TestCase;
13+
use Nexmo\Client\Exception\Exception;
1314

1415

1516
class SignatureTest extends TestCase
1617
{
18+
public function testInvalidSignatureMethod() {
19+
$this->expectException(Exception::class);
20+
$this->expectExceptionMessage('Unknown signature algorithm: fake_algo. Expected: md5hash, md5, sha1, sha256, or sha512');
21+
$signature = new Signature(['foo' => 'bar'], 'sig_secret', 'fake_algo');
22+
}
23+
24+
/**
25+
* @dataProvider hmacSignatureProvider
26+
*/
27+
public function testHmacSignature($algorithm, $expected){
28+
$data = [
29+
'api_key' => 'fake_api_key',
30+
'to' => '14155550100',
31+
'from' => 'AcmeInc',
32+
'text' => 'Test From Nexmo',
33+
'type' => 'text',
34+
'timestamp' => '1540924779'
35+
];
36+
$secret = '71efab63122f1d179f51c46bac838fb5';
37+
$signature = new Signature($data, $secret, $algorithm);
38+
39+
$this->assertEquals($expected, $signature->getSignature());
40+
}
41+
42+
public function hmacSignatureProvider() {
43+
$data = [];
44+
45+
$data['md5'] = ['md5', '51cdafebb4bbce9525b195c1617cb8d2'];
46+
$data['sha1'] = ['sha1', '0162aec64bc183b2e1256545951fe5639dc98020'];
47+
$data['sha256'] = ['sha256', '9fec5ef6d0f2b3d2bb7558b6e4042569823cab9ea0dd30503472b7b304601975'];
48+
$data['sha512'] = ['sha512', '40bd12b9a4b6000ad1138eefd24ffe9fbd72aee13c3fa04b32bb69dbc256ad0a04a463b1a9af6660d10f6e1e769ee14b9cff6a635502e93afcd0bfab29f38f87'];
49+
50+
return $data;
51+
}
52+
1753
/**
1854
* @dataProvider signatures
1955
* @param $sig
@@ -23,7 +59,7 @@ class SignatureTest extends TestCase
2359
public function testSignature($sig, $params, $secret)
2460
{
2561
//a signature is created from a set of parameters and a secret
26-
$signature = new Signature($params, $secret);
62+
$signature = new Signature($params, $secret, 'md5hash');
2763

2864
//the parameters should ne be changed
2965
$this->assertEquals($params, $signature->getParams());

test/ClientTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ public static function assertValidSignature($array, $secret)
569569
self::assertArrayHasKey('api_key', $array);
570570

571571
//params should be correctly signed
572-
$signature = new Signature($array, $secret);
572+
$signature = new Signature($array, $secret, 'md5hash');
573573
self::assertTrue($signature->check($array));
574574
}
575575
}

0 commit comments

Comments
 (0)