1

Windows Live Connect JavaScript SDK を使用して、Web ページにユーザーをログインさせています。ID アサーションをサーバーに渡すために、この SDK は署名付き JWT トークンを として提供しますWL.getSession().authentication_token。標準の JWT のようですが、署名を確認できません。

どのシークレットを使用しますか? Microsoft アカウント デベロッパー センターからアプリケーションのクライアント シークレットを試しましたが、JWT ライブラリとオンライン JWT チェッカー (jwt.io など) の両方で署名の検証に失敗します。

このトークンのドキュメントはでたらめです。主なドキュメントはこれのようです。ただし、コード サンプルは移行で削除されたため、 github の履歴から削除する必要があります。いずれにせよ、その出所について言及せずに、「アプリケーションシークレット」を使用するだけです。

このブログ エントリには、 http://appdev.microsoft.com/StorePortalsにアクセスする必要があると書かれていますが、私のアプリは Windows ストアの一部ではありません。これは、標準のデベロッパー センター アプリケーションです ( https://account.live.com/developers/applications/index )。

トークンのデコード方法を説明しているマイクロソフトの公式ビデオを見つけました(スライド 15を参照するか、ビデオの 29:35 をご覧ください)。秘密がどこから来るのかについても曖昧です。さらに悪いことに、現在の SDK には表示されない SDK メソッドを参照しています ( LiveAuthClient.GetUserId())。

私は困惑しています。はい、access_token を取得してプロファイル エンドポイントからユーザー ID を取得できることはわかっていますが、この余分な API ラウンドトリップを回避する必要があります。JWT authentication_token は、まさにこの目的のために明確に存在します。コンテンツを確認するにはどうすればよいですか?

4

3 に答える 3

1

さらに、さらにいくつかの言語での実装を次に示します。

