0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

cscの作法 その225

Posted at

概要

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

以上。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?