Skip to content

Commit

Permalink
Fixed iOS issues and fully tested
Browse files Browse the repository at this point in the history
  • Loading branch information
adlanarifzr committed Jan 23, 2019
1 parent d5cd495 commit b7f7b35
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 50 deletions.
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## 0.0.3

* Fixed iOS functions and optimize the code

## 0.0.2+1

* Add method for decrypt string (more 512 byte) with public key (ANDROID)

## 0.0.2

* Add iOS compatibility
* Add signData and verifyData method

## 0.0.1

* Initial release
* Initial experimentation
35 changes: 26 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Simple RSA Encryption

Flutter plugin to encrypt and decrypt (RSA/ECB/PCSK1) string with a public and a private key
Flutter plugin to encrypt, decrypt (RSA/ECB/PCSK1), verify and sign string with a public and a private key

Support only ANDROID (for now)
Support for ANDROID and iOS(thanks to adlanarifzr)

## Installation

Expand All @@ -11,22 +11,39 @@ To use the plugin, add `simple_rsa` as a

## Usage

First, add private and public key
First, initialize private and public key. Preferably in BASE64 format.

```
final PUBLIC_KEY = "...";
final PRIVATE_KEY = "...";
final publicKey = '...';
final privateKey = '...';
```

After, you can encrypt text
After that, you can encrypt or decyrpt text

```final String textEncrypted = await encryptString(text, PUBLIC_KEY);```
```
let plainText = 'something';
final encryptedText = await encryptString(plainText, utf8.decode(base64.decode(publicKey)));
final decryptedText = await decryptString(encryptedText, utf8.decode(base64.decode(privateKey)));
// Test
print(plainText == decryptedText ? 'true' : 'false');
```

and decrypt
Or you might want to sign and verify text

```final String textDecrypted = await decryptString(encodedText, PRIVATE_KEY);```
```
let plainText = 'something';
final signedText = await signString(plainText, utf8.decode(base64.decode(privateKey)));
final verified = await verifyString(plainText, signedText, utf8.decode(base64.decode(publicKey)));
// Test
print(verified ? 'true' : 'false');
```

## Example

See the [example application](https://github.com/giandifra/simple_rsa/tree/master/example) source
for a complete sample app using the Simple RSA encryption.

### Contributions
[Adlan Arif Zakaria (adlanarifzr)](https://github.com/adlanarifzr) iOS compatibility, sign and verify method.
4 changes: 2 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ group 'com.juanito21.simplersa'
version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.2.30'
ext.kotlin_version = '1.2.51'
repositories {
google()
jcenter()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
52 changes: 50 additions & 2 deletions android/src/main/kotlin/com/juanito21/simplersa/SimpleRsaPlugin.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.juanito21.simplersa

import android.util.Base64
import android.util.Log
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
Expand All @@ -16,6 +17,8 @@ import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException




class SimpleRsaPlugin() : MethodCallHandler {

// private var mRegistrar: Registrar? = registrar
Expand Down Expand Up @@ -46,8 +49,9 @@ class SimpleRsaPlugin() : MethodCallHandler {
e.printStackTrace()
result.error("UNAVAILABLE", "Encrypt failure.", null)
}
}else{
result.error("NULL INPUT STRING", "Encrypt failure.", null)
}
result.error("NULL INPUT STRING", "Encrypt failure.", null)
}
"decrypt" -> {
val text = call.argument<String>("txt")
Expand Down Expand Up @@ -96,6 +100,22 @@ class SimpleRsaPlugin() : MethodCallHandler {
result.error("NULL INPUT STRING", "Verify failure.", null)
}
}
"decryptWithPublicKey" -> {
val text = call.argument<String>("plainText")
val publicKey = call.argument<String>("publicKey")
if (text != null && publicKey != null) {
try {
val d = Base64.decode(text, Base64.DEFAULT)
val output = decryptStringWithPublicKey(d, publicKey)
result.success(output)
} catch (e: java.lang.Exception) {
e.printStackTrace()
result.error("UNAVAILABLE", "Decrypt failure.", null)
}
} else {
result.error("NULL INPUT STRING", "Decrypt failure.", null)
}
}
else -> result.notImplemented()
}
}
Expand Down Expand Up @@ -136,6 +156,35 @@ class SimpleRsaPlugin() : MethodCallHandler {
return String(decryptedBytes)
}

