Public Key Client Validation Quickstart
This guide will walk you through the steps of implementing Public Key Client Validation. We include sample cURL commands and HTTP requests, then at the end we'll detail the steps in Java.
Public key client validation quickstart
To get started quickly, you can follow the Java example at the bottom of the page. It shows how Client Validation can be implemented, along with links to the Twilio Java helper library that supports this feature.
リクエストの送信手順
- Generate an RSA Key Pair: Create a valid key pair. (This only has to be done once.)
- Submit the Public Key: Submit the public key to the Twilio via the Credentials Endpoint. (This is a one time requirement as well.)
- Hash the Canonical Request: Every outgoing request needs to be hashed and signed. (This functionality is implemented in the Java helper library and can be seen below.)
- Generate JWT: Once the hash is created, it needs to be embedded in the JWT payload and signed with the private key. (This is also handled by the Java helper library.)
- Attach JWT to the request header: The last step is to add the JWT to the request header.
1. Generate an RSA Keypair
A private key is used to sign your requests. It is is verified by the public key which you provide to Twilio.
Note: When you generate the private key, be sure to save and protect it as this is the only means to verify your application's identity.
RSAキーの生成にはOpenSSLツールキットを使用を強く推奨します。
Windowsシステムをお使いの方へ
Install and use Cygwin to run the OpenSSL RSA keypair commands below.
MacおよびLinux/Unixベースシステムをお使いの方へ
OpenSSLコマンドを使用してRSAキーペアを生成できます。
Generate a Private Key
openssl genrsa -aes256 -out private_key.pem 2048
メモ: Twilioは65537の指数 (e) を持つ2048ビット長の鍵のみを受け入れます。
公開鍵の生成
openssl rsa -pubout -in private_key.pem -out public_key.pem
公開鍵のフォーマット例: 生成に成功すると、RSA公開鍵は下記の例のようになります:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlRgaHOdjxFVceFucQXkA
0tTT6tY6YDlkWgThv4FLjtbBqzfRcRUkaTqpSJaGgBsTgXeBdLK0DgneTRmPwZzw
...
sD93r4H6ti519kM+u87I6On00S3k4r6pGsWnBCf+1RmJps6xfsDflPIAstyZEpa9
xQIDAQAB
-----END PUBLIC KEY-----
Be sure to include the full header and footer when submitting the key: * '-----BEGIN PUBLIC KEY-----' AND * '-----END PUBLIC KEY-----'
2. Submit the Public Key to Twilio
サンプルのcURLリクエスト
curl -X POST -H "Authorization: Basic <token>"
-F "PublicKey=-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BA....9xQIDAQAB-----END PUBLIC KEY-----"
-F "FriendlyName=Client Validation" "https://accounts.twilio.com/v1/Credentials/PublicKeys/"
Note: Line breaks in the PEM format of the key need to be removed when making the cURL request.
レスポンス例
{
"date_updated": "2016-10-25T19:54:49Z",
"friendly_name": "Client Validation",
"account_sid": "AC171b8eb......e737e0ee2cb99ee",
"url": "https://accounts.twilio.com/v1/Credentials/PublicKeys/CR934061....ed833471f596a5b4",
"sid": "CR934061....ed833471f596a5b4",
"date_created": "2016-10-25T19:54:49Z"
}
3. Hash the Canonical Request
下記セクションではリクエストがどのように正規化され、ハッシュされ、そしてリクエストに追加されるかについて説明しています。
_Note: The Java helper library implements this functionality and will do the work for you. An end-to-end example is on the bottom of this page. _
This approach is loosely based on the approach Amazon is using to sign AWS API requests.
正規化されたリクエストの擬似コード
Canonical HTTP Method + '\n' +
Canonical URI + '\n' +
Canonical Query String + '\n' +
Canonical Headers + '\n' +
Signed Headers + '\n' +
HexEncode(Hash(Request Body))
ハッシュの例
HTTPリクエストの例
POST /2010-04-01/Accounts/AC00000000000000000000000000000000
HTTP/1.1
Host: api.twilio.com
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 33
Authorization: Basic QUMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDpmb29iYXI=
FriendlyName=my new friendly name
HTTPメソッドを正規化する
HTTPメソッドは下記の操作を行うことで正規化されます:
- 大文字にする
- Trim
リクエスト例では、次のような結果になります:
POST
リソースのパスを正規化する
リソースのパスを正規化するには:
- 余分なパス要素を取り除きます。 例:
- '/foobar/./barfoo' becomes '/foobar/barfoo' AND
- 「/foobar/../barfoo」は「/barfo」となります
- URL-encode the remaining path using the UTF-8 character set in accordance with RFC 3986 with the following caveats:
- 「 」空白文字は常に「%20」にしてください
- 「*」空白文字は常に「%2A」にしてください
- 「%7E」は常に「~(チルダ)」にしてください
- 空の文字列パスは常に「/」となります
リクエスト例では、次のような結果になります:
/2010-04-01/Accounts/AC00000000000000000000000000000000
クエリー文字列を正規化する
query-stringは下記の操作で正規化してください:
- URIからquery-stringを削除します(「?」は含みません)
- クエリー文字列を「&」記号で分割し、(「key」だけではなく)「key=value」の文字列をASCII順に並べ替えすることで一連のキーと値の組として構成します
- URL encode each key and value following the Resource Path (RFC 3986) with our caveats from above
- 各キーと値の組をこのように結合します: {key}={value}
- もしキーと組になる値が存在しない場合は、結果は&を間に挟むキーと値の組を結合する「{key}=」となります。
リクエスト例では、結果は空の文字列になります。
/2010-04-01/Accounts/AC00000000000000000000000000000000
リクエストに下記のようなクエリー文字列が含まれる場合、
?from=4151234567&to=4157654321&message=Thanks for your order
正規化されたクエリー文字列は下記のようになります:
from=4151234567&message=Thanks%20for%20your%20order%20&to=4157654321
ヘッダーを正規化する
ヘッダーは下記操作で正規化されます:
- ヘッダーの完全な一覧を外部のJWT内の「hrh」(hashed request headers)値でフィルタリングします
- 各ヘッダーのキーを小文字にします
- 各ヘッダー値をトリミングし、連続する空白文字を単一の空白に置き換えます
- 同じキーに対応するヘッダーの値を並べ替えます
- 「{key}:{values}\n」のようにキーと値を結合します
- ASCII順に並べ替えます
- Note that because each header line is terminated with a ‘\n’. When the entire canonical request is combined, there should be a blank-line between the canonical-headers and the canonical-hashed-headers
リクエスト例では、次のような結果になります:
authorization:Basic QUMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDpmb29iYXI=
host:api.twilio.com
ハッシュ済みヘッダーを正規化する
ハッシュ済みヘッダーは下記の操作で正規化されます:
- 「;」(セミコロン)で分割します
- 小文字に変換し、トリミングします
- ASCII順に並べ替えます
- 「;」(セミコロン)で結合します
リクエスト例では、ハッシュ済みヘッダーの一覧には「Host」および「Authorization」を含めたいと仮定しており、その結果は下記のようになります:
authorization;host
リクエスト本文をエンコードする
リクエスト本文が空の場合、ハッシュを省略します。
リクエスト本文をエンコードするには:
- SHA-256を使用してリクエストをハッシュします
- ハッシュ結果を16進エンコードします
リクエスト例では、次のような結果になります:
b8e20591615abc52293f088c87be6df8e9b7b40c3da573f134c9132add851e2d
最終的な正規化されたリクエスト
下記の例では、最初の空行はクエリー文字列を持っていないために発生します。 次の空行はすべての正規化されたヘッダーが「\n」で終わるために発生します。
POST
/2010-04-01/Accounts/AC00000000000000000000000000000000
authorization:Basic QUMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDpmb29iYXI=
host:api.twilio.com
authorization;host
b8e20591615abc52293f088c87be6df8e9b7b40c3da573f134c9132add851e2d
正規化リクエストハッシュ
When the final canonical request string is created it must be hashed in a similar manner to the request body.
正規化されたリクエストをエンコードするには:
- SHA-256を使用してリクエストをハッシュします
- ハッシュ結果を16進エンコードします
リクエスト例では、次のような結果になります:
245eece1e638d9b0081ca0621183cd417fc97a1818bd822aa26697f9aa70c792
4. JWTを生成する
ハッシュを作成したら、埋め込まれたハッシュでJWTを生成できます。
Every JWT assertion is composed of three components, the header, the payload, and the signature.
- ヘッダーにはJWTの署名に使用されるアルゴリズムが指定されます。
- ペイロードにはハッシュと追加のメタデータが格納されます。
- 署名はJWTの送信者がそう主張する本人であるか確認し、また送信途中に改竄されていないことを保証するための検証に使用されます。
JWTアサーションを組み立てるには、これらの3つの構成要素がbase64エンコーディングされ、「.」セパレーターを使用して結合することが必要です。
<base64URLencoded header>.<base64URLencoded claims>.<base64URLencoded signature>
Note: For additional details on JWT go to: https://jwt.io/introduction/
Let’s have a closer look at the different parts of the JWT Assertion:
ヘッダー
ヘッダーは4つの部分から成り立っています: コンテントタイプ、トークンの種類、使用されたハッシュ化のアルゴリズム、そしてTwilioがメッセージの検証に使用すべき公開鍵への参照です。
フィールド | Value(s) | 必須 | 概要 |
---|---|---|---|
cty | twilio-pkrv;v=1 | はい | ContentType = Twilio Public Key Request Validation - Version 1 |
typ | JWT | No (Default: ‘JWT’) | Media Type = JSON Web Token, other values rejected |
alg | RS256 | はい | SHA-256ハッシュアルゴリズムを使用するRSASSA-PKCS-v1_5です。 これが現時点で唯一サポートされるアルゴリズムです。 |
kid | Credential SID | はい | Key ID = JWTの署名に使用する秘密鍵に対応する公開鍵の資格情報の識別子です |
ヘッダー例:
{
"cty": "twilio-pkrv;v=1",
"typ": "JWT",
"alg": "RS256",
"kid": "CR00000000000000000000000000000000"
}
ペイロード
トークンの2番目の部分は、申告内容を含むペイロードです。 申請内容はエンティティーおよび追加のメタデータについての宣言です。
フィールド | Value(s) | 必須 | 概要 |
---|---|---|---|
iss | APIKeySid | はい | Issuer = リクエストの資格情報に対応づけられるAPIKey Sidです |
sub | AccountSid | はい | Subject = AccountSid |
exp | 有効期限 | はい | トークンの有効期限: 有効期限が切れた後に受信するトークン +- クロックスキューは拒否されます。Max exp - nbfは300秒です |
nbf | not before time | いいえ | (既定値: ‘now’) Not Before Time |
hrh | ハッシュを行うヘッダーの一覧 | はい | リクエストハッシュの計算に含める、「;」(セミコロン)で区切られた小文字によるヘッダーの一覧です。 少なくとも、「Host」および「Authorization」を含めることが必要です |
rqh | リクエストハッシュ | はい | 上記の「3. 正規化されたリクエストのハッシュを作成する」を参照してください。 |
ペイロードの例:
{
"iss": "SK00000000000000000000000000000000",
"sub": "AC00000000000000000000000000000000",
"exp": 1471827354,
"hrh": "authorization;host",
"rqh": "245eece1e638d9b0081ca0621183cd417fc97a1818bd822aa26697f9aa70c792"
}
署名
署名部分を作成するには、エンコードされたヘッダー、エンコードされたペイロード、シークレット、ヘッダーに記載されたアルゴリズム、およびその署名が必要になります。
署名の作成例
RS256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
公開鍵
署名を検証するには、Twilioに公開鍵が必要です。 この公開鍵をTwilioにアップロードすることが必要です。 公開鍵は、次のようなものである必要があります:
- アルゴリズム: RSA
- 係数::ビット長: 2048
- フォーマット: X.509
Public key to successfully validate the Example JWT (below):
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAum6dAjx7jM1GTYOcIo1x
b+KvO/FsKUMd4xLiDeKNd5DZ1sVKoJSH1oMGRtaVnN4Uzo1h5rUDGrB73hY9PRAK
uGEGZotiVR7Zmbq7l+NuR+pR3KhYJagzLQ+K91GkBsJM0f4geK1qwXfHYmA11O19
8eNAMS3sRwNnVlyPwtvIamwN8iDxEr+GvT7OIGZxHOCYRXmDAueDDLZqSF5j/qdw
vwGSHlXh/sr91o7fy/thWxwzM9Dp+h95OiML3cH/edt68NNLD5zxnHEZxx1K/w/Y
/g6KGo7b0ehR241pV0cmqFm0ebF0m+950F7iCI+qha97kHpBtBSAzyyHOhy2d4v7
IQIDAQAB
-----END PUBLIC KEY-----
プライベートキー
リクエストは秘密鍵で署名されていることが必要です。 秘密鍵はTwilioにアップロードされた公開鍵と対になっている必要があります。
There are no limitations on the private key (as opposed to the public key, enumerated above) other than it needs to match the public key. It can be either PKCS#1 or PKCS#8 (whichever the signing library supports).
Private key used to sign the Example JWT:
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAum6dAjx7jM1GTYOcIo1xb+KvO/FsKUMd4xLiDeKNd5DZ1sVK
oJSH1oMGRtaVnN4Uzo1h5rUDGrB73hY9PRAKuGEGZotiVR7Zmbq7l+NuR+pR3KhY
JagzLQ+K91GkBsJM0f4geK1qwXfHYmA11O198eNAMS3sRwNnVlyPwtvIamwN8iDx
Er+GvT7OIGZxHOCYRXmDAueDDLZqSF5j/qdwvwGSHlXh/sr91o7fy/thWxwzM9Dp
+h95OiML3cH/edt68NNLD5zxnHEZxx1K/w/Y/g6KGo7b0ehR241pV0cmqFm0ebF0
m+950F7iCI+qha97kHpBtBSAzyyHOhy2d4v7IQIDAQABAoIBAQCIFvbGCyClR7Nq
Igh3sIh+BBumxjUOadAHUmFxgU+DWFmsTZiMX+BI1pxeWYYdXIATx2EP6FK7yNii
5dkOGge5UBo8AMNnH334mjcWSQ7XsFTRnpG5625wFkh7AT2bMXqiT7+kV/L2B1mk
lla1eCfXyuuw+rTfobxtbmQC+izygW6pri4KbmIBxhlTMPcgns3dTADL0eoH0po6
u2mKHBaLP9GZpxR+pbZE0y4e6qDJt4M3nwUpm1zDkJGVuyAQebTbMxtsP4VHQ/0t
wKKi73bnD62CanRf+bqt+FJWEIPI6yOBVbxcvLVLStRRkOVwugZlP0seDOlLrWVo
YnwIReABAoGBAOfUlV9xgTwYdokaZKVOh8uJewAc4qqE1e3dh4epLm9jLiUul2bQ
dFxL/dtAur76Th9kpRbbNQizGKKKjDOD4r0qF+aNbsRpNhx9OqaTaK40CH3g6zlc
i8HmW5kjoRTJTBoFtp+8U6OdeiksUZ1Xbm5yR8395Wm7Y4p4LmCOGIcRAoGBAM3e
YB5tNM6U0FgRFRc6R8UMrgo4SLXNlqvzMyKC/eHPziJP2PKAvBAasqZwEISlK4D6
T6Fqbb6eFh0XNYJEQq/3JkuC5HNfBIMZ81X5gxGq/pMQPiPbr6QfY3hzgUljKyky
xkYiQdcu9E6KiMJXWpz2GNmctlQT1b0cpQW3GNMRAoGARGN00RwFuLmqthVAHXfG
HWfoDgd3YkAfb7ULFxz0Ys2KPlO5PA5AVT3hnD1DGbVzOFWTUePGiFN07/YZF9VP
HOh+9ndAdtZmrQ7QL3WKyuD0pFWmblx7qe6PlORqz1v2hDKtRf/jWH/LGrxFMzoo
jJJP1leQxpkN6zo6zCb+21ECgYAsoYk1D3fjUV/Zt9parsfgcF9K1+jrgSapIJB1
avCfg+2sgqMF7+LVmvQgIStzlltYGuwokmo4aQ1iQSXYl/PdMjebJ0Vfvbm8smOO
wAkqS2fleh/+piHt8uAdvOzKfDVfOSLDEao0fHl6jY4Yk9eRL8kzZEYi9CniVdNw
6cD4AQKBgQDPluvF2FmQiEPR0to4rcpfa3IznO2uC8V7fjSUBAZQ38zQhFbsL+DR
7SbJbloHv1K5HzcAwkNuKQJeJ7WKGjGgtm4ScuLJbkTWQF2BJTcZA6cuqQ0RVRq4
LGJ+GQyvLu2JUtZj+gL9Aab0mbB/pL/zw3vzg9bdYgVtN0rA2nF7jQ==
-----END RSA PRIVATE KEY-----
JWTの例
下記のJWTは上記のブロック例から構成されています。 JWTは上記の秘密鍵で署名されています。 このJWTは上記の公開鍵で検証できます。
eyJjdHkiOiJ0d2lsaW8tcGtydjt2PTEiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkNSMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifQ.eyJpc3MiOiJTSzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwic3ViIjoiQUMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCIsImV4cCI6MTQ3MTgyNzM1NCwiaHJoIjoiYXV0aG9yaXphdGlvbjtob3N0IiwicnFoIjoiMjQ1ZWVjZTFlNjM4ZDliMDA4MWNhMDYyMTE4M2NkNDE3ZmM5N2ExODE4YmQ4MjJhYTI2Njk3ZjlhYTcwYzc5MiJ9.a8Z-NXPEf8FrfEpxYBF8kIdn_1VAoa4H6t_X_CmtT7YksKkLMsQl6X00Hx0zEItgu64Z-qeaANxmwme6Y7nRRVz2AV8ZPTv5sWPhXOHVevyEDf2QfPpteDd0gpoPA4KjaklJtnNR8iSAd68DBaUvVE6bnAsop6dM4vowYNOMCe4PUe_W8AXu6iIzHmQxm5AVatyPoRY4dR-Il1tswbUr5FlVGzJJsw7JLNd46FYp2gIfhDM52cgBMeH5qNQw9inUm-BUybT1rB-kB1UCNq_3WenGoTGZsJ32QSBXAS9pbjOYNHIrylR51GV2foxqcOpsgIBFt_udnWlsqkezRun7TQ
5. リクエストヘッダーにJWTを追加する
JWTは、Twilio-Client-Validationヘッダー経由でリクエストに追加することが必要です。
クライアント検証のJavaの例
この機能は現在、最新のJavaヘルパーライブラリーでのみサポートされています。
下記の例では正しくクライアント検証リクエストを行う全5ステップをすべて網羅しています。 このサンプルは、GitHubでも利用可能です。
package com.twilio.example;
import com.twilio.http.TwilioRestClient;
import com.twilio.http.ValidationClient;
import com.twilio.rest.accounts.v1.credential.PublicKey;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.rest.api.v2010.account.NewKey;
import com.twilio.twiml.TwiMLException;
import com.twilio.type.PhoneNumber;
import org.apache.commons.codec.binary.Base64;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
public class ValidationExample {
public static final String ACCOUNT_SID = System.getenv("TWILIO_ACCOUNT_SID");
public static final String AUTH_TOKEN = System.getenv("TWILIO_AUTH_TOKEN");
/**
* Example Twilio usage.
*
* @param args command line args
* @throws TwiMLException if unable to generate TwiML
*/
public static void main(String[] args) throws Exception {
// Generate public/private key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair pair = keyGen.generateKeyPair();
java.security.PublicKey pk = pair.getPublic();
// Use the default rest client
TwilioRestClient client =
new TwilioRestClient.Builder(ACCOUNT_SID, AUTH_TOKEN)
.build();
// Create a public key and api key using the default client
PublicKey key = PublicKey.creator(
Base64.encodeBase64String(pk.getEncoded())
).setFriendlyName("Public Key").create(client);
NewKey apiKey = NewKey.creator().create(client);
// Switch to validation client as the default client
TwilioRestClient validationClient = new TwilioRestClient.Builder(apiKey.getSid(), apiKey.getSecret())
.accountSid(ACCOUNT_SID)
.httpClient(new ValidationClient(ACCOUNT_SID, key.getSid(), apiKey.getSid(), pair.getPrivate()))
.build();
// Make REST API requests with Client Validation enabled
Iterable<Message> messages = Message.reader().read(validationClient);
for (Message m : messages) {
System.out.println(m.getBody());
}
Message m = Message.creator(
new PhoneNumber("+1XXXXXXXXXX"),
new PhoneNumber("+1XXXXXXXXXX"),
"Client Validation Test"
).create(validationClient);
System.out.println(m.getSid());
}
}
Notes:
Standard API Keys are not permitted to manage Accounts (e.g. create sub accounts) and other API Keys. If you require this functionality please refer to this page for additional details.
It may take a few minutes after Enforcing Public Key Client Validation from Settings for it to take effect.
ヘルプが必要ですか?
We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd by visiting Twilio's Stack Overflow Collective or browsing the Twilio tag on Stack Overflow.