cscの作法 その225

# 概要
cscの作法、調べてみた。
jwtをデコードしてみた。

# サンプルコード

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using LitJson;

public enum JwtHashAlgorithm {
	RS256,
	HS384,
	HS512
}
public class jwt {
	private static Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>> HashAlgorithms;
	static jwt() {
		HashAlgorithms = new Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>> {
			{
				JwtHashAlgorithm.RS256, (key, value) => {
					using (var sha = new HMACSHA256(key)) 
					{
						return sha.ComputeHash(value);
					}
				}
			}, {
				JwtHashAlgorithm.HS384, (key, value) => {
					using (var sha = new HMACSHA384(key)) 
					{
						return sha.ComputeHash(value);
					}
				}
			}, {
				JwtHashAlgorithm.HS512, (key, value) => {
					using (var sha = new HMACSHA512(key)) 
					{
						return sha.ComputeHash(value);
					}
				}
			}
		};
	}
	public static string Encode(object payload, string key, JwtHashAlgorithm algorithm) {
		return Encode(payload, Encoding.UTF8.GetBytes(key), algorithm);
	}
	public static string Encode(object payload, byte[] keyBytes, JwtHashAlgorithm algorithm) {
		var segments = new List<string>();
		var header = new {
			alg = algorithm.ToString(),
			typ = "JWT"
		};
		byte[] headerBytes = Encoding.UTF8.GetBytes(LitJson.JsonMapper.ToJson(header));
		byte[] payloadBytes = Encoding.UTF8.GetBytes(LitJson.JsonMapper.ToJson(payload));
		segments.Add(Base64UrlEncode(headerBytes));
		segments.Add(Base64UrlEncode(payloadBytes));
		var stringToSign = string.Join(".", segments.ToArray());
		var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);
		byte[] signature = HashAlgorithms[algorithm](keyBytes, bytesToSign);
		segments.Add(Base64UrlEncode(signature));
		return string.Join(".", segments.ToArray());
	}
	public static string Decode(string token, string key) {
		return Decode(token, key, true);
	}
	public static string Decode(string token, string key, bool verify) {
		var parts = token.Split('.');
		var header = parts[0];
		var payload = parts[1];
		byte[] crypto = Base64UrlDecode(parts[2]);
		var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
		var headerData = LitJson.JsonMapper.ToObject(headerJson);
		var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
		var payloadData = LitJson.JsonMapper.ToObject(payloadJson);
		if (verify)
		{
			var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
			var keyBytes = Encoding.UTF8.GetBytes(key);
			var algorithm = (string) headerData["alg"];
			var signature = HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign);
			var decodedCrypto = Convert.ToBase64String(crypto);
			var decodedSignature = Convert.ToBase64String(signature);
			if (decodedCrypto != decodedSignature)
			{
				throw new ApplicationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));
			}
		}
		return payloadData.ToString();
	}
	private static JwtHashAlgorithm GetHashAlgorithm(string algorithm) {
		switch (algorithm)
		{
		case "RS256":
			return JwtHashAlgorithm.RS256;
		case "HS384":
			return JwtHashAlgorithm.HS384;
		case "HS512":
			return JwtHashAlgorithm.HS512;
		default:
			throw new InvalidOperationException("Algorithm not supported.");
		}
	}
	public static string Base64UrlEncode(byte[] input) {
		var output = Convert.ToBase64String(input);
		output = output.Split('=')[0];
		output = output.Replace('+', '-');
		output = output.Replace('/', '_');
		return output;
	}
	public static byte[] Base64UrlDecode(string input) {
		var output = input;
		output = output.Replace('-', '+');
		output = output.Replace('_', '/');
		switch (output.Length % 4)
		{
		case 0:
		break;
		case 2:
			output += "==";
		break;
		case 3:
			output += "=";
		break;
		default:
			throw new System.Exception("Illegal base64url string!");
		}
		var converted = Convert.FromBase64String(output);
		return converted;
	}
}

public class test0 {
	static void Main() {
		string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
		var parts = token.Split('.');
		var header = parts[0];
		var payload = parts[1];
		byte[] crypto = jwt.Base64UrlDecode(parts[2]);
		var headerJson = Encoding.UTF8.GetString(jwt.Base64UrlDecode(header));
		Console.WriteLine(headerJson);
		var headerData = LitJson.JsonMapper.ToObject(headerJson);
		var payloadJson = Encoding.UTF8.GetString(jwt.Base64UrlDecode(payload));
		Console.WriteLine(payloadJson);
		var payloadData = LitJson.JsonMapper.ToObject(payloadJson);
		var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
		var algorithm = (string) headerData["alg"];
		var decodedCrypto = Convert.ToBase64String(crypto);
		Console.WriteLine("ok");
	}
}
```

# 実行結果

```
>jwt0
{"alg":"HS256","typ":"JWT"}
{"sub":"1234567890","name":"John Doe","iat":1516239022}
ok
```


以上。