Swift 4 で RSA 暗号を使った文字列の暗号化/復号を行う

メモです。

f:id:bps_tomoya:20171229193056j:plain

あ、これはお肉です。

Swift の素振りで、RSA 暗号を使った文字列の暗号化/復号を行ってみました。
実装は、参考にしたこちらの記事そのまんまなのですが、現行の Swift4 では動かない箇所がいくつかありました。
ので、動くようにしたコードをここに残しておきます。

qiita.com

コード

func main() {
    // 暗号化対象の文字列と UInt8 Byte Array でのデータ、文字数を確保しておく
    let plainText = "I'm TomoyaShibata!"
    let plainTextData = [UInt8](plainText.utf8)
    let plainTextDataLength = plainText.count

    // 自作したキーペア生成メソッドの呼び出す
    let keyPair = self.generateKeyPair()
    let blockSize = SecKeyGetBlockSize(keyPair.publicKey!)

    // 暗号化した結果を入れる、公開鍵のブロック長に応じた変数を用意する
    var encryptedData = [UInt8](
        repeating: 0,
        count: Int(blockSize)
    )
    var encryptedDataLength = blockSize

    // 暗号化対象の文字列を暗号化する
    // 暗号化/復号のばあいパディングには SecPadding.PKCS1 または SecPadding.OAEP いずれかが利用可能
    // それ以外は署名/検証にしか使えないってコメントに書いてあった
    let encryptOsStatus = SecKeyEncrypt(
        keyPair.publicKey!,
        SecPadding.PKCS1,
        plainTextData,
        plainTextDataLength,
        &encryptedData,
        &encryptedDataLength
    )
    // 暗号化に失敗していたら終了する
    if encryptOsStatus != noErr {
        print("Encryption Error")
        return
    }

    // 復号した結果を入れる、公開鍵のブロック長に応じた変数を用意する
    var decryptedData = [UInt8](
        repeating: 0,
        count: Int(blockSize)
    )
    var decryptedDataLength = blockSize

    // 暗号化されたデータを復号する
    // 暗号化/復号のばあいパディングには SecPadding.PKCS1 または SecPadding.OAEP いずれかが利用可能
    // それ以外は署名/検証にしか使えないってコメントに書いてあった
    let decryptOsStatus = SecKeyDecrypt(
        keyPair.privateKey!,
        SecPadding.PKCS1,
        encryptedData,
        encryptedDataLength,
        &decryptedData,
        &decryptedDataLength
    )
    // 復号に失敗していたら終了する
    if decryptOsStatus != noErr {
        print("Decryption Error")
        return
    }

    // 復号したデータを文字列にする
    let decryptText = NSString(
        bytes: &decryptedData,
        length: decryptedDataLength,
        encoding: String.Encoding.utf8.rawValue
    )!
    // 暗号化の文字列と、復号したデータの文字列を比較して同じ文字列かどうかを確認する
    if decryptText.compare(plainText) == ComparisonResult.orderedSame {
        print("SUCCESS!")
    } else {
        print("FAIL...")
    }
}

func generateKeyPair() -> (publicKey: SecKey?, privateKey: SecKey?) {
    // RSA 暗号/2048桁という指定を変数に確保している
    // RSA 暗号なので、桁数には 1024、2048、4096 いずれかの値が設定可能
    // 4096桁では体感できるレベルで遅いのでちょっと面白い
    let parameters: [String: Any] = [
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
        kSecAttrKeySizeInBits as String: 2048
    ]

    // SecKeyGeneratePair を KeyPair を生成する
    var publicKey: SecKey?
    var privateKey: SecKey?
    let osStatus = SecKeyGeneratePair(parameters as CFDictionary, &publicKey, &privateKey)

    // エラーなく生成できたら公開鍵と秘密鍵を Tuple で返却する
    switch osStatus {
    case noErr:
        return (publicKey, privateKey)
    default:
        return (nil, nil)
    }
}