算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。
一个算法的优劣可以用空间复杂度与时间复杂度来衡量。空间复杂度 (Space Complexity) 是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。比如直接插入排序的时间复杂度是O(n^2),空间复杂度是O(1) 。而一般的递归算法就要有O(n)的空间复杂度,因为每次递归都要存储返回信息。一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量。在计算机科学中,时间复杂性,又称时间复杂度,算法时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
算法中的指令描述的是一个计算,当其运行时能从一个初始状态和(可能为空的)初始输入开始,经过一系列有限而清晰定义的状态,最终产生输出并停止于一个终态。一个状态到另一个状态的转移不一定是确定的。随机化算法在内的一些算法,包含了一些随机输入。
在了解了算法概念后,想必对算法有了初识。在这里小编就来讲讲 Flutter 开发常用哪些加密算法。
首先,推荐学习项目 dart_crypto,它集成了 Base64, 32/16 Bits MD5, AES, RSA 等算法。下面讲解一下各算法的实现与使用。
Base64
- Base64 Algorithm
/// Converts a string to base64.
static yf_base64Encode(String string) {
if (string == null) throw new ArgumentError("The argument is null");
// get a base64 encoder instance.
var encoder = new Base64Encoder();
// utf8 encoding.
var list = utf8.encode(string);
// encode a string to Base64.
var encodedString = encoder.convert(list);
return encodedString;
}
/// Converts a base64 encoded string to a string or a `Uint8List`.
static yf_base64Decode(String encodedString, {bool createUint8List = false}) {
if (encodedString == null) throw new ArgumentError("encodedString is null");
// get a base64 decoder instance.
var decoder = Base64Decoder();
// decode a base64 encoded string to a List of bytes.
var bytes = decoder.convert(encodedString);
if (createUint8List) {
return createUint8ListFromList(bytes);
}
var output = utf8.decode(bytes);
return output;
}
/// Converts a List of bytes to a base64 encoded string.
static base64EncodeList(List<int> bytes) {
if (bytes == null) throw new ArgumentError("The list is null");
// get a base64 encoder instance
var encoder = new Base64Encoder();
// encode a List of bytes - use line breaks
var out = encoder.convert(bytes);
return out;
}
/// Converts a base64 encoded List of bytes to a string or a `Uint8List`.
static base64DecodeList(List<int> bytes, {bool createUint8List = false}) {
if (bytes == null) throw new ArgumentError("The list is null");
// get a base64 decoder instance
var decoder = Base64Decoder();
var input = new String.fromCharCodes(bytes);
// decode a Base64 encoded list
var result = decoder.convert(input);
if (createUint8List) {
return createUint8ListFromList(result);
}
var output = utf8.decode(result);
return output;
}
- Base64 Usage
try {
// Base64 - Encode
final base64Encoded = crypto.DYFCryptoProvider.yf_base64Encode(plainText);
print("[Base64] Encode: " + base64Encoded);
// Base64 - Decode
final base64Decoded = crypto.DYFCryptoProvider.yf_base64Decode(base64Encoded);
print("[Base64] Decode: " + base64Decoded);
} catch (e) {
print("e: $e");
}
MD5
- MD5 Algorithm
/// Creates a hash value with md5.
static md5Encode(String input) {
if (input == null) throw new ArgumentError("The input is null");
var bytes = utf8.encode(input); // data being hashed
var digest = md5.convert(bytes);
return digest.toString();
}
/// Creates a 16 bits hash value with md5.
static bit16md5Enconde(String input) {
var hash = md5Encode(input);
return hash.substring(8, 24);
}
- MD5 Usage
try {
// MD5 - 32 Bits Encode
final md5Hash = crypto.DYFCryptoProvider.md5Encode(plainText);
print("[MD5] Hash: " + md5Hash);
// MD5 - 16 Bits Encode
final md5b16hash = crypto.DYFCryptoProvider.bit16md5Enconde(plainText);
print("[MD5] 16 Bits Hash: " + md5b16hash);
} catch (e) {
print("e: $e");
}
AES
- AES Key
/// Private. The length for AES key is 128 bits, 192 bits, 256 bits.
static Uint8List _getAESKey(String key, int blockSize) {
var keyData = createUint8ListFromList(utf8.encode(key));
var length = blockSize ~/ 8;
var output = createUint8ListFromList(List.generate(length, (i) => 0));
int count = Math.min(keyData.lengthInBytes, output.lengthInBytes);
for (var i = 0; i < count; i++) {
output[i] = keyData[i];
}
return output;
}
- AES Algorithm
/// Returns a cipher text with `AES` algorithm.
/// The length for AES key is 128 bits, 192 bits, 256 bits.
static aesEncrypt(String message, String key, {int blockSize: 128}) {
if (message == null || key == null)
throw new ArgumentError("message or key is null");
var keyData = _getAESKey(key, blockSize);
var aes = AES.fromBytes(keyData);
var ciphertext = aes.encode(message);
return ciphertext;
}
/// Returns a decoded text with `AES` algorithm.
/// The length for AES key is 128 or 192 or 256 bits.
static aesDecrypt(String ciphertext, String key, {int blockSize: 128}) {
if (ciphertext == null || key == null)
throw new ArgumentError("ciphertext or key is null");
var keyData = _getAESKey(key, blockSize);
var aes = AES.fromBytes(keyData);
var decryptedText = aes.decode(ciphertext);
return decryptedText;
}
- AES Usage
try {
// AES - Key
final aesKey = "smMQI8dMK2nOMUR0TdpBYQUnLpbW8kjHrdy86WtU6eB1Ff6mYveYzezopmbjwBZEjPQmg";
print("[AES] Key: " + aesKey);
// AES - Encrypt
String aesEncryptedText = crypto.DYFCryptoProvider.aesEncrypt(plainText, aesKey);
print("[AES] Encrypted Text: " + aesEncryptedText);
// AES - Decrypt
String aesDecryptedText = crypto.DYFCryptoProvider.aesDecrypt(aesEncryptedText, aesKey);
print("[AES] Decrypted Text: " + aesDecryptedText);
} catch (e) {
print("e: $e");
}
RSA
- RSA Key Formatter
// Converts the key to the content of pem for RSA
class RSAKeyFormatter {
static formatRSAPublicKey(String publicKey) {
if (publicKey == null) return null;
var buffer = new StringBuffer();
buffer.write("-----BEGIN PUBLIC KEY-----\n");
final length = publicKey.length;
int count = 0;
for (var i = 0; i < length; i++) {
var c = publicKey.codeUnitAt(i);
var s = String.fromCharCode(c);
if (s == "\n" || s == "\r") {
continue;
}
buffer.writeCharCode(c);
if (++count == 64) {
buffer.write("\n");
count = 0;
}
}
buffer.write("\n-----END PUBLIC KEY-----\n");
return buffer.toString();
}
static formatRSAPrivateKey(String privateKey) {
if (privateKey == null) return null;
var buffer = new StringBuffer();
buffer.write("-----BEGIN PRIVATE KEY-----\n");
final length = privateKey.length;
int count = 0;
for (var i = 0; i < length; i++) {
var c = privateKey.codeUnitAt(i);
var s = String.fromCharCode(c);
if (s == "\n" || s == "\r") {
continue;
}
buffer.writeCharCode(c);
if (++count == 64) {
buffer.write("\n");
count = 0;
}
}
buffer.write("\n-----END PRIVATE KEY-----\n");
return buffer.toString();
}
}
- RSA Block
// Calculates the number of block processing times.
class RSABlock {
final data;
final int size;
RSABlock(this.data, this.size);
get source => data;
get blockSize => size;
get blockCount {
int dataLength = data.length;
var result = dataLength / size;
var count = dataLength ~/ size;
if (result > count) {
count += 1;
}
return count;
}
}
- RSA Algorithm
/// Returns a cipher text with `RSA` algorithm.
static rsaEncrypt(String message, String publicKey) {
if (message == null || publicKey == null)
throw new ArgumentError("message or publicKey is null");
String pubKey = RSAKeyFormatter.formatRSAPublicKey(publicKey);
KeyPair pair = KeyPair.parsePem(pubKey);
int blockSize = pair.bytesize - 11;
var builder = BytesBuilder();
var data = utf8.encode(message);
var rb = RSABlock(data, blockSize);
int count = rb.blockCount;
for (var i = 0; i < count; i++) {
int dataLength = data.length;
int start = i * blockSize;
int bufferSize = Math.min(blockSize, dataLength - start);
int end = start + bufferSize;
var subdata = data.sublist(start, end);
var bytes = pair.encrypt(subdata);
builder.add(bytes);
}
var ciphertext = base64Encode(builder.toBytes());
return ciphertext;
}
/// Returns a decoded text with `RSA` algorithm.
static rsaDecrypt(String ciphertext, String privateKey) {
if (ciphertext == null || privateKey == null)
throw new ArgumentError("ciphertext or privateKey is null");
String privKey = RSAKeyFormatter.formatRSAPrivateKey(privateKey);
KeyPair pair = KeyPair.parsePem(privKey);
int blockSize = pair.bytesize;
var builder = BytesBuilder();
var data = base64Decode(ciphertext);
var rb = RSABlock(data, blockSize);
int count = rb.blockCount;
for (var i = 0; i < count; i++) {
int dataLength = data.length;
int start = i * blockSize;
int bufferSize = Math.min(blockSize, dataLength - start);
int end = start + bufferSize;
var subdata = data.sublist(start, end);
var bytes = pair.decrypt(subdata);
builder.add(bytes);
}
var decryptedText = utf8.decode(builder.toBytes());
return decryptedText;
}
/// Returns a signature with `RSA` algorithm.
static rsaSign(String message, String privateKey) {
if (message == null || privateKey == null)
throw new ArgumentError("message or privateKey is null");
String privKey = RSAKeyFormatter.formatRSAPrivateKey(privateKey);
KeyPair pair = KeyPair.parsePem(privKey);
var msgBytes = createUint8ListFromList(utf8.encode(message));
var signBytes = pair.sign(msgBytes);
var sign = base64Encode(signBytes);
return sign;
}
/// Verifies a signature with `RSA` algorithm.
/// If true, the signature is correct, otherwise, signing failed.
static rsaVerify(String signature, String message, String publicKey) {
if (signature == null || message == null || publicKey == null)
throw new ArgumentError("signature, message or publicKey is null");
String pubKey = RSAKeyFormatter.formatRSAPublicKey(publicKey);
KeyPair pair = KeyPair.parsePem(pubKey);
var signBytes = base64Decode(signature);
var msgBytes = createUint8ListFromList(utf8.encode(message));
bool ret = pair.verify(signBytes, msgBytes);
return ret;
}
- RSA Usage
// 公钥
final publicKey =
"""MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmPW2SwJFldGVB1SM82VYvSZYR
F1H5DREUiDK2SLnksxHAV/roC1uB44a4siUehJ9AKeV/g58pVrjhX3eSiBh9Khom
/S2hEWF2n/6+lqqiwQi1W5rjl86v+dI2F6NgbPFpfesrRjWD9uskT2VX/ZJuMRLz
8VPIyQOM9TW3PkMYBQIDAQAB""";
// 私钥 (pkcs8)
final privateKey =
"""MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKY9bZLAkWV0ZUHV
IzzZVi9JlhEXUfkNERSIMrZIueSzEcBX+ugLW4HjhriyJR6En0Ap5X+DnylWuOFf
d5KIGH0qGib9LaERYXaf/r6WqqLBCLVbmuOXzq/50jYXo2Bs8Wl96ytGNYP26yRP
ZVf9km4xEvPxU8jJA4z1Nbc+QxgFAgMBAAECgYArZVW5PXO3HE9ihBUSyVlqNrdp
9sB7VyHiTjuOwiVkwiocH9trv6s/mPmONVLjSJOZ2FYEl4Nw8yaIDrfUFJrvhdbh
HJnwkO27Wo5jEfm2qGCwgQNtUACoIH637LXfP81v5I7eZtEa7kfO8Axpp3czvO1H
dIAlOI8rU4jb3fB1cQJBANLgfHd/CDro1gtvTrUeTw/lqsKVScGiHn+pmT+THed6
ftJ2MAJVcL/0H8+fFN5mRypCL7LQyPO48dTmfY9PbocCQQDJz8xZGq2BNAd3gSrN
i3q++SEyjRPzDfr8AGJBJF8qtslcSYrVB/jjPx/qNNlMxOoXnpozBojzVTO3UirM
J/wTAkEAzb930YOhPREGHnwImFCtJT6ZYGcWYpXSGg8Y1d2tlLeA28myx+QjMTZ4
fzOgwemaz9FqBpcNKjctxOLqaRRAKwJAXPZwznbgh8zcx6rjea2PjFscdLnR/7tn
6x+OIy3K/NUYan+iCUHT33JblDpmAtwObXTs2SZgfZ645PBfsI2WqwJAGJxnG8+w
iCnzN0CIZvG96tfOZmz0lkM4NSHDwdCSbagJlZccOtodpn00Dzy+l0t+oFe0Xm3R
A0WkPzQX/seO0Q==""";
try {
// RSA - Encrypt
String rsaEncryptedText = crypto.DYFCryptoProvider.rsaEncrypt(plainText, publicKey);
print("[RSA] Encrypted Text: " + rsaEncryptedText);
// RSA - Decrypt
String rsaDecryptedText = crypto.DYFCryptoProvider.rsaDecrypt(rsaEncryptedText, privateKey);
print("[RSA] Decrypted Text: " + rsaDecryptedText);
// RSA - Sign
String signature = crypto.DYFCryptoProvider.rsaSign(plainText, privateKey);
print("[RSA] Signature: " + signature);
// RSA - Verify
bool ret = crypto.DYFCryptoProvider.rsaVerify(signature, plainText, publicKey);
print("[RSA] Signature Verification: " + ret.toString());
} catch (e) {
print("e: $e");
}
Sample
import 'dart:convert';
import 'dart:math' show Random;
import './crypto/crypto_provider.dart' as crypto;
// Updated and unreal keys.
class KeyConstants {
static final kComm = "##@...#FiQKBgQCmPW2SwJFl}";
static final kPublic =
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmPW2SwJFldGVB1SM82VYvSZYR...8VPIyQOM9TW3PkMYBQIDAQAB";
static final kPrivate =
"MIICXAIBAAKBgQCmPW2SwJFldGVB1SM82VYvSZYRF1H5DREUiDK2SLnksxHAV/ro...CZWXHDraHaZ9NA88vpdLfqBXtF5t0QNFpD80F/7HjtE=";
}
class RandomObjectGenerator {
int genRandomNumber() {
var max = 1 << 32;
return Random().nextInt(max);
}
String genRandomString({int length: 20}) {
var buffer = StringBuffer();
for (var i = 0; i < length; i++) {
int r = new Random().nextInt(2);
var s = (r == 1 ? "A" : "a");
int start = s.codeUnitAt(0);
int c = start + Random().nextInt(26);
buffer.writeCharCode(c);
}
return buffer.toString();
}
}
abstract class BaseStringUtils {
String urlEncode(String s);
String urlDecode(String s);
String apiEncode(String s);
String apiDecode(String s);
}
class StringUtils extends RandomObjectGenerator implements BaseStringUtils {
@override
String genRandomString({int length: 20}) => super.genRandomString(length: length);
String urlEncode(String s) {
return Uri.encodeQueryComponent(s);
}
String urlDecode(String s) {
return Uri.decodeQueryComponent(s);
}
String apiEncode(String s) {
if (s == null) throw ArgumentError("The input is null");
if (s.isEmpty) return s;
try {
String randomKey = genRandomString();
print("randomKey: $randomKey");
String middleKey = randomKey + KeyConstants.kComm;
print("middleKey: $middleKey");
String realKey = crypto.DYFCryptoProvider.bit16md5Enconde(middleKey);
String mParam = crypto.DYFCryptoProvider.aesEncrypt(s, realKey);
var middleMap = Map();
middleMap["p"] = mParam;
middleMap["k"] = randomKey;
var jp = json.encode(middleMap);
print("jp: $jp");
String ciphertext = crypto.DYFCryptoProvider.rsaEncrypt(jp, KeyConstants.kPublic);
print("ciphertext: $ciphertext");
return ciphertext;
} catch (e) {
print("e: $e");
}
return null;
}
String apiDecode(String s) {
if (s == null) throw ArgumentError("The input is null");
if (s.isEmpty) return s;
try {
String data = crypto.DYFCryptoProvider.rsaDecrypt(s, KeyConstants.kPrivate);
var map = json.decode(data);
var mParam = map["p"];
var randomKey = map["k"];
print("randomKey: $randomKey");
String middleKey = randomKey + KeyConstants.kComm;
print("middleKey: $middleKey");
String realKey = crypto.DYFCryptoProvider.bit16md5Enconde(middleKey);
String decodedText = crypto.DYFCryptoProvider.aesDecrypt(mParam, realKey);
return decodedText;
} catch (e) {
print("e: $e");
}
return null;
}
}
总结:写的有不好的地方希望大家指出,我会更正,大家有什么看不明白的,也可以在评论里面提问,我会尽力解答。
点赞+关注,第一时间获取技术干货和最新知识点,谢谢你的支持!转发请注明出处。
最后祝大家生活愉快~
网友评论