OpenID Connect のメモ

Amazon Cognito の署名検証のコードを書いたので一旦メモとして残す。

import java.math.BigInteger
import java.net.URI
import java.security.interfaces.RSAPublicKey
import java.security.spec.RSAPublicKeySpec
import java.security.{KeyFactory, PublicKey}
import java.util.Base64

import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import spray.json._

private case class Key(alg: String,
                       e: String,
                       kid: String,
                       kty: String,
                       n: String,
                       use: String)
private case class JwksJson(keys: Array[Key])

private object JwksJsonProtocol extends DefaultJsonProtocol {
  implicit val keyJsonFormat = jsonFormat6(Key)
  implicit val inputJsonFormat = jsonFormat(JwksJson, "keys")
}

trait OpenIDConnectGetKey {
  val openIdConnectUrl: String
  private lazy val jwksJsonUrl =
    new URI(openIdConnectUrl.concat("/.well-known/jwks.json"))
  private var keys: Map[String, PublicKey] = Map.empty

  def openIDConnectGetKeys: Map[String, PublicKey] = {
    import JwksJsonProtocol._

    val input = jwksJsonUrl.toURL.openStream()
    try {
      val bytes = Stream.continually(input.read).takeWhile(_ != -1).map(_.toByte).toArray
      val jwksJson = JsonParser(bytes).convertTo[JwksJson]
      keys ++= jwksJson.keys.map { key =>
        val kty = key.kty
        val modulus = new BigInteger(1, Base64.getUrlDecoder.decode(key.n))
        val publicExponent = new BigInteger(1, Base64.getUrlDecoder.decode(key.e))
        val keySpec = new RSAPublicKeySpec(modulus, publicExponent)
        key.kid -> KeyFactory.getInstance(kty).generatePublic(keySpec)
      }.toMap
      keys
    } finally {
      input.close()
    }
  }

  def verify(idToken: String): JsValue = {
    val jwt = JWT.decode(idToken)
    val maybePublicKey = keys.get(jwt.getKeyId) match {
      case None => openIDConnectGetKeys.get(jwt.getKeyId)
      case s => s
    }
    maybePublicKey map { publicKey =>
      val algorithm = Algorithm.RSA256(publicKey.asInstanceOf[RSAPublicKey], null)
      val verification = JWT.require(algorithm)
      verification.build().verify(jwt)
    } getOrElse(throw new RuntimeException(s"Not exists ${jwt.getKeyId}"))
    JsonParser(Base64.getUrlDecoder().decode(jwt.getPayload))
  }
}

Java で書いてみた

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public interface OpenIDConnectGetKeys {

    Map<String, KeyItem> getKeys();
    void saveKeys(Map<String, KeyItem> keys);
    String getOpenIdConnectUrl();
    default URI getJwksJsonUrl() throws URISyntaxException {
        return new URI(getOpenIdConnectUrl().concat("/.well-known/jwks.json"));
    }
    default BigInteger decodeBase64UrlUInt(String value) {
        byte[] uintBinary = Base64.getUrlDecoder().decode(value);
        return new BigInteger(1, uintBinary);
    }
    default Map<String, KeyItem> openIDConnectGetKeys() throws URISyntaxException, IOException {
        try (InputStream input = getJwksJsonUrl().toURL().openStream()) {
            Map<String, KeyItem> map = new HashMap();
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            while (true) {
                int ch = input.read();
                if (ch == -1) {
                    break;
                }
                buffer.write(ch);
            }
            ObjectMapper mapper = new ObjectMapper();
            Keys keys = mapper.readValue(buffer.toByteArray(), Keys.class);
            for (KeyItem key: keys.keys) {
                map.put(key.kid, key);
            }
            return map;
        }
    }
    default HashMap<String, Object> verify(String idToken) throws TokenVerifyException {
        try {
            DecodedJWT jwt = JWT.decode(idToken);
            Map<String, KeyItem> keys = getKeys();
            if (!keys.containsKey(jwt.getKeyId())) {
                keys = openIDConnectGetKeys();
                saveKeys(keys);
            }
            KeyItem key = keys.get(jwt.getKeyId());
            if (key != null) {
                BigInteger modulus = decodeBase64UrlUInt(key.n);
                BigInteger publicExponent = decodeBase64UrlUInt(key.e);
                KeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
                RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(spec);
                Algorithm algorithm = Algorithm.RSA256(publicKey, null);
                Verification verification = JWT.require(algorithm);
                verification.build().verify(jwt);
                ObjectMapper mapper = new ObjectMapper();
                TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {};
                return mapper.readValue(Base64.getUrlDecoder().decode(jwt.getPayload()), typeRef);
            } else {
                throw new TokenVerifyException(String.format("Not exists %s", jwt.getKeyId()));
            }
        } catch (IOException e) {
            throw new TokenVerifyException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new TokenVerifyException(e);
        } catch (URISyntaxException e) {
            throw new TokenVerifyException(e);
        } catch (InvalidKeySpecException e) {
            throw new TokenVerifyException(e);
        }
    }
}