ジャワ:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.BaseEncoding;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class WLChecker {

    private final String clientSecret;

    public WLChecker(String clientSecret) {
        this.clientSecret = clientSecret;
    }

    /**
     * @throws GeneralSecurityException if the token is not perfect
     */
    public String check(String tokenString) throws IOException, GeneralSecurityException {
        final String[] parts = tokenString.split("\\.");
        if (parts.length != 3)
            throw new GeneralSecurityException("Not a valid token");

        validate(parts[0], parts[1], parts[2]);

        JsonNode claims = new ObjectMapper().readTree(BaseEncoding.base64Url().decode(parts[1]));

        String uid = claims.path("uid").asText();
        if (uid == null || uid.length() == 0)
            throw new GeneralSecurityException("No uid in claims");

        return uid;
    }

    private void validate(String envelope, String claims, String sig) throws GeneralSecurityException {
        byte[] signingKey = sha256(getBytesUTF8(clientSecret + "JWTSig"));
        byte[] input = getBytesUTF8(envelope + "." + claims);

        Mac hmac = Mac.getInstance("HmacSHA256");
        hmac.init(new SecretKeySpec(signingKey, "HmacSHA256"));

        byte[] calculated = hmac.doFinal(input);

        if (!Arrays.equals(calculated, BaseEncoding.base64Url().decode(sig)))
            throw new GeneralSecurityException("Signature verification failed");
    }

    private byte[] getBytesUTF8(String s) {
        try {
            return s.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    private byte[] sha256(byte[] input) {
        try {
            return MessageDigest.getInstance("SHA-256").digest(input);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}

ノード:

var crypto = require('crypto');

var token = "BLAH.BLAH.BLAH";
var parts = token.split(".");

var input = parts[0] + "." + parts[1];
var masterKey = "YOUR MASTER KEY";

var key = crypto.createHash('sha256').update(masterKey + "JWTSig").digest('binary');
var str = crypto.createHmac('sha256', key).update(input).digest('base64');

console.log(str);
console.log(parts[2]);
于 2015-04-27T05:18:46.477 に答える
0

PHP:

authentication_code 部分をデコードするために、実装を参照できるコード スニペットを次に示します。

function jsonWebTokenBase64Decode($string)
{
    $string = str_replace('-', '+', $string);
    $string = str_replace('_', '/', $string);

    switch (strlen($string) % 4)
    {
        case 0: break;
        case 2: $string .= '=='; break;
        case 3: $string .= '='; break;
        default: throw createInvalidAuthenticationTokenException();
    }

    return base64_decode($string);
}

function jsonWebTokenBase64Encode($string)
{
    $string = base64_encode($string);
    $string = trim($string, '=');
    $string = str_replace('+', '-', $string);
    return str_replace('/', '_', $string);
}

function decodeAuthenticationToken($authenticationToken, $clientSecret)
{
    // Break the token into segments delimited by dots and verify there are three segments
    $segments = explode('.', $authenticationToken);
    if (count($segments) != 3)
    {
        throw createInvalidAuthenticationTokenException();
    }

    // Decode the segments to extract two JSON objects and the signature
    $envelope = json_decode(jsonWebTokenBase64Decode($segments[0]), true);
    $claims = json_decode(jsonWebTokenBase64Decode($segments[1]), true);
    $signature = $segments[2];

    // If the authentication token is expired, return false
    if ($claims['exp'] < time())
    {
        return false;
    }

    // Verify that the algorithm and token type are correct
    if ($envelope['alg'] != 'HS256')
    {
        throw createInvalidAuthenticationTokenException();
    }

    if ($envelope['typ'] != 'JWT')
    {
        throw createInvalidAuthenticationTokenException();
    }

    // Compute the signing key by hashing the client secret
    $encodedClientSecret = utf8_encode($clientSecret . 'JWTSig');
    $signingKey = hash('sha256', $encodedClientSecret, true);

    // Concatenate the first two segments of the token and perform an HMAC hash with the signing key
    $input = utf8_encode($segments[0] . '.' . $segments[1]);
    $hashValue = hash_hmac('sha256', $input, $signingKey, true);

    // Validate the token by base64 encoding the hash value and comparing it to the signature
    $encodedHashValue = jsonWebTokenBase64Encode($hashValue);
    if ($encodedHashValue != $signature)
    {
        throw createInvalidAuthenticationTokenException();
    }

    // If the token passes validation, return the user ID stored in the token
    return $claims['uid'];
}
于 2016-10-29T09:39:06.290 に答える
0

次の C# サンプルのように「JWTSig」が必要です。

    public static byte[] EncodeSigningToken(string token)
    {
        try
        {

            var sha256 = new SHA256Managed();
            var secretBytes = StrToByteArray(token + "JWTSig");

            var signingKey = sha256.ComputeHash(secretBytes);

            return signingKey;
        }
        catch (Exception)
        {
            return null;
        }
    }

またはこれ:

    private void ValidateSignature(string key)
    {
        // Derive signing key, Signing key = SHA256(secret + "JWTSig")
        byte[] bytes = UTF8Encoder.GetBytes(key + "JWTSig");
        byte[] signingKey = SHA256Provider.ComputeHash(bytes);

        // To Validate:
        // 
        // 1. Take the bytes of the UTF-8 representation of the JWT Claim
        //  Segment and calculate an HMAC SHA-256 MAC on them using the
        //  shared key.
        //
        // 2. Base64url encode the previously generated HMAC as defined in this
        //  document.
        //
        // 3. If the JWT Crypto Segment and the previously calculated value
        //  exactly match in a character by character, case sensitive
        //  comparison, then one has confirmation that the key was used to
        //  generate the HMAC on the JWT and that the contents of the JWT
        //  Claim Segment have not be tampered with.
        //
        // 4. If the validation fails, the token MUST be rejected.

        // UFT-8 representation of the JWT envelope.claim segment
        byte[] input = UTF8Encoder.GetBytes(this.envelopeTokenSegment + "." + this.claimsTokenSegment);

        // calculate an HMAC SHA-256 MAC
        using (HMACSHA256 hashProvider = new HMACSHA256(signingKey))
        {
            byte[] myHashValue = hashProvider.ComputeHash(input);

            // Base64 url encode the hash
            string base64urlEncodedHash = this.Base64UrlEncode(myHashValue);

            // Now compare the two has values
            if (base64urlEncodedHash != this.Signature)
            {
                throw new Exception("Signature does not match.");
            }
        }
    }
于 2015-04-27T02:11:27.613 に答える