暗号ツール Webアプリケーション  その1

前書き

 この記事は,SLP KBIT Advent Calendar 2019 の20日目の記事です。

adventar.org

 皆さんは、暗号にどれほど興味がありますか。普段、私が使っている連絡ツールにも暗号は使われています。
今回は、難しい暗号には挑戦しませんが、簡単な暗号方程式を考え、暗号を生成するWebアプリケーションを作成しました。
その方法を紹介したいと思います。

目次

開発環境

  • Windows10
  • ruby 2.6.5

インストール

gem install sinatra

暗号方程式

 今回、3つの暗号方程式を紹介します。
1つ目は大文字のアルファベット、2つ目は小文字のアルファベット、3つ目は数字を暗号化する方程式です。 これから紹介する暗号方程式は、ASCIIコードという文字データを用いて考えました。また、10進数の値で考えているため、10進数のデータで説明します。

 1つ目は、下の図のように「M」と「N」を境に鏡状に文字を反転させる方程式です。 f:id:Drink_15:20191213204008p:plain
 例えば、Aという文字をASCIIコードで表すと65、Zは90になります。単純にAをZにしたいとき25を足せばよいです。しかし、MをNにしたいとき、25を足しても上手くいきません。そのため、25の数値を変化させる必要があります。

 変化させるにあたって、25とはどんな数値か考えてみましょう。もちろん、AからZまでの差でもありますが、細かく分ければ、AからM MからN NからZの差を足したものだとも考えられます。MからNまでの差は1でNからZまでの差はAからMの差と同じです。AからMまでの差を2倍したものに1足した値が25です。つまり、文字を鏡状に反転させるには、特定の文字からMまでの差を2倍して1足した値をその文字に足せば、反転させることができます。

 上に記述した方法では、NからZの文字をAからMに反転させることはできないと疑問に持つ人がいるかもしれません。試しにZで計算してみましょう。 ASCIIコードに直すと、Zは90でMは77です。
2*(77-90)+1=-25 90-25=65
上の式のように65という値になりました。ASCIIコードで65はAを表します。このように、NからZの値もAからMの値に 反転させることができます。

 これをプログラムで表すと以下のようになります。

puts "パスワードを入力してください"
keyword = gets.chomp
puts "暗号化前: #{keyword}"
code = []

keyword.chars.each do |char| 
  if (65 <= char.ord && char.ord <= 90) then
    num1 = char.ord 
    num2 = 77 - num1
    num3 = 2 * num2 + 1 + num1      
  end
  code << num3.chr
end
puts "暗号化後: #{code.join("")}" 

実行結果

パスワードを入力してください
ABM
暗号化前: ABM
暗号化後: ZYN

 2つ目は、下の図のように1文字目は1つずらし、2文字目は2つずらし、3文字目は3つずらし、再び4文字目は1つずらすような、1、2、3文字分ずらす操作を繰り返す方程式です。 f:id:Drink_15:20191218201712p:plain

a b c d e f g h i j k l m n o p q r s t u v w x y z
b d f e g i h j l k m o n p r q s u t v x w y a z b

 上の表を見てもらえば分かるように、変換後bが2回出てきており、cが1度も出てきてません。
暗号だけの場合は、それでも別に構いませんが復号のことも考えるとあまりよくありません。bはaとzのどちらに復号すればよいか、分からなくなります。例外ですが、zは変換後cになるようにします。

 aをASCIIコードで表すと97です。なので、96を引いて1 2 3…というように小さい数値で考えます。この時に、3で割りあまりの数で何文字ずらすか判断します。あまりが0の場合は99、1の場合は97、2の場合は98を足せばよいと分かります。しかし、xはaに変換に限っては大きな値から小さい値に変換する必要があり、普通に足すだけではできません。xは3で割り切れるので、あまりが0の場合は除法の計算を用いて考えます。3で割りきれる場合、97を引いて3を足します。その後、26で割りあまりを見ます。そのあまりと97を足すと3ずらすことができます。
3をずらしたい数に変更すれば、その分ずらすことできます。また、26で割る理由はアルファベットが26個のためです。数字に適応させる時など、必要に応じてこの値を変える必要があります。
cとxの2つ場合を考えてみましょう。ASCIIコードでcは99、xは120です。

cの場合
 99-97=2 (2+3)/26=0…5 97+5=102
xの場合
 120-97=23 (23+3)/26=1…0 97+0=97
上の計算式よりcがf、xがaになったことが分かります。
プログラムで表すと以下のようになります。

keyword.chars.each do |char| 
  num1 = char.ord - 96 
  if (num1 == 26) then 
      num3 = 99 
  elsif (num1 % 3 == 0) then 
      num1 = num1 - 1
      num2 = (num1 + 3) % 26
      num3 = num2 + 97
  elsif (num1 % 3 == 1) then 
      num3 = num1 + 97           
  else 
      num3 = num1 + 98 
  end
  code << num3.chr
end

実行結果

