@capgo/capacitor-updater 6.1.16 → 6.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipherV2.java +189 -0
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +1 -1
- package/ios/Plugin/CryptoCipherV2.swift +263 -0
- package/package.json +1 -1
|
@@ -56,7 +56,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
56
56
|
private static final String channelUrlDefault =
|
|
57
57
|
"https://api.capgo.app/channel_self";
|
|
58
58
|
|
|
59
|
-
private final String PLUGIN_VERSION = "6.1.
|
|
59
|
+
private final String PLUGIN_VERSION = "6.1.17";
|
|
60
60
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
61
61
|
|
|
62
62
|
private SharedPreferences.Editor editor;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
package ee.forgr.capacitor_updater;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Created by Awesometic
|
|
11
|
+
* It's encrypt returns Base64 encoded, and also decrypt for Base64 encoded cipher
|
|
12
|
+
* references: http://stackoverflow.com/questions/12471999/rsa-encryption-decryption-in-android
|
|
13
|
+
*/
|
|
14
|
+
import android.util.Base64;
|
|
15
|
+
import java.security.GeneralSecurityException;
|
|
16
|
+
import java.security.InvalidAlgorithmParameterException;
|
|
17
|
+
import java.security.InvalidKeyException;
|
|
18
|
+
import java.security.KeyFactory;
|
|
19
|
+
import java.security.NoSuchAlgorithmException;
|
|
20
|
+
import java.security.PublicKey;
|
|
21
|
+
import java.security.spec.InvalidKeySpecException;
|
|
22
|
+
import java.security.spec.X509EncodedKeySpec;
|
|
23
|
+
import javax.crypto.BadPaddingException;
|
|
24
|
+
import javax.crypto.Cipher;
|
|
25
|
+
import javax.crypto.IllegalBlockSizeException;
|
|
26
|
+
import javax.crypto.NoSuchPaddingException;
|
|
27
|
+
import javax.crypto.SecretKey;
|
|
28
|
+
import javax.crypto.spec.IvParameterSpec;
|
|
29
|
+
import javax.crypto.spec.PSource;
|
|
30
|
+
import javax.crypto.spec.SecretKeySpec;
|
|
31
|
+
|
|
32
|
+
public class CryptoCipherV2 {
|
|
33
|
+
|
|
34
|
+
public static byte[] decryptRSA(byte[] source, PublicKey publicKey)
|
|
35
|
+
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
|
36
|
+
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
|
37
|
+
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
|
38
|
+
byte[] decryptedBytes = cipher.doFinal(source);
|
|
39
|
+
return decryptedBytes;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public static byte[] decryptAES(byte[] cipherText, SecretKey key, byte[] iv) {
|
|
43
|
+
try {
|
|
44
|
+
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
|
45
|
+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
46
|
+
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
|
|
47
|
+
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
|
|
48
|
+
return cipher.doFinal(cipherText);
|
|
49
|
+
} catch (Exception e) {
|
|
50
|
+
e.printStackTrace();
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public static SecretKey byteToSessionKey(byte[] sessionKey) {
|
|
56
|
+
// rebuild key using SecretKeySpec
|
|
57
|
+
return new SecretKeySpec(sessionKey, 0, sessionKey.length, "AES");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private static PublicKey readX509PublicKey(byte[] x509Bytes)
|
|
61
|
+
throws GeneralSecurityException {
|
|
62
|
+
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
|
63
|
+
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(x509Bytes);
|
|
64
|
+
try {
|
|
65
|
+
return keyFactory.generatePublic(keySpec);
|
|
66
|
+
} catch (InvalidKeySpecException e) {
|
|
67
|
+
throw new IllegalArgumentException("Unexpected key format!", e);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public static PublicKey stringToPublicKey(String public_key)
|
|
72
|
+
throws GeneralSecurityException {
|
|
73
|
+
// Base64 decode the result
|
|
74
|
+
|
|
75
|
+
String pkcs1Pem = public_key.toString();
|
|
76
|
+
pkcs1Pem = pkcs1Pem.replace("-----BEGIN RSA PUBLIC KEY-----", "");
|
|
77
|
+
pkcs1Pem = pkcs1Pem.replace("-----END RSA PUBLIC KEY-----", "");
|
|
78
|
+
pkcs1Pem = pkcs1Pem.replace("\\n", "");
|
|
79
|
+
pkcs1Pem = pkcs1Pem.replace(" ", "");
|
|
80
|
+
|
|
81
|
+
byte[] pkcs1EncodedBytes = Base64.decode(pkcs1Pem, Base64.DEFAULT);
|
|
82
|
+
|
|
83
|
+
// extract the public key
|
|
84
|
+
return readPkcs1PublicKey(pkcs1EncodedBytes);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// since the public key is in pkcs1 format, we have to convert it to x509 format similar
|
|
88
|
+
// to what needs done with the private key converting to pkcs8 format
|
|
89
|
+
// so, the rest of the code below here is adapted from here https://stackoverflow.com/a/54246646
|
|
90
|
+
private static final int SEQUENCE_TAG = 0x30;
|
|
91
|
+
private static final int BIT_STRING_TAG = 0x03;
|
|
92
|
+
private static final byte[] NO_UNUSED_BITS = new byte[] { 0x00 };
|
|
93
|
+
private static final byte[] RSA_ALGORITHM_IDENTIFIER_SEQUENCE = {
|
|
94
|
+
(byte) 0x30,
|
|
95
|
+
(byte) 0x0d,
|
|
96
|
+
(byte) 0x06,
|
|
97
|
+
(byte) 0x09,
|
|
98
|
+
(byte) 0x2a,
|
|
99
|
+
(byte) 0x86,
|
|
100
|
+
(byte) 0x48,
|
|
101
|
+
(byte) 0x86,
|
|
102
|
+
(byte) 0xf7,
|
|
103
|
+
(byte) 0x0d,
|
|
104
|
+
(byte) 0x01,
|
|
105
|
+
(byte) 0x01,
|
|
106
|
+
(byte) 0x01,
|
|
107
|
+
(byte) 0x05,
|
|
108
|
+
(byte) 0x00,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
private static PublicKey readPkcs1PublicKey(byte[] pkcs1Bytes)
|
|
112
|
+
throws NoSuchAlgorithmException, InvalidKeySpecException, GeneralSecurityException {
|
|
113
|
+
// convert the pkcs1 public key to an x509 favorable format
|
|
114
|
+
byte[] keyBitString = createDEREncoding(
|
|
115
|
+
BIT_STRING_TAG,
|
|
116
|
+
joinPublic(NO_UNUSED_BITS, pkcs1Bytes)
|
|
117
|
+
);
|
|
118
|
+
byte[] keyInfoValue = joinPublic(
|
|
119
|
+
RSA_ALGORITHM_IDENTIFIER_SEQUENCE,
|
|
120
|
+
keyBitString
|
|
121
|
+
);
|
|
122
|
+
byte[] keyInfoSequence = createDEREncoding(SEQUENCE_TAG, keyInfoValue);
|
|
123
|
+
return readX509PublicKey(keyInfoSequence);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private static byte[] joinPublic(byte[]... bas) {
|
|
127
|
+
int len = 0;
|
|
128
|
+
for (int i = 0; i < bas.length; i++) {
|
|
129
|
+
len += bas[i].length;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
byte[] buf = new byte[len];
|
|
133
|
+
int off = 0;
|
|
134
|
+
for (int i = 0; i < bas.length; i++) {
|
|
135
|
+
System.arraycopy(bas[i], 0, buf, off, bas[i].length);
|
|
136
|
+
off += bas[i].length;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return buf;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private static byte[] createDEREncoding(int tag, byte[] value) {
|
|
143
|
+
if (tag < 0 || tag >= 0xFF) {
|
|
144
|
+
throw new IllegalArgumentException(
|
|
145
|
+
"Currently only single byte tags supported"
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
byte[] lengthEncoding = createDERLengthEncoding(value.length);
|
|
150
|
+
|
|
151
|
+
int size = 1 + lengthEncoding.length + value.length;
|
|
152
|
+
byte[] derEncodingBuf = new byte[size];
|
|
153
|
+
|
|
154
|
+
int off = 0;
|
|
155
|
+
derEncodingBuf[off++] = (byte) tag;
|
|
156
|
+
System.arraycopy(
|
|
157
|
+
lengthEncoding,
|
|
158
|
+
0,
|
|
159
|
+
derEncodingBuf,
|
|
160
|
+
off,
|
|
161
|
+
lengthEncoding.length
|
|
162
|
+
);
|
|
163
|
+
off += lengthEncoding.length;
|
|
164
|
+
System.arraycopy(value, 0, derEncodingBuf, off, value.length);
|
|
165
|
+
|
|
166
|
+
return derEncodingBuf;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private static byte[] createDERLengthEncoding(int size) {
|
|
170
|
+
if (size <= 0x7F) {
|
|
171
|
+
// single byte length encoding
|
|
172
|
+
return new byte[] { (byte) size };
|
|
173
|
+
} else if (size <= 0xFF) {
|
|
174
|
+
// double byte length encoding
|
|
175
|
+
return new byte[] { (byte) 0x81, (byte) size };
|
|
176
|
+
} else if (size <= 0xFFFF) {
|
|
177
|
+
// triple byte length encoding
|
|
178
|
+
return new byte[] {
|
|
179
|
+
(byte) 0x82,
|
|
180
|
+
(byte) (size >> Byte.SIZE),
|
|
181
|
+
(byte) size,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
throw new IllegalArgumentException(
|
|
186
|
+
"size too large, only up to 64KiB length encoding supported: " + size
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -16,7 +16,7 @@ import SwiftyRSA
|
|
|
16
16
|
@objc(CapacitorUpdaterPlugin)
|
|
17
17
|
public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
18
18
|
public var implementation = CapacitorUpdater()
|
|
19
|
-
private let PLUGIN_VERSION: String = "6.1.
|
|
19
|
+
private let PLUGIN_VERSION: String = "6.1.17"
|
|
20
20
|
static let updateUrlDefault = "https://api.capgo.app/updates"
|
|
21
21
|
static let statsUrlDefault = "https://api.capgo.app/stats"
|
|
22
22
|
static let channelUrlDefault = "https://api.capgo.app/channel_self"
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Foundation
|
|
8
|
+
import CommonCrypto
|
|
9
|
+
|
|
10
|
+
///
|
|
11
|
+
/// Constants
|
|
12
|
+
///
|
|
13
|
+
private enum CryptoCipherConstantsV2 {
|
|
14
|
+
static let rsaKeySizeInBits: NSNumber = 2048
|
|
15
|
+
static let aesAlgorithm: CCAlgorithm = CCAlgorithm(kCCAlgorithmAES)
|
|
16
|
+
static let aesOptions: CCOptions = CCOptions(kCCOptionPKCS7Padding)
|
|
17
|
+
static let rsaAlgorithm: SecKeyAlgorithm = .rsaEncryptionPKCS1
|
|
18
|
+
}
|
|
19
|
+
///
|
|
20
|
+
/// The AES key. Contains both the initialization vector and secret key.
|
|
21
|
+
///
|
|
22
|
+
public struct AES128KeyV2 {
|
|
23
|
+
/// Initialization vector
|
|
24
|
+
private let iv: Data
|
|
25
|
+
private let aes128Key: Data
|
|
26
|
+
#if DEBUG
|
|
27
|
+
public var __debug_iv: Data { iv }
|
|
28
|
+
public var __debug_aes128Key: Data { aes128Key }
|
|
29
|
+
#endif
|
|
30
|
+
init(iv: Data, aes128Key: Data) {
|
|
31
|
+
self.iv = iv
|
|
32
|
+
self.aes128Key = aes128Key
|
|
33
|
+
}
|
|
34
|
+
///
|
|
35
|
+
/// Takes the data and uses the private key to decrypt it. Will call `CCCrypt` in CommonCrypto
|
|
36
|
+
/// and provide it `ivData` for the initialization vector. Will use cipher block chaining (CBC) as
|
|
37
|
+
/// the mode of operation.
|
|
38
|
+
///
|
|
39
|
+
/// Returns the decrypted data.
|
|
40
|
+
///
|
|
41
|
+
public func decrypt(data: Data) -> Data? {
|
|
42
|
+
let encryptedData: UnsafePointer<UInt8> = (data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count)
|
|
43
|
+
let encryptedDataLength: Int = data.count
|
|
44
|
+
|
|
45
|
+
if let result: NSMutableData = NSMutableData(length: encryptedDataLength) {
|
|
46
|
+
let keyData: UnsafePointer<UInt8> = (self.aes128Key as NSData).bytes.bindMemory(to: UInt8.self, capacity: self.aes128Key.count)
|
|
47
|
+
let keyLength: size_t = size_t(self.aes128Key.count)
|
|
48
|
+
let ivData: UnsafePointer<UInt8> = (iv as NSData).bytes.bindMemory(to: UInt8.self, capacity: self.iv.count)
|
|
49
|
+
|
|
50
|
+
let decryptedData: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>(result.mutableBytes.assumingMemoryBound(to: UInt8.self))
|
|
51
|
+
let decryptedDataLength: size_t = size_t(result.length)
|
|
52
|
+
|
|
53
|
+
var decryptedLength: size_t = 0
|
|
54
|
+
|
|
55
|
+
let status: CCCryptorStatus = CCCrypt(CCOperation(kCCDecrypt), CryptoCipherConstants.aesAlgorithm, CryptoCipherConstants.aesOptions, keyData, keyLength, ivData, encryptedData, encryptedDataLength, decryptedData, decryptedDataLength, &decryptedLength)
|
|
56
|
+
|
|
57
|
+
if Int32(status) == Int32(kCCSuccess) {
|
|
58
|
+
result.length = Int(decryptedLength)
|
|
59
|
+
return result as Data
|
|
60
|
+
} else {
|
|
61
|
+
return nil
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
return nil
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
///
|
|
70
|
+
/// The RSA keypair. Includes both private and public key.
|
|
71
|
+
///
|
|
72
|
+
public struct RSAKeyPairV2 {
|
|
73
|
+
private let privateKey: SecKey
|
|
74
|
+
private let publicKey: SecKey
|
|
75
|
+
|
|
76
|
+
#if DEBUG
|
|
77
|
+
public var __debug_privateKey: SecKey { self.privateKey }
|
|
78
|
+
public var __debug_publicKey: SecKey { self.publicKey }
|
|
79
|
+
#endif
|
|
80
|
+
|
|
81
|
+
fileprivate init(privateKey: SecKey, publicKey: SecKey) {
|
|
82
|
+
self.privateKey = privateKey
|
|
83
|
+
self.publicKey = publicKey
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public func extractPublicKey() -> RSAPublicKey {
|
|
87
|
+
RSAPublicKey(publicKey: publicKey)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
///
|
|
91
|
+
/// Takes the data and uses the private key to decrypt it.
|
|
92
|
+
/// Returns the decrypted data.
|
|
93
|
+
///
|
|
94
|
+
public func decrypt(data: Data) -> Data? {
|
|
95
|
+
var error: Unmanaged<CFError>?
|
|
96
|
+
if let decryptedData: CFData = SecKeyCreateDecryptedData(self.privateKey, CryptoCipherConstants.rsaAlgorithm, data as CFData, &error) {
|
|
97
|
+
if error != nil {
|
|
98
|
+
return nil
|
|
99
|
+
} else {
|
|
100
|
+
return decryptedData as Data
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
return nil
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
///
|
|
109
|
+
/// The RSA public key.
|
|
110
|
+
///
|
|
111
|
+
public struct RSAPublicKeyV2 {
|
|
112
|
+
private let publicKey: SecKey
|
|
113
|
+
|
|
114
|
+
#if DEBUG
|
|
115
|
+
public var __debug_publicKey: SecKey { self.publicKey }
|
|
116
|
+
#endif
|
|
117
|
+
|
|
118
|
+
fileprivate init(publicKey: SecKey) {
|
|
119
|
+
self.publicKey = publicKey
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
///
|
|
123
|
+
/// Takes the data and uses the public key to decrypt it.
|
|
124
|
+
/// Returns the decrypted data.
|
|
125
|
+
///
|
|
126
|
+
public func decrypt(data: Data) -> Data? {
|
|
127
|
+
do {
|
|
128
|
+
guard let decryptedData = RSAPublicKey.decryptWithRSAKey(data, rsaKeyRef: self.publicKey, padding: SecPadding()) else {
|
|
129
|
+
throw CustomError.cannotDecryptSessionKey
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return decryptedData
|
|
133
|
+
} catch {
|
|
134
|
+
print("Error decrypting data: \(error)")
|
|
135
|
+
return nil
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
///
|
|
140
|
+
/// Allows you to export the RSA public key to a format (so you can send over the net).
|
|
141
|
+
///
|
|
142
|
+
public func export() -> Data? {
|
|
143
|
+
return publicKey.exportToData()
|
|
144
|
+
}
|
|
145
|
+
//
|
|
146
|
+
|
|
147
|
+
///
|
|
148
|
+
/// Allows you to load an RSA public key (i.e. one downloaded from the net).
|
|
149
|
+
///
|
|
150
|
+
public static func load(rsaPublicKey: String) -> RSAPublicKey? {
|
|
151
|
+
var pubKey: String = rsaPublicKey
|
|
152
|
+
pubKey = pubKey.replacingOccurrences(of: "-----BEGIN RSA PUBLIC KEY-----", with: "")
|
|
153
|
+
pubKey = pubKey.replacingOccurrences(of: "-----END RSA PUBLIC KEY-----", with: "")
|
|
154
|
+
pubKey = pubKey.replacingOccurrences(of: "\\n+", with: "", options: .regularExpression)
|
|
155
|
+
pubKey = pubKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
156
|
+
do {
|
|
157
|
+
guard let rsaPublicKeyData: Data = Data(base64Encoded: String(pubKey)) else {
|
|
158
|
+
throw CustomError.cannotDecode
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
guard let publicKey: SecKey = .loadPublicFromData(rsaPublicKeyData) else {
|
|
162
|
+
throw CustomError.cannotDecode
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return RSAPublicKey(publicKey: publicKey)
|
|
166
|
+
} catch {
|
|
167
|
+
print("Error load RSA: \(error)")
|
|
168
|
+
return nil
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// code is copied from here: https://github.com/btnguyen2k/swiftutils/blob/88494f4c635b6c6d42ef0fb30a7d666acd38c4fa/SwiftUtils/RSAUtils.swift#L393
|
|
173
|
+
public static func decryptWithRSAKey(_ encryptedData: Data, rsaKeyRef: SecKey, padding: SecPadding) -> Data? {
|
|
174
|
+
let blockSize = SecKeyGetBlockSize(rsaKeyRef)
|
|
175
|
+
let dataSize = encryptedData.count / MemoryLayout<UInt8>.size
|
|
176
|
+
|
|
177
|
+
var encryptedDataAsArray = [UInt8](repeating: 0, count: dataSize)
|
|
178
|
+
(encryptedData as NSData).getBytes(&encryptedDataAsArray, length: dataSize)
|
|
179
|
+
|
|
180
|
+
var decryptedData = [UInt8](repeating: 0, count: 0)
|
|
181
|
+
var idx = 0
|
|
182
|
+
while idx < encryptedDataAsArray.count {
|
|
183
|
+
var idxEnd = idx + blockSize
|
|
184
|
+
if idxEnd > encryptedDataAsArray.count {
|
|
185
|
+
idxEnd = encryptedDataAsArray.count
|
|
186
|
+
}
|
|
187
|
+
var chunkData = [UInt8](repeating: 0, count: blockSize)
|
|
188
|
+
for i in idx..<idxEnd {
|
|
189
|
+
chunkData[i-idx] = encryptedDataAsArray[i]
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
var decryptedDataBuffer = [UInt8](repeating: 0, count: blockSize)
|
|
193
|
+
var decryptedDataLength = blockSize
|
|
194
|
+
|
|
195
|
+
let status = SecKeyDecrypt(rsaKeyRef, padding, chunkData, idxEnd-idx, &decryptedDataBuffer, &decryptedDataLength)
|
|
196
|
+
if status != noErr {
|
|
197
|
+
return nil
|
|
198
|
+
}
|
|
199
|
+
let finalData = removePadding(decryptedDataBuffer)
|
|
200
|
+
decryptedData += finalData
|
|
201
|
+
|
|
202
|
+
idx += blockSize
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return Data(decryptedData)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// code is copied from here: https://github.com/btnguyen2k/swiftutils/blob/88494f4c635b6c6d42ef0fb30a7d666acd38c4fa/SwiftUtils/RSAUtils.swift#L429
|
|
209
|
+
private static func removePadding(_ data: [UInt8]) -> [UInt8] {
|
|
210
|
+
var idxFirstZero = -1
|
|
211
|
+
var idxNextZero = data.count
|
|
212
|
+
for i in 0..<data.count {
|
|
213
|
+
if data[i] == 0 {
|
|
214
|
+
if idxFirstZero < 0 {
|
|
215
|
+
idxFirstZero = i
|
|
216
|
+
} else {
|
|
217
|
+
idxNextZero = i
|
|
218
|
+
break
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if idxNextZero-idxFirstZero-1 == 0 {
|
|
223
|
+
idxNextZero = idxFirstZero
|
|
224
|
+
idxFirstZero = -1
|
|
225
|
+
}
|
|
226
|
+
var newData = [UInt8](repeating: 0, count: idxNextZero-idxFirstZero-1)
|
|
227
|
+
for i in idxFirstZero+1..<idxNextZero {
|
|
228
|
+
newData[i-idxFirstZero-1] = data[i]
|
|
229
|
+
}
|
|
230
|
+
return newData
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
fileprivate extension SecKeyV2 {
|
|
235
|
+
func exportToData() -> Data? {
|
|
236
|
+
var error: Unmanaged<CFError>?
|
|
237
|
+
if let cfData: CFData = SecKeyCopyExternalRepresentation(self, &error) {
|
|
238
|
+
if error != nil {
|
|
239
|
+
return nil
|
|
240
|
+
} else {
|
|
241
|
+
return cfData as Data
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
return nil
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
static func loadPublicFromData(_ data: Data) -> SecKey? {
|
|
248
|
+
let keyDict: [NSObject: NSObject] = [
|
|
249
|
+
kSecAttrKeyType: kSecAttrKeyTypeRSA,
|
|
250
|
+
kSecAttrKeyClass: kSecAttrKeyClassPublic,
|
|
251
|
+
kSecAttrKeySizeInBits: CryptoCipherConstants.rsaKeySizeInBits
|
|
252
|
+
]
|
|
253
|
+
return SecKeyCreateWithData(data as NSData, keyDict as CFDictionary, nil)
|
|
254
|
+
}
|
|
255
|
+
static func loadPrivateFromData(_ data: Data) -> SecKey? {
|
|
256
|
+
let keyDict: [NSObject: NSObject] = [
|
|
257
|
+
kSecAttrKeyType: kSecAttrKeyTypeRSA,
|
|
258
|
+
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
|
259
|
+
kSecAttrKeySizeInBits: CryptoCipherConstants.rsaKeySizeInBits
|
|
260
|
+
]
|
|
261
|
+
return SecKeyCreateWithData(data as CFData, keyDict as CFDictionary, nil)
|
|
262
|
+
}
|
|
263
|
+
}
|