@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, IllegalBlockSizeException::class, BadPaddingException::class)
private fun decryptStringWithPublicKey(encryptedBytes: ByteArray, publicKey: String): String {
val publicBytes = Base64.decode(publicKey, Base64.DEFAULT)
val keySpec = X509EncodedKeySpec(publicBytes)
val keyFactory = KeyFactory.getInstance("RSA")
val pubKey = keyFactory.generatePublic(keySpec)
val cipher1 = Cipher.getInstance("RSA/ECB/PKCS1PADDING")
cipher1.init(Cipher.DECRYPT_MODE, pubKey)

val blockSize = cipher1.blockSize
val blocks : Int = Math.ceil(encryptedBytes.size / blockSize.toDouble()).toInt()
var output = ByteArray(blocks * blockSize)
var outputSize = 0

for (i in 0 until blocks) {
val offset = i * blockSize
val blockLength = Math.min(blockSize, encryptedBytes.size - offset)
val cryptoBlock = cipher1.doFinal(encryptedBytes, offset, blockLength)
System.arraycopy(cryptoBlock, 0, output, outputSize, cryptoBlock.size)
outputSize += cryptoBlock.size
}

if (outputSize != output.size) {
val tmp = output.copyOfRange(0, outputSize)
output = tmp
}
return String(output)
}

@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, IllegalBlockSizeException::class, BadPaddingException::class)
private fun signData(plainText: String, privateKey: String): String {
try {
Expand Down Expand Up @@ -165,5 +214,4 @@ class SimpleRsaPlugin() : MethodCallHandler {
throw Exception(e.toString())
}
}

}
2 changes: 1 addition & 1 deletion example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ flutter {
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
Expand Down
4 changes: 2 additions & 2 deletions example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.2.30'
ext.kotlin_version = '1.2.51'
repositories {
google()
jcenter()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
2 changes: 1 addition & 1 deletion example/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Home extends StatefulWidget {
}

class HomeState extends State<Home> {

final PUBLIC_KEY =
"MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBuAGGBgg9nuf6D2c5AIHc8" +
"vZ6KoVwd0imeFVYbpMdgv4yYi5obtB/VYqLryLsucZLFeko+q1fi871ZzGjFtYXY" +
Expand Down
36 changes: 12 additions & 24 deletions ios/Classes/SwiftSimpleRsaPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,66 @@ import SwiftyRSA

public class SwiftSimpleRsaPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "simple_rsa", binaryMessenger: registrar.messenger())
let channel = FlutterMethodChannel(name: "juanito21.com/simple_rsa", binaryMessenger: registrar.messenger())
let instance = SwiftSimpleRsaPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}

public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let argsMap : NSDictionary = call.arguments as! NSDictionary
print("this is my method" + call.method)
switch (call.method) {
case "encrypt":
let text : String = argsMap["txt"] as! String
let publicKey : String = argsMap["publicKey"] as! String

let base64Key = Data(publicKey.utf8).base64EncodedString()

let res = encryptData(plainText: text, publicKey: base64Key)
let res = encryptData(plainText: text, publicKey: publicKey)
result(res)
case "decrypt":
let text : String = argsMap["txt"] as! String
let privateKey : String = argsMap["privateKey"] as! String

let base64Text = Data(text.utf8).base64EncodedString()
let base64Key = Data(privateKey.utf8).base64EncodedString()

let res = decryptData(encryptedText: base64Text, privateKey: base64Key)
let res = decryptData(encryptedText: text, privateKey: privateKey)
result(res)
case "sign":
let plainText : String = argsMap["plainText"] as! String
let privateKey : String = argsMap["privateKey"] as! String

let base64Key = Data(privateKey.utf8).base64EncodedString()

let res = signData(plainText: plainText, privateKey: base64Key)
let res = signData(plainText: plainText, privateKey: privateKey)
result(res)
case "verify":
let plainText : String = argsMap["plainText"] as! String
let signature : String = argsMap["signature"] as! String
let publicKey : String = argsMap["publicKey"] as! String

let base64Signature = Data(signature.utf8).base64EncodedString()
let base64Key = Data(publicKey.utf8).base64EncodedString()

let res = verifyData(plainText: plainText, signature: base64Signature, publicKey: base64Key)
let res = verifyData(plainText: plainText, signature: signature, publicKey: publicKey)
result(res)
default:
result(FlutterMethodNotImplemented)
}
}

