メニュー

Rate this page:

Thanks for rating this page!

We are always striving to improve our documentation quality, and your feedback is valuable to us. How could this documentation serve you better?

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.

リクエストの送信手順

  1. Generate an RSA Key Pair: Create a valid key pair. (This only had to be done once.)
  2. Submit the Public Key: Submit the public key to the Twilio via the Credentials Endpoint. (This is a one time requirement as well.)
  3. Hash the Canonical Request: Every outgoing request needs to be hashed and signed. (This functionality is implemented in the alpha branch of the Java helper libraries and can be seen below.)
  4. 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 latest alpha branch of the Java helper libraries.)
  5. 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

The following section describes how the request needs to be canonicalized, hashed and attached to the 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メソッドは下記の操作を行うことで正規化されます:

  1. 大文字にする
  2. Trim

リクエスト例では、次のような結果になります:

POST

リソースのパスを正規化する

リソースのパスを正規化するには:

  1. 余分なパス要素を取り除きます。 例:
    • '/foobar/./barfoo' becomes '/foobar/barfoo' AND
    • 「/foobar/../barfoo」は「/barfo」となります
  2. URL-encode the remaining path using the UTF-8 character set in accordance with RFC 3986 with the following caveats:
    • 「 」空白文字は常に「%20」にしてください
    • 「*」空白文字は常に「%2A」にしてください
    • 「%7E」は常に「~(チルダ)」にしてください
  3. 空の文字列パスは常に「/」となります

リクエスト例では、次のような結果になります:

/2010-04-01/Accounts/AC00000000000000000000000000000000

クエリー文字列を正規化する

query-stringは下記の操作で正規化してください:

  1. URIからquery-stringを削除します(「?」は含みません)
  2. クエリー文字列を「&」記号で分割し、(「key」だけではなく)「key=value」の文字列をASCII順に並べ替えすることで一連のキーと値の組として構成します
  3. URL encode each key and value following the Resource Path (RFC 3986) with our caveats from above
  4. 各キーと値の組をこのように結合します: {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

ヘッダーを正規化する

ヘッダーは下記操作で正規化されます:

  1. ヘッダーの完全な一覧を外部のJWT内の「hrh」(hashed request headers)値でフィルタリングします
  2. 各ヘッダーのキーを小文字にします
  3. 各ヘッダー値をトリミングし、連続する空白文字を単一の空白に置き換えます
  4. 同じキーに対応するヘッダーの値を並べ替えます
  5. 「{key}:{values}\n」のようにキーと値を結合します
  6. ASCII順に並べ替えます
  7. 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

ハッシュ済みヘッダーを正規化する

ハッシュ済みヘッダーは下記の操作で正規化されます:

  1. 「;」(セミコロン)で分割します
  2. 小文字に変換し、トリミングします
  3. ASCII順に並べ替えます
  4. 「;」(セミコロン)で結合します

リクエスト例では、ハッシュ済みヘッダーの一覧には「Host」および「Authorization」を含めたいと仮定しており、その結果は下記のようになります:

authorization;host

リクエスト本文をエンコードする

リクエスト本文が空の場合、ハッシュを省略します。

リクエスト本文をエンコードするには:

  1. SHA-256を使用してリクエストをハッシュします
  2. ハッシュ結果を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.

正規化されたリクエストをエンコードするには:

  1. SHA-256を使用してリクエストをハッシュします
  2. ハッシュ結果を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-----

プライベートキー

The request has to be signed with a private key. The private key must match the public key uploaded to 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());
    }
}

Note: 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.

ヘルプが必要ですか?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.