古くなったMacBook ProからバッテリーとHDDを取り出した

気づくと、バッテリーがパンパンに膨らんでいて、とりあえず、バッテリーとHDDを取り出そうと MacBook Pro の分解をはじめました

f:id:section27:20190824183150j:plain

以前、バッテリーをはずすYドライバーは購入していたのですが、バッテリーの膨らみにより簡単には外せませんでした。もっと早く外しておけばよかったと後悔。

目的のハードディスクは、ホームセンターで購入した精密ドライバーセット(EPS-650)を使って外しました。

参考にした記事

www.mikinote.com

HeadlessなDebianをmacOSのVirtualBox上に構築したメモ

  • Debianからインストールイメージをダウンロード
  • インストールイメージを使って、仮想マシンを構築(sshを有効にしておきます)

ホストオンリーアダプターを追加

/etc/network/interfaces にホストオンリーアダプターを追記

allow-hotplug enp0s8
iface enp0s8 inet dhcp

ホストオンリーアダプターに設定された IP Address の確認

$ ip addr show

Installing the Linux Guest Additions

Guest Additions for Linux

# mount /dev/cdrom /media/cdrom
# apt-get install -y dkms build-essential linux-headers-$(uname -r)
# ./VBoxLinuxAdditions.run

ヘッドレス起動

VBoxHeadless, the Remote Desktop Server

$ VBoxManage startvm "VM name" --type headless

Ubuntu 18.04 に pyenv と Python 3.7.2 をインストールしたメモ

$ sudo apt-get install git
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
export PYENV_ROOT=$HOME/.pyenv
export PATH=${PYENV_ROOT}/bin:$PATH
eval "$(pyenv init -)"
$ sudo apt-get install gcc make
$ sudo apt-get install libssl-dev \
  libbz2-dev \
  libreadline-dev \
  libsqlite3-dev
$ sudo apt-get install zlib1g-dev
$ sudo apt-get install libffi-dev
$ pyenv install -v 3.7.2

VirtualBox に Ubuntu 18.04 と Docker をインストールしたメモ

VirtualBox 上の Ubuntussh でログインするために、openssh-server をインストール

$ sudo apt-get install openssh-server

Docker CE をインストール

Get Docker CE for Ubuntu

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
$ sudo gpasswd -a your-user docker

確認

$ docker run hello-world

MacBook に pyenv をインストールしたときのメモ

Homebrew

$ brew install pyenv

.bash_profileの編集

$HOME/.bash_profile

export PYENV_ROOT=$HOME/.pyenv
export PATH=$(pyenv root)/shims:$PATH

Pythonのインストール

$ sudo installer \
  -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg \
  -target /
$ pyenv install 3.7.2
$ pyenv global 3.7.2

参考