SwiftでRSAを実装してみた（改良版）

前回作成した[このアプリ](https://qiita.com/masaomi346/items/f4ce3d0487b38b4e5bfb)を改良していきます。
開発環境、アプリの画面構成は前回と同じです。
今回は[BigInt](https://github.com/attaswift/BigInt)を使って実装してみました。

#BigIntを導入する
ライブラリの管理ツールにCocoaPodsを使用しています。

1.プロジェクトファイルのところまで移動してこのコマンドを打ち、Podfileを生成する。

```
pod init
```

2.Podfileを次のように書き換える

```
# Uncomment the next line to define a global platform for your project
# platform :ios, '11.4'

target 'RSACrypter' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!
  pod 'BigInt', '~> 3.0'
  # Pods for RSACrypter

end
```

BigIntのバージョンは3.0を使っています。

3.次のコマンドを打ってインストールする。

```
pod install
```

これでxcworkspaceが生成されます。
#コードを書き換える
BigIntに合わせて書き換えます。

```swift:ViewController.swift
import UIKit
import Foundation
import BigInt

class ViewController: UIViewController,UITextFieldDelegate,UITextViewDelegate {
    
    var p:BigInt = 0
    var q:BigInt = 0
    var r:BigInt = 0
    
    var sample:String = "11111"
    
    @IBOutlet weak var textView1: UITextView!
    @IBOutlet weak var textView2: UITextView!
    
    @IBOutlet weak var textField1: UITextField!
    @IBOutlet weak var textField2: UITextField!
    @IBOutlet weak var textField3: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        textField1.delegate = self
        textField2.delegate = self
        textField3.delegate = self
        textView1.delegate = self
        textView2.delegate = self
        textView1.text = ""
        textView2.text = ""
        p = PrimeNumber(bit: 256)
        q = PrimeNumber(bit: 256)
        r = PrimeNumber(bit: 256)
        while true {
            if CheckCrypt(p: p, q: q, r: r) {
                break
            } else {
                p = PrimeNumber(bit: 256)
                q = PrimeNumber(bit: 256)
                r = PrimeNumber(bit: 256)
            }
        }
        textField1.text = String(p)
        textField2.text = String(q)
        textField3.text = String(r)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    //暗号ボタンを押した時の処理
    @IBAction func Encrypt(_ sender: UIButton) {
        let pq:BigInt = p * q
        let crypt:BigInt = Mod(num: BigInt(textView1.text)!, exp: BigInt(textField3.text!)!, a: pq)
        textView2.text = String(crypt)
    }
    
    //復号ボタンを押した時の処理
    @IBAction func Decrypt(_ sender: UIButton) {
        let key:BigInt = privateKey(a: BigInt(textField1.text!)!, b: BigInt(textField2.text!)!, c: BigInt(textField3.text!)!)
        let pq:BigInt = p * q
        let crypt:BigInt = Mod(num: BigInt(textView2.text)!, exp: key, a: pq)
        textView1.text = String(crypt)
    }
    
    //生成ボタンを押した時の処理
    @IBAction func Generate(_ sender: UIButton) {
        p = PrimeNumber(bit: 256)
        q = PrimeNumber(bit: 256)
        r = PrimeNumber(bit: 256)
        while true {
            if CheckCrypt(p: p, q: q, r: r) {
                break
            } else {
                p = PrimeNumber(bit: 256)
                q = PrimeNumber(bit: 256)
                r = PrimeNumber(bit: 256)
            }
        }
        textField1.text = String(p)
        textField2.text = String(q)
        textField3.text = String(r)
    }
    
    //素数生成
    func PrimeNumber(bit: Int) -> BigInt {
        while true {
            var random = BigUInt.randomInteger(withExactWidth: bit)
            random |= BigUInt(1)
            if random.isPrime() {
                return BigInt(random)
            }
        }
    }
    
    //最小公倍数の計算
    func Lcm(a: BigInt, b: BigInt) -> BigInt {
        var i:BigInt = a
        var j:BigInt = b
        var c:BigInt
        let ij:BigInt = i * j
        while i % j != 0 {
            c = j
            j = i % j
            i = c
        }
        return ij / j
    }
    
    //べき乗計算
    func Mod(num: BigInt, exp: BigInt, a: BigInt) -> BigInt {
        var res:BigInt = 1
        var number:BigInt = num
        var e:BigInt = exp
        while e > 0 {
            if (e & 1) > 0 {
                res = (res * number) % a
            }
            number = (number * number) % a
            e >>= 1
        }
        return res
    }
    
    //秘密鍵生成
    func privateKey(a: BigInt, b: BigInt, c: BigInt) -> BigInt {
        let lcm = Lcm(a: a - 1, b: b - 1)
        var a1:BigInt = 1
        var b1:BigInt = 0
        var c1:BigInt = lcm
        var a2:BigInt = 0
        var b2:BigInt = 1
        var c2:BigInt = c
        var a3:BigInt
        var b3:BigInt
        var c3:BigInt
        var d:BigInt = 1
        while c2 > 0 {
            d = BigInt(c1 / c2)
            a3 = a1 - d * a2
            b3 = b1 - d * b2
            c3 = c1 - d * c2
            a1 = a2
            b1 = b2
            c1 = c2
            a2 = a3
            b2 = b3
            c2 = c3
        }
        return (a1 > b1) ? a1 : b1
    }
    
    //3つの素数で暗号・復号できるか確認
    func CheckCrypt(p: BigInt, q: BigInt, r: BigInt) -> Bool {
        let pq:BigInt = p * q
        var crypt:BigInt = Mod(num: BigInt(sample)!, exp: r, a: pq)
        let key:BigInt = privateKey(a: p, b: q, c: r)
        crypt = Mod(num: crypt, exp: key, a: pq)
        if String(crypt) == sample {
            return true
        }
        return false
    }
    
    //キーボードのreturnキー押す時の処理
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }

    //textViewでreturnキー押す時の処理
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if text == "\n" {
            textView.resignFirstResponder()
            return false
        }
        return true
    }
}

```
512bitの鍵を生成して暗号・復号しています。素数生成の部分以外は前回とほとんど変わっていません。
#実行してみる
<img width="1440" alt="スクリーンショット 2018-08-14 10.45.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/274361/3ce4e1d6-1ee7-4247-bb22-fcceb89a31bd.png">
前回のやつと比べるとかなり重くなりますが、実用的になりました。
もう少し鍵の長さを長くしても大丈夫だと思う。