private func encryptData(plainText: String, publicKey: String) -> String {
let publicKey = try! PublicKey(base64Encoded: publicKey)
let publicKey = try! PublicKey(pemEncoded: publicKey)
let clear = try! ClearMessage(string: plainText, using: .utf8)
let encrypted = try! clear.encrypted(with: publicKey, padding: .PKCS1)
return encrypted.base64String
}

private func decryptData(encryptedText: String, privateKey: String) -> String {
let privateKey = try! PrivateKey(base64Encoded: privateKey)
let privateKey = try! PrivateKey(pemEncoded: privateKey)
let encrypted = try! EncryptedMessage(base64Encoded: encryptedText)
let clear = try! encrypted.decrypted(with: privateKey, padding: .PKCS1)
return clear.base64String
let plain = try! clear.string(encoding: String.Encoding(rawValue: 0))
return plain
}

private func signData(plainText: String, privateKey: String) -> String {
let privateKey = try! PrivateKey(base64Encoded: privateKey)
let privateKey = try! PrivateKey(pemEncoded: privateKey)
let clear = try! ClearMessage(string: plainText, using: .utf8)
let signature = try! clear.signed(with: privateKey, digestType: .sha1)
return signature.base64String
}

private func verifyData(plainText: String, signature: String, publicKey: String) -> Bool {
let clear = try! ClearMessage(string: plainText, using: .utf8)
let publicKey = try! PublicKey(base64Encoded: publicKey)
let publicKey = try! PublicKey(pemEncoded: publicKey)
let signature = try! Signature(base64Encoded: signature)
let isSuccessful = try! clear.verify(with: publicKey, signature: signature, digestType: .sha1)
return isSuccessful
Expand Down
8 changes: 4 additions & 4 deletions ios/simple_rsa.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
#
Pod::Spec.new do |s|
s.name = 'simple_rsa'
s.version = '0.0.1'
s.summary = 'A new flutter plugin project.'
s.version = '0.0.3'
s.summary = 'A simple RSA plugin for flutter.'
s.description = <<-DESC
A new flutter plugin project.
DESC
s.homepage = 'http://example.com'
s.homepage = 'https://github.com/adlanarifzr'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.author = { 'Adlan Arif Zakaria' => 'adlanarifzr@gmail.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
Expand Down
14 changes: 12 additions & 2 deletions lib/simple_rsa.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,22 @@ Future<String> signString(String plainText, String privateKey) async {
}
}

Future<String> verifyString(String plainText, String signature, String publicKey) async {
Future<bool> verifyString(String plainText, String signature, String publicKey) async {
try {
final String result = await _channel
final bool result = await _channel
.invokeMethod('verify', {"plainText": plainText, "signature": signature, "publicKey": publicKey});
return result;
} on PlatformException catch (e) {
throw "Failed decoded string: '${e.message}'.";
}
}

Future<String> decryptStringWithPublicKey(String plainText, String signature, String publicKey) async {
try {
final String result = await _channel
.invokeMethod('decryptWithPublicKey', {"plainText": plainText, "publicKey": publicKey});
return result;
} on PlatformException catch (e) {
throw "Failed decoded string: '${e.message}'.";
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: simple_rsa
description: Flutter plugin to encrypt and decrypt string with a public and a private key
version: 0.0.1
version: 0.0.3
author: Gian Marco Di Francesco <[email protected]>
homepage: https://github.com/giandifra/simple_rsa

Expand Down

0 comments on commit b7f7b35

Please sign in to comment.