パスワードを入力してください
abcdefghijklmnopqrstuvwxyz
暗号化前: abcdefghijklmnopqrstuvwxyz
暗号化後: bdfegihjlkmonprqsutvxwyazc

 私は、先ほど考えた反転の方程式と組み合わせてより複雑にしました。
そのプログラムを載せておきます。

keyword.chars.each do |char| 
  if (97 <= char.ord && char.ord <= 122) then
    num1 = char.ord 
    num2 = 109 - num1
    num3 = 2 * num2 + 1 + num1
    num4 = num3 - 96 
    if (num4 == 26) then 
        num5 = 99
    elsif (num4 % 3 == 0) then
        num4 = num4 - 1 
        num6 = (num4 + 3) % 26
        num5 = num6 + 97
    elsif (num4 % 3 == 1) then 
        num5 = num4 + 97 
    else 
        num5 = num4 + 98 
    end
  end
  code << num5.chr
end

実行結果

パスワードを入力してください
abc
暗号化前: abc
暗号化後: cza

 3つ目は、入力した順に文字を1つ2つ3つずらす操作を繰り返す方程式です。
例えば、 0000 と入力すると 1231 というように変換されます。 3つ目の方程式はjという変数を用いて考えます。入力した数をjだけずらします。初めは、jに1を代入し入力されるたびにjの値を増加させます。jが3以上になれば再びjに1を代入します。
プログラムで表すと以下のようになります。

keyword.chars.each do |char| 
if(48 <= char.ord && char.ord <= 58)then
  num1 = char.ord - 48
  num2 = (num1 + j) % 10
  num3 = num2 + 48
  if(j >= 3)then
      j = 1
          
  else
      j = j + 1
  end
end
  code << num3.chr
end

実行結果

パスワードを入力してください
000000
暗号化前: 000000
暗号化後: 123123

復号方程式

 復号の方法ですが、暗号の方法とあまり変わりません。
 1つ目の復号方程式ですが、暗号方程式と同じです。要するに、もう一度反転作業を行えばよいだけです。

 順番を少し変えて3つ目の復号方程式について説明します。これもほとんど暗号方程式と同じです。暗号化する際、jを足して文字をずらしました。なので、復号する際はjを引いて文字ずらします。
プログラムで表すと以下のようになります。

j=1
keyword.chars.each do |char| 
if(48 <= char.ord && char.ord <= 58)then
  num1 = char.ord - 48
  num2 = (num1 - j) % 10
  num3 = num2 + 48
  if(j >= 3)then
      j = 1
          
  else
      j = j + 1
  end
end
  code << num3.chr
end

実行結果

パスワードを入力してください
123123
復号前: 123123
復号後: 000000

 最後に2つ目の復号方程式は、どれだけ数を足せば3で割り切れるかでずらす値を判断します。
暗号化の際、文字を1つずらしたものは、3で割ってあまりが2になるものに変換されます。なので、1を足せば3で割り切れる値になります。よって、1を足して3で割り切れる値は1つ文字をずらして復号します。
文字を2つずらしたものは、3で割って余りが1になるものに変換されます。なので、2を足せば3で割り切れる値になります。よって、2を足して3で割りきれる値は2つずらして復号します。残った値は、3つずらし復号します。 暗号化の際、例外を1つ作りました。最初に例外の復号を行うことを忘れないようにしましょう。
プログラムで表すと以下のようになります。

keyword.chars.each do |char| 
  if (97 <= char.ord && char.ord<=122)then
    num = char.ord

    if(num == 97)then
        num3 =120

    elsif((num+1)%3==0)then
        num1 = num - 97
        num2 = (num1 - 1) % 26
        num3 = num2 + 97

    elsif((num+2)%3==0)then
        num1 = num - 97
        num2 = (num1 - 2) % 26
        num3 = num2 + 97

    else
        num1 = num - 97
        num2 = (num1 - 3) % 26
        num3 = num2 + 97
    end
  end
  code << num3.chr
end

実行結果

パスワードを入力してください
bdfegihjlkmonprqsutvxwyazc
復号前: bdfegihjlkmonprqsutvxwyazc
復号後: abcdefghijklmnopqrstuvwxyz

 私は、少し複雑な暗号にしたのでその復号のプログラムも載せておきます。

keyword.chars.each do |char| 
  if (97 <= char.ord && char.ord<=122)then
    num = char.ord

    if(num == 97)then
        num3 =120

    elsif((num+1)%3==0)then
        num1 = num - 97
        num2 = (num1 - 1) % 26
        num3 = num2 + 97

    elsif((num+2)%3==0)then
        num1 = num - 97
        num2 = (num1 - 2) % 26
        num3 = num2 + 97

    else
        num1 = num - 97
        num2 = (num1 - 3) % 26
        num3 = num2 + 97
    end
    num4 = 109 - num3
    num5 = 2 * num4 + 1 + num3  
  end
  code << num5.chr
end

実行結果

パスワードを入力してください
cza
復号前: cza
復号後: abc

追記

内容が思っていたよりも多くなったので、その2に続きます。