[ruby] ruby-openid で signature アルゴリズムに SHA256 を使う
解決方法
OpenID::DefaultNegotiator.allowed_types = [['HMAC-SHA256', 'DH-SHA256']] server = OpenID::Server::Server.new(ActiveRecordStore.new, servers_url)
consumer 実装の場合も一緒。
ruby-openid のサンプルコードに従っているのだけど openid.mode = associate 時の provider 側挙動が怪しい。
だんだん ruby-openid のデバッグになってくる。
OpenID カンペキ理解のためにも ruby-openid 再実装しちゃろうかーとか考え始める。
以下は調査ログ
- -
自分のために情報整理を兼ねてメモ。
ruby-openid(2.0.4) を使って OpenID の処理を書くとき signature アルゴリズムで SHA1 でなく SHA256 が選びたいのだけど、どうも OpenID::Consumer の実装が SHA1 以外選べないようになっている気がする。
今日は追跡記録を書いて、明日、SHA256 を使う為の実装を書いてみよう。
Rails で OpenID リクエストを発行するには以下のように
consumer = OpenID::Consumer.new(session, store) oidreq = consumer.begin(params[:openid_identifier])
begin を実行すると OpenID でいう discovery, association 両方のプロセスが実行される。
begin(consumer.rb)の中身は
def begin(openid_identifier, anonymous=false) # discovery プロセスの実行 manager = discovery_manager(openid_identifier) service = manager.get_next_service(&method(:discover)) if service.nil? raise DiscoveryFailure.new("No usable OpenID services were found "\ "for #{openid_identifier.inspect}", nil) else # association プロセスの実行 begin_without_discovery(service, anonymous) end end
begin_without_discovery の中身は
def begin_without_discovery(service, anonymous) # get_association で OP と通信を行い Association を確立する assoc = association_manager(service).get_association checkid_request = CheckIDRequest.new(assoc, service) checkid_request.anonymous = anonymous if service.compatibility_mode rt_args = checkid_request.return_to_args rt_args[Consumer.openid1_return_to_nonce_name] = Nonce.mk_nonce rt_args[Consumer.openid1_return_to_claimed_id_name] = service.claimed_id end self.last_requested_endpoint = service return checkid_request end
get_association(associationmanager.rb)は
def get_association if @store.nil? return nil end assoc = @store.get_association(@server_url) if assoc.nil? || assoc.expires_in <= 0 # ここで association 生成 assoc = negotiate_association if !assoc.nil? @store.store_association(@server_url, assoc) end end return assoc end
で、また negotiate_association を追う
def negotiate_association assoc_type, session_type = @negotiator.get_allowed_type begin return request_association(assoc_type, session_type) rescue ServerError => why supported_types = extract_supported_association_type(why, assoc_type) if !supported_types.nil? # Attempt to create an association from the assoc_type and # session_type that the server told us it supported. assoc_type, session_type = supported_types begin return request_association(assoc_type, session_type) rescue ServerError => why Util.log("Server #{@server_url} refused its suggested " \ "association type: session_type=#{session_type}, " \ "assoc_type=#{assoc_type}") return nil end end end end
assoc_type と session_type の中身を見るとそれぞれ HMAC-SHA1 と DH-SHA1 がアサインされている。
で、どこで決まっているのかと get_allowed_type を追うと
get_allowed_type(association.rb)
def get_allowed_type @allowed_types.empty? ? nil : @allowed_types[0] end
allowed_types は AssociationNegotiator オブジェクトのメンバで association.rb から AssociationNegotiator 関連の実装を抜き出すと
module OpenID class AssociationNegotiator attr_reader :allowed_types def self.get_session_types(assoc_type) case assoc_type when 'HMAC-SHA1' ['DH-SHA1', 'no-encryption'] when 'HMAC-SHA256' ['DH-SHA256', 'no-encryption'] else raise StandardError, "Unknown association type #{assoc_type.inspect}" end end def initialize(allowed_types) self.allowed_types=(allowed_types) end def allowed_types=(allowed_types) allowed_types.each do |assoc_type, session_type| self.class.check_session_type(assoc_type, session_type) end @allowed_types = allowed_types end def get_allowed_type @allowed_types.empty? ? nil : @allowed_types[0] end end DefaultNegotiator = AssociationNegotiator.new([['HMAC-SHA1', 'DH-SHA1'], ['HMAC-SHA1', 'no-encryption'], ['HMAC-SHA256', 'DH-SHA256'], ['HMAC-SHA256', 'no-encryption']]) EncryptedNegotiator = AssociationNegotiator.new([['HMAC-SHA1', 'DH-SHA1'], ['HMAC-SHA256', 'DH-SHA256']]) end
これ、DefaultNegotiator でも EncryptoNegotiator でも get_allowed_type を使う限り SHA1 が常に呼ばれてしまう。
と同時に、「新しい Negotiator を定義する」方法が使えそう。
では negotiator の決定をどこで行っているのかと探すと consumer.rb の begin_without_discovery で決まっている。
assoc = association_manager(service).get_association
association_manager の実装は
def association_manager(service) AssociationManager.new(@store, service.server_url, service.compatibility_mode, negotiator) end
negotiator の実装は
def negotiator DefaultNegotiator end
追跡編は以上。
明日は SHA256 を使えるようにしてみよう。