react-native-security-suite 0.9.22 → 1.0.0-rc.1
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/README.md +235 -69
- package/android/build.gradle +11 -0
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/securitysuite/CryptoConfig.java +158 -0
- package/android/src/main/java/com/securitysuite/CryptoUtils.java +152 -0
- package/android/src/main/java/com/securitysuite/EcdhKeyStore.java +60 -0
- package/android/src/main/java/com/securitysuite/HeaderSanitizer.java +75 -0
- package/android/src/main/java/com/securitysuite/JWSGenerator.java +237 -32
- package/android/src/main/java/com/securitysuite/JwsFetchPayload.java +81 -0
- package/android/src/main/java/com/securitysuite/Obfuscation.java +57 -0
- package/android/src/main/java/com/securitysuite/SecureStorageNative.java +211 -0
- package/android/src/main/java/com/securitysuite/SecureView.java +2 -10
- package/android/src/main/java/com/securitysuite/SecureWindowHelper.java +30 -0
- package/android/src/main/java/com/securitysuite/SecuritySuiteModule.java +310 -102
- package/android/src/main/java/com/securitysuite/Sslpinning.java +219 -106
- package/android/src/main/java/com/securitysuite/security/AppIntegrityChecker.java +133 -0
- package/android/src/main/java/com/securitysuite/security/EmulatorDetector.java +145 -0
- package/android/src/main/java/com/securitysuite/security/RuntimeDetector.java +234 -0
- package/android/src/test/java/com/securitysuite/JWSGeneratorTest.java +153 -0
- package/android/src/test/java/com/securitysuite/SecureStorageNativeTest.java +37 -0
- package/ios/CryptoConfig.swift +124 -0
- package/ios/JWSGenerator.swift +288 -0
- package/ios/JWSGeneratorTests.swift +168 -0
- package/ios/KeychainHelper.swift +104 -0
- package/ios/Obfuscation.swift +42 -0
- package/ios/SecureStorageNative.swift +84 -0
- package/ios/Security/AppIntegrityChecker.swift +85 -0
- package/ios/Security/EmulatorDetector.swift +45 -0
- package/ios/Security/RuntimeDetector.swift +107 -0
- package/ios/SecuritySuite.mm +28 -4
- package/ios/SecuritySuite.swift +407 -131
- package/ios/SslPinning.swift +242 -263
- package/lib/commonjs/clipboard/index.js +3 -0
- package/lib/commonjs/clipboard/index.js.map +1 -0
- package/lib/commonjs/crypto/index.js +39 -0
- package/lib/commonjs/crypto/index.js.map +1 -0
- package/lib/commonjs/device/index.js +40 -0
- package/lib/commonjs/device/index.js.map +1 -0
- package/lib/commonjs/errors.js +62 -0
- package/lib/commonjs/errors.js.map +1 -0
- package/lib/commonjs/index.js +220 -151
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/integrity/index.js +40 -0
- package/lib/commonjs/integrity/index.js.map +1 -0
- package/lib/commonjs/jws.js +141 -0
- package/lib/commonjs/jws.js.map +1 -0
- package/lib/commonjs/legacy/cryptoOptions.js +20 -0
- package/lib/commonjs/legacy/cryptoOptions.js.map +1 -0
- package/lib/commonjs/native/bridge.js +23 -0
- package/lib/commonjs/native/bridge.js.map +1 -0
- package/lib/commonjs/network/index.js +3 -0
- package/lib/commonjs/network/index.js.map +1 -0
- package/lib/commonjs/risk/score.js +36 -0
- package/lib/commonjs/risk/score.js.map +1 -0
- package/lib/commonjs/runtime/index.js +31 -0
- package/lib/commonjs/runtime/index.js.map +1 -0
- package/lib/commonjs/screen/index.js +13 -0
- package/lib/commonjs/screen/index.js.map +1 -0
- package/lib/commonjs/securitySuite/index.js +42 -0
- package/lib/commonjs/securitySuite/index.js.map +1 -0
- package/lib/commonjs/storage/index.js +3 -0
- package/lib/commonjs/storage/index.js.map +1 -0
- package/lib/commonjs/types/detection.js +2 -0
- package/lib/commonjs/types/detection.js.map +1 -0
- package/lib/module/clipboard/index.js +3 -0
- package/lib/module/clipboard/index.js.map +1 -0
- package/lib/module/crypto/index.js +35 -0
- package/lib/module/crypto/index.js.map +1 -0
- package/lib/module/device/index.js +36 -0
- package/lib/module/device/index.js.map +1 -0
- package/lib/module/errors.js +55 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/index.js +147 -148
- package/lib/module/index.js.map +1 -1
- package/lib/module/integrity/index.js +36 -0
- package/lib/module/integrity/index.js.map +1 -0
- package/lib/module/jws.js +127 -0
- package/lib/module/jws.js.map +1 -0
- package/lib/module/legacy/cryptoOptions.js +16 -0
- package/lib/module/legacy/cryptoOptions.js.map +1 -0
- package/lib/module/native/bridge.js +19 -0
- package/lib/module/native/bridge.js.map +1 -0
- package/lib/module/network/index.js +3 -0
- package/lib/module/network/index.js.map +1 -0
- package/lib/module/risk/score.js +32 -0
- package/lib/module/risk/score.js.map +1 -0
- package/lib/module/runtime/index.js +27 -0
- package/lib/module/runtime/index.js.map +1 -0
- package/lib/module/screen/index.js +5 -0
- package/lib/module/screen/index.js.map +1 -0
- package/lib/module/securitySuite/index.js +38 -0
- package/lib/module/securitySuite/index.js.map +1 -0
- package/lib/module/storage/index.js +3 -0
- package/lib/module/storage/index.js.map +1 -0
- package/lib/module/types/detection.js +2 -0
- package/lib/module/types/detection.js.map +1 -0
- package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts +215 -0
- package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/SecureView.d.ts +1 -1
- package/lib/typescript/commonjs/src/SecureView.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/clipboard/index.d.ts +2 -0
- package/lib/typescript/commonjs/src/clipboard/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/crypto/index.d.ts +15 -0
- package/lib/typescript/commonjs/src/crypto/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/device/index.d.ts +11 -0
- package/lib/typescript/commonjs/src/device/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/errors.d.ts +17 -0
- package/lib/typescript/commonjs/src/errors.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/helpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +77 -24
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/integrity/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/integrity/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/jws.d.ts +44 -0
- package/lib/typescript/commonjs/src/jws.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts +35 -0
- package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/native/bridge.d.ts +12 -0
- package/lib/typescript/commonjs/src/native/bridge.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/network/index.d.ts +2 -0
- package/lib/typescript/commonjs/src/network/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/risk/score.d.ts +12 -0
- package/lib/typescript/commonjs/src/risk/score.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/runtime/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/runtime/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/screen/index.d.ts +3 -0
- package/lib/typescript/commonjs/src/screen/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/securitySuite/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/securitySuite/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/storage/index.d.ts +2 -0
- package/lib/typescript/commonjs/src/storage/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/types/detection.d.ts +41 -0
- package/lib/typescript/commonjs/src/types/detection.d.ts.map +1 -0
- package/lib/typescript/module/docs/api-v1-proposal.d.ts +215 -0
- package/lib/typescript/module/docs/api-v1-proposal.d.ts.map +1 -0
- package/lib/typescript/module/src/SecureView.d.ts +1 -1
- package/lib/typescript/module/src/SecureView.d.ts.map +1 -1
- package/lib/typescript/module/src/clipboard/index.d.ts +2 -0
- package/lib/typescript/module/src/clipboard/index.d.ts.map +1 -0
- package/lib/typescript/module/src/crypto/index.d.ts +15 -0
- package/lib/typescript/module/src/crypto/index.d.ts.map +1 -0
- package/lib/typescript/module/src/device/index.d.ts +11 -0
- package/lib/typescript/module/src/device/index.d.ts.map +1 -0
- package/lib/typescript/module/src/errors.d.ts +17 -0
- package/lib/typescript/module/src/errors.d.ts.map +1 -0
- package/lib/typescript/module/src/helpers.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +77 -24
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/integrity/index.d.ts +6 -0
- package/lib/typescript/module/src/integrity/index.d.ts.map +1 -0
- package/lib/typescript/module/src/jws.d.ts +44 -0
- package/lib/typescript/module/src/jws.d.ts.map +1 -0
- package/lib/typescript/module/src/legacy/cryptoOptions.d.ts +35 -0
- package/lib/typescript/module/src/legacy/cryptoOptions.d.ts.map +1 -0
- package/lib/typescript/module/src/native/bridge.d.ts +12 -0
- package/lib/typescript/module/src/native/bridge.d.ts.map +1 -0
- package/lib/typescript/module/src/network/index.d.ts +2 -0
- package/lib/typescript/module/src/network/index.d.ts.map +1 -0
- package/lib/typescript/module/src/risk/score.d.ts +12 -0
- package/lib/typescript/module/src/risk/score.d.ts.map +1 -0
- package/lib/typescript/module/src/runtime/index.d.ts +6 -0
- package/lib/typescript/module/src/runtime/index.d.ts.map +1 -0
- package/lib/typescript/module/src/screen/index.d.ts +3 -0
- package/lib/typescript/module/src/screen/index.d.ts.map +1 -0
- package/lib/typescript/module/src/securitySuite/index.d.ts +6 -0
- package/lib/typescript/module/src/securitySuite/index.d.ts.map +1 -0
- package/lib/typescript/module/src/storage/index.d.ts +2 -0
- package/lib/typescript/module/src/storage/index.d.ts.map +1 -0
- package/lib/typescript/module/src/types/detection.d.ts +41 -0
- package/lib/typescript/module/src/types/detection.d.ts.map +1 -0
- package/package.json +2 -10
- package/src/clipboard/index.ts +1 -0
- package/src/crypto/index.ts +49 -0
- package/src/device/index.ts +47 -0
- package/src/errors.ts +84 -0
- package/src/index.tsx +293 -195
- package/src/integrity/index.ts +46 -0
- package/src/jws.ts +213 -0
- package/src/legacy/cryptoOptions.ts +49 -0
- package/src/native/bridge.ts +37 -0
- package/src/network/index.ts +1 -0
- package/src/risk/score.ts +49 -0
- package/src/runtime/index.ts +43 -0
- package/src/screen/index.ts +2 -0
- package/src/securitySuite/index.ts +45 -0
- package/src/storage/index.ts +1 -0
- package/src/types/detection.ts +46 -0
- package/android/src/main/java/com/securitysuite/StorageEncryption.java +0 -52
- package/ios/StorageEncryption.swift +0 -89
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.npmjs.com/package/react-native-security-suite)
|
|
6
6
|
|
|
7
|
-
**Comprehensive security solutions for React Native applications**
|
|
7
|
+
**Comprehensive security solutions for React Native applications** — Protect your mobile apps with root/jailbreak detection, SSL certificate pinning, RFC 7515 JWS request signing, hardware-backed secure storage, X25519 key exchange, screenshot protection, and network monitoring.
|
|
8
8
|
|
|
9
9
|
<div style="display: flex; flex-direction: row; justify-content: center; align-items: center; gap: 20px;">
|
|
10
10
|
<img src="https://raw.githubusercontent.com/mohamadnavabi/react-native-security-suite/master/pulse.gif" alt="iOS Pulse Network Monitor" width="200" />
|
|
@@ -23,17 +23,18 @@
|
|
|
23
23
|
|
|
24
24
|
### Data Security & Encryption
|
|
25
25
|
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
26
|
+
- **Secure Storage**: Hardware-backed encrypted storage (Keychain on iOS, EncryptedSharedPreferences on Android)
|
|
27
|
+
- **Diffie-Hellman / X25519 Key Exchange**: Configurable key agreement with `CryptoOptions`
|
|
28
|
+
- **Shared-Key Encryption**: AES-GCM encrypt/decrypt using a derived shared secret
|
|
29
|
+
- **Obfuscation**: Local string obfuscation with an explicit secret (not for credentials at rest)
|
|
30
30
|
|
|
31
31
|
### Network Security & Monitoring
|
|
32
32
|
|
|
33
|
+
- **JWS Request Signing (RFC 7515)**: HMAC-signed compact JWS tokens for `fetch` requests (HS256/HS384/HS512)
|
|
34
|
+
- **SSL Certificate Pinning**: Pin SPKI SHA-256 hashes with domain allowlists
|
|
33
35
|
- **Network Logger**: Built-in request/response logging
|
|
34
36
|
- **Android Chucker Integration**: Advanced network debugging
|
|
35
37
|
- **iOS Pulse Integration**: Network monitoring for iOS
|
|
36
|
-
- **SSL Pinning with Custom Certificates**: Enhanced security for API calls
|
|
37
38
|
|
|
38
39
|
## 📱 Supported Platforms
|
|
39
40
|
|
|
@@ -43,18 +44,16 @@
|
|
|
43
44
|
|
|
44
45
|
## 🛠 Installation
|
|
45
46
|
|
|
46
|
-
> **Note:** You must add `@react-native-async-storage/async-storage` in your app's `package.json` as well. React Native only autolinks native modules that are listed in your project's dependencies, so AsyncStorage must be a direct dependency of your app.
|
|
47
|
-
|
|
48
47
|
### Using Yarn
|
|
49
48
|
|
|
50
49
|
```bash
|
|
51
|
-
yarn add react-native-security-suite
|
|
50
|
+
yarn add react-native-security-suite
|
|
52
51
|
```
|
|
53
52
|
|
|
54
53
|
### Using NPM
|
|
55
54
|
|
|
56
55
|
```bash
|
|
57
|
-
npm install react-native-security-suite
|
|
56
|
+
npm install react-native-security-suite
|
|
58
57
|
```
|
|
59
58
|
|
|
60
59
|
### iOS Setup
|
|
@@ -63,6 +62,34 @@ npm install react-native-security-suite @react-native-async-storage/async-storag
|
|
|
63
62
|
cd ios && pod install
|
|
64
63
|
```
|
|
65
64
|
|
|
65
|
+
## 📁 Project Structure
|
|
66
|
+
|
|
67
|
+
The public API is exported from `src/index.tsx`. JWS validation and normalization live in a dedicated module so TypeScript and native code share the same contract.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
src/
|
|
71
|
+
├── index.tsx # Public API (fetch, crypto, SecureStorage, SecureView)
|
|
72
|
+
├── jws.ts # JWS types, validation, payload normalization
|
|
73
|
+
├── SecureView.tsx # Screenshot-protected view component
|
|
74
|
+
└── helpers.ts # Internal utilities
|
|
75
|
+
|
|
76
|
+
android/src/main/java/com/securitysuite/
|
|
77
|
+
├── SecureStorageNative.java # EncryptedSharedPreferences + Android Keystore
|
|
78
|
+
├── JWSGenerator.java # RFC 7515 compact JWS (sign + verify)
|
|
79
|
+
├── JwsFetchPayload.java # Default fetch signing payload builder
|
|
80
|
+
├── SecuritySuiteModule.java
|
|
81
|
+
└── Sslpinning.java # fetch + SSL pinning + JWS header injection
|
|
82
|
+
|
|
83
|
+
ios/
|
|
84
|
+
├── SecureStorageNative.swift # Keychain-backed secure storage
|
|
85
|
+
├── KeychainHelper.swift # Keychain read/write/query helpers
|
|
86
|
+
├── JWSGenerator.swift # RFC 7515 compact JWS (sign + verify)
|
|
87
|
+
├── SecuritySuite.swift # Native module + fetch
|
|
88
|
+
└── SslPinning.swift
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**JWS flow:** JavaScript validates options in `src/jws.ts` (secret, algorithm, headers, payload shape), normalizes the payload to the exact UTF-8 signing string, then calls native `generateJWS` on Android/iOS. For `fetch`, native code builds the signing payload when `jws.payload` is omitted, signs it, and attaches the compact token to the request header.
|
|
92
|
+
|
|
66
93
|
## 📖 Usage Examples
|
|
67
94
|
|
|
68
95
|
### 1. Root/Jailbreak Detection
|
|
@@ -108,33 +135,22 @@ const SensitiveScreen = () => {
|
|
|
108
135
|
};
|
|
109
136
|
```
|
|
110
137
|
|
|
111
|
-
### 3.
|
|
138
|
+
### 3. Obfuscation (local only)
|
|
112
139
|
|
|
113
|
-
|
|
140
|
+
Obfuscation requires an explicit secret and is intended for non-sensitive local encoding — not for credentials, tokens, or PII at rest. Use `SecureStorage` for persisted secrets.
|
|
114
141
|
|
|
115
142
|
```javascript
|
|
116
|
-
import {
|
|
117
|
-
|
|
118
|
-
const handleEncryption = async () => {
|
|
119
|
-
// Soft encryption (faster, less secure)
|
|
120
|
-
const softEncrypted = await encrypt('Sensitive data', false);
|
|
121
|
-
console.log('Soft encrypted:', softEncrypted);
|
|
122
|
-
|
|
123
|
-
const softDecrypted = await decrypt(softEncrypted, false);
|
|
124
|
-
console.log('Soft decrypted:', softDecrypted);
|
|
143
|
+
import { obfuscate, deobfuscate } from 'react-native-security-suite';
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
console.log('Hard encrypted:', hardEncrypted);
|
|
129
|
-
|
|
130
|
-
const hardDecrypted = await decrypt(hardEncrypted, true);
|
|
131
|
-
console.log('Hard decrypted:', hardDecrypted);
|
|
132
|
-
};
|
|
145
|
+
const encoded = await obfuscate('local-cache-value', 'app-specific-secret');
|
|
146
|
+
const decoded = await deobfuscate(encoded, 'app-specific-secret');
|
|
133
147
|
```
|
|
134
148
|
|
|
149
|
+
> `encrypt()` / `decrypt()` remain available but are deprecated. They now require an explicit `secretKey` and should be replaced with `obfuscate()` / `deobfuscate()` or `SecureStorage`.
|
|
150
|
+
|
|
135
151
|
### 4. Secure Storage
|
|
136
152
|
|
|
137
|
-
Store sensitive data
|
|
153
|
+
Store sensitive data in hardware-backed encrypted storage. On **iOS**, values are stored in the Keychain (`kSecClassGenericPassword`). On **Android**, values are encrypted with **EncryptedSharedPreferences** backed by Android Keystore (`AES256_GCM`).
|
|
138
154
|
|
|
139
155
|
```javascript
|
|
140
156
|
import { SecureStorage } from 'react-native-security-suite';
|
|
@@ -154,24 +170,39 @@ const handleSecureStorage = async () => {
|
|
|
154
170
|
})
|
|
155
171
|
);
|
|
156
172
|
|
|
157
|
-
// Retrieve
|
|
173
|
+
// Retrieve data
|
|
158
174
|
const token = await SecureStorage.getItem('userToken');
|
|
159
175
|
const credentials = await SecureStorage.getItem('userCredentials');
|
|
176
|
+
const allKeys = await SecureStorage.getAllKeys();
|
|
160
177
|
|
|
161
178
|
console.log('Retrieved token:', token);
|
|
162
179
|
console.log('Retrieved credentials:', JSON.parse(credentials));
|
|
180
|
+
console.log('Stored keys:', allKeys);
|
|
163
181
|
|
|
164
182
|
// Remove sensitive data
|
|
165
183
|
await SecureStorage.removeItem('userToken');
|
|
184
|
+
|
|
185
|
+
// Clear all secure storage entries
|
|
186
|
+
await SecureStorage.clear();
|
|
166
187
|
} catch (error) {
|
|
167
188
|
console.error('Secure storage error:', error);
|
|
168
189
|
}
|
|
169
190
|
};
|
|
170
191
|
```
|
|
171
192
|
|
|
172
|
-
|
|
193
|
+
#### Security Guarantee
|
|
194
|
+
|
|
195
|
+
`SecureStorage` no longer uses AsyncStorage or any JavaScript-side persistence. All keys and values are:
|
|
196
|
+
|
|
197
|
+
- **Encrypted at rest** — AES-GCM (Android) / Keychain-protected blobs (iOS)
|
|
198
|
+
- **Hardware-backed where available** — Android Keystore master key; iOS Keychain with `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`
|
|
199
|
+
- **System-managed keys** — No hardcoded salts or encryption keys in app code; the OS generates and protects master keys
|
|
200
|
+
|
|
201
|
+
If a native read/write fails (for example, KeyStore initialization errors on Android), the Promise rejects with a clear `Secure storage operation failed` error.
|
|
202
|
+
|
|
203
|
+
### 5. Diffie-Hellman / X25519 Key Exchange
|
|
173
204
|
|
|
174
|
-
Implement secure key exchange
|
|
205
|
+
Implement secure key exchange with optional `CryptoOptions` (defaults: X25519 key agreement, AES-256-GCM, HMAC-SHA-512):
|
|
175
206
|
|
|
176
207
|
```javascript
|
|
177
208
|
import {
|
|
@@ -181,33 +212,124 @@ import {
|
|
|
181
212
|
decryptBySharedKey,
|
|
182
213
|
} from 'react-native-security-suite';
|
|
183
214
|
|
|
215
|
+
const cryptoOptions = {
|
|
216
|
+
keyAgreementAlgorithm: 'X25519',
|
|
217
|
+
keyType: 'OKP',
|
|
218
|
+
encryptionKeyAlgorithm: 'AES-256',
|
|
219
|
+
hmacAlgorithm: 'HMAC-SHA-512',
|
|
220
|
+
cipher: 'AES-GCM',
|
|
221
|
+
};
|
|
222
|
+
|
|
184
223
|
const handleKeyExchange = async () => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const clientPublicKey = await getPublicKey();
|
|
188
|
-
console.log('Client public key:', clientPublicKey);
|
|
224
|
+
const clientPublicKey = await getPublicKey();
|
|
225
|
+
const serverPublicKey = 'SERVER_PUBLIC_KEY_FROM_API';
|
|
189
226
|
|
|
190
|
-
|
|
191
|
-
const serverPublicKey = 'SERVER_PUBLIC_KEY_FROM_API';
|
|
227
|
+
await getSharedKey(serverPublicKey, cryptoOptions);
|
|
192
228
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
229
|
+
const encryptedMessage = await encryptBySharedKey('Secret message', cryptoOptions);
|
|
230
|
+
const decryptedMessage = await decryptBySharedKey(encryptedMessage, cryptoOptions);
|
|
231
|
+
};
|
|
232
|
+
```
|
|
196
233
|
|
|
197
|
-
|
|
198
|
-
const encryptedMessage = await encryptBySharedKey('Secret message');
|
|
199
|
-
console.log('Encrypted message:', encryptedMessage);
|
|
234
|
+
### 6. JWS Generation (RFC 7515)
|
|
200
235
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
236
|
+
Generate [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515) compact JWS tokens. Supported algorithms: **HS256**, **HS384**, **HS512**. An explicit `secret` is always required.
|
|
237
|
+
|
|
238
|
+
**How it works**
|
|
239
|
+
|
|
240
|
+
1. TypeScript (`src/jws.ts`) validates `secret`, `algorithm`, and custom headers.
|
|
241
|
+
2. The payload is normalized to the exact UTF-8 string used for signing (objects/arrays are `JSON.stringify`'d; `undefined`/`null`/`''` become an empty payload).
|
|
242
|
+
3. Native code builds sorted protected headers (always including `alg`), base64url-encodes header and payload, signs `base64url(header).base64url(payload)` with HMAC, and returns the compact token.
|
|
243
|
+
|
|
244
|
+
**Empty payload** (`undefined`, `null`, or `''`) produces three segments with an empty middle segment:
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
import { generateJWS } from 'react-native-security-suite';
|
|
248
|
+
|
|
249
|
+
const jws = await generateJWS({
|
|
250
|
+
algorithm: 'HS256',
|
|
251
|
+
secret: 'my-temporary-secret',
|
|
252
|
+
headers: { kid: 'key-1' },
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// compact form: <protectedHeader>.<payload>.<signature>
|
|
256
|
+
// jws.split('.').length === 3
|
|
257
|
+
// jws.split('.')[1] === '' // empty payload segment
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**JSON payload:**
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
const jws = await generateJWS({
|
|
264
|
+
algorithm: 'HS512',
|
|
265
|
+
secret: 'my-temporary-secret',
|
|
266
|
+
payload: { amount: 1000, currency: 'USD' },
|
|
267
|
+
headers: { kid: 'key-1', request_id: 'req-123', typ: 'JWS' },
|
|
268
|
+
});
|
|
208
269
|
```
|
|
209
270
|
|
|
210
|
-
|
|
271
|
+
**Header rules:** keys must match `^[a-zA-Z][a-zA-Z0-9_-]*$`; values must be JSON primitives (`string`, `number`, `boolean`, `null`). String values must be printable ASCII (`0x20`–`0x7E`). If both `algorithm` and `headers.alg` are set, they must match.
|
|
272
|
+
|
|
273
|
+
> **Security note:** HS* JWS on mobile uses a client-side shared secret. It helps with request integrity when combined with TLS, but it is not proof against a fully compromised client.
|
|
274
|
+
|
|
275
|
+
### 7. Fetch Request Signing with JWS
|
|
276
|
+
|
|
277
|
+
Pass a `jws` object on `fetch` options. The signed token is sent as an HTTP header (default: `X-Request-Signature`).
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
import { fetch } from 'react-native-security-suite';
|
|
281
|
+
|
|
282
|
+
await fetch('https://api.example.com/payments', {
|
|
283
|
+
method: 'POST',
|
|
284
|
+
headers: { 'Content-Type': 'application/json' },
|
|
285
|
+
body: { amount: 1000 },
|
|
286
|
+
jws: {
|
|
287
|
+
algorithm: 'HS256',
|
|
288
|
+
secret: 'temporary-session-secret',
|
|
289
|
+
headers: {
|
|
290
|
+
kid: 'key-1',
|
|
291
|
+
request_id: 'req-123',
|
|
292
|
+
timestamp: Date.now(),
|
|
293
|
+
nonce: 'unique-nonce-per-request',
|
|
294
|
+
},
|
|
295
|
+
headerName: 'X-Request-Signature',
|
|
296
|
+
detached: false,
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Default signing payload** (when `jws.payload` is omitted): native code builds a sorted JSON object from:
|
|
302
|
+
|
|
303
|
+
| Field | Source |
|
|
304
|
+
|-------|--------|
|
|
305
|
+
| `method` | HTTP method (uppercased) |
|
|
306
|
+
| `path` | URL path (`/` if empty) |
|
|
307
|
+
| `query` | Query string (if present) |
|
|
308
|
+
| `bodyHash` | Base64url SHA-256 of request body (if body present) |
|
|
309
|
+
| `timestamp`, `nonce`, `request_id` | Copied from `jws.headers` when provided |
|
|
310
|
+
|
|
311
|
+
**Explicit fetch payload:** when you set `jws.payload`, it must already be a string (use `JSON.stringify` for objects). For fetch, object payloads are not auto-serialized — only `generateJWS` accepts object payloads directly.
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
jws: {
|
|
315
|
+
secret: 'temporary-session-secret',
|
|
316
|
+
payload: JSON.stringify({ amount: 1000, currency: 'USD' }),
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Detached fetch signing** — set `jws.detached: true` to send `header..signature` while the raw payload remains in the request body:
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
jws: {
|
|
324
|
+
secret: 'temporary-session-secret',
|
|
325
|
+
detached: true,
|
|
326
|
+
headers: { kid: 'key-1', request_id: 'req-123' },
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Migration from legacy options:** `options.keyId`, `options.requestId`, and top-level `options.secret` are deprecated. Use `options.jws` with `secret`, `headers.kid`, and `headers.request_id` instead. Legacy signing always used detached HS256 and the `X-JWS-Signature` header.
|
|
331
|
+
|
|
332
|
+
### 8. SSL Certificate Pinning
|
|
211
333
|
|
|
212
334
|
Secure your API communications with certificate pinning:
|
|
213
335
|
|
|
@@ -243,7 +365,7 @@ const secureApiCall = async () => {
|
|
|
243
365
|
};
|
|
244
366
|
```
|
|
245
367
|
|
|
246
|
-
###
|
|
368
|
+
### 9. Network Monitoring & Debugging
|
|
247
369
|
|
|
248
370
|
Monitor network requests in development:
|
|
249
371
|
|
|
@@ -274,37 +396,81 @@ const monitoredRequest = async () => {
|
|
|
274
396
|
|
|
275
397
|
### Security Detection
|
|
276
398
|
|
|
277
|
-
- `deviceHasSecurityRisk()`
|
|
399
|
+
- `deviceHasSecurityRisk()` — Detect rooted/jailbroken devices
|
|
278
400
|
|
|
279
401
|
### Encryption & Storage
|
|
280
402
|
|
|
281
|
-
- `
|
|
282
|
-
- `
|
|
283
|
-
- `SecureStorage` -
|
|
403
|
+
- `obfuscate(input, secret)` — Local obfuscation with explicit secret
|
|
404
|
+
- `deobfuscate(input, secret)` — Reverse `obfuscate`
|
|
405
|
+
- `SecureStorage` — Hardware-backed encrypted storage (`setItem`, `getItem`, `removeItem`, `getAllKeys`, `clear`, `multiGet`, `multiSet`, `multiRemove`)
|
|
406
|
+
- `encrypt(text, hardEncryption?, secretKey?)` — **deprecated**; requires `secretKey`
|
|
407
|
+
- `decrypt(encryptedText, hardEncryption?, secretKey?)` — **deprecated**; requires `secretKey`
|
|
284
408
|
|
|
285
409
|
### Key Exchange
|
|
286
410
|
|
|
287
|
-
- `getPublicKey()`
|
|
288
|
-
- `getSharedKey(serverPublicKey)`
|
|
289
|
-
- `encryptBySharedKey(text)` -
|
|
290
|
-
- `decryptBySharedKey(encryptedText)` -
|
|
411
|
+
- `getPublicKey()` — Generate client public key (JWK)
|
|
412
|
+
- `getSharedKey(serverPublicKey, options?)` — Derive shared secret; accepts `CryptoOptions`
|
|
413
|
+
- `encryptBySharedKey(text, options?)` — AES-GCM encrypt with derived key
|
|
414
|
+
- `decryptBySharedKey(encryptedText, options?)` — AES-GCM decrypt with derived key
|
|
415
|
+
|
|
416
|
+
#### `CryptoOptions`
|
|
417
|
+
|
|
418
|
+
| Option | Default | Description |
|
|
419
|
+
|--------|---------|-------------|
|
|
420
|
+
| `keyAgreementAlgorithm` | `'X25519'` | Key agreement (e.g. `X25519`, `ECDH`) |
|
|
421
|
+
| `keyType` | `'OKP'` | JWK key type (`OKP`, `EC`) |
|
|
422
|
+
| `encryptionKeyAlgorithm` | `'AES-256'` | Symmetric key algorithm |
|
|
423
|
+
| `hmacAlgorithm` | `'HMAC-SHA-512'` | HMAC for signing and key derivation |
|
|
424
|
+
| `cipher` | `'AES-GCM'` | AEAD cipher mode |
|
|
425
|
+
| `tagLength` | `128` | GCM authentication tag length (bits) |
|
|
426
|
+
| `ivLength` | `12` | GCM IV length (bytes) |
|
|
427
|
+
|
|
428
|
+
### JWS
|
|
429
|
+
|
|
430
|
+
- `generateJWS(options: GenerateJWSOptions)` — Generate compact or detached JWS
|
|
431
|
+
|
|
432
|
+
#### `GenerateJWSOptions`
|
|
433
|
+
|
|
434
|
+
| Field | Required | Description |
|
|
435
|
+
|-------|----------|-------------|
|
|
436
|
+
| `secret` | yes | Non-empty HMAC secret |
|
|
437
|
+
| `algorithm` | no | `HS256` (default), `HS384`, or `HS512` |
|
|
438
|
+
| `payload` | no | String, object, array, number, boolean, `null`, or `undefined` |
|
|
439
|
+
| `headers` | no | Custom protected headers (`kid`, `request_id`, etc.) |
|
|
440
|
+
|
|
441
|
+
#### `JwsFetchOptions` (on `fetch` options)
|
|
442
|
+
|
|
443
|
+
| Field | Required | Description |
|
|
444
|
+
|-------|----------|-------------|
|
|
445
|
+
| `secret` | yes | Non-empty HMAC secret |
|
|
446
|
+
| `algorithm` | no | `HS256` (default), `HS384`, or `HS512` |
|
|
447
|
+
| `headers` | no | Protected headers; `timestamp`/`nonce`/`request_id` also feed the default payload |
|
|
448
|
+
| `payload` | no | Explicit signing string; omit to use the default fetch payload |
|
|
449
|
+
| `detached` | no | Detached compact form (default: `false`) |
|
|
450
|
+
| `headerName` | no | Request header name (default: `X-Request-Signature`) |
|
|
451
|
+
|
|
452
|
+
Exported types: `JwsAlgorithm`, `JwsPayload`, `JwsHeaders`, `JwsHeaderValue`, `GenerateJWSOptions`, `JwsFetchOptions`.
|
|
291
453
|
|
|
292
454
|
### Network Security
|
|
293
455
|
|
|
294
|
-
- `fetch(url, options, loggerEnabled?)`
|
|
456
|
+
- `fetch(url, options, loggerEnabled?)` — Secure fetch with SSL pinning and optional JWS signing
|
|
457
|
+
|
|
458
|
+
`fetch` `options` also accepts `certificates` + `validDomains` for SSL pinning, and deprecated top-level `keyId`, `requestId`, `secret` for legacy JWS.
|
|
295
459
|
|
|
296
460
|
### UI Components
|
|
297
461
|
|
|
298
|
-
- `SecureView`
|
|
462
|
+
- `SecureView` — Screenshot-protected view component
|
|
299
463
|
|
|
300
464
|
## 🛡️ Security Best Practices
|
|
301
465
|
|
|
302
|
-
1. **Always validate certificates**
|
|
303
|
-
2. **Detect compromised devices**
|
|
304
|
-
3. **
|
|
305
|
-
4. **Protect sensitive UI**
|
|
306
|
-
5. **
|
|
307
|
-
6. **
|
|
466
|
+
1. **Always validate certificates** — Use SSL pinning for production APIs
|
|
467
|
+
2. **Detect compromised devices** — Check for root/jailbreak before sensitive operations
|
|
468
|
+
3. **Store secrets in SecureStorage** — Use hardware-backed storage for tokens and credentials
|
|
469
|
+
4. **Protect sensitive UI** — Wrap sensitive content in `SecureView`
|
|
470
|
+
5. **Sign requests with JWS** — Include `timestamp`, `nonce`, and `request_id` in `jws.headers` for replay protection; never embed long-lived secrets in the app binary
|
|
471
|
+
6. **Use detached JWS for body signing** — When the request body is the signed payload, set `jws.detached: true` so the body is not duplicated inside the token
|
|
472
|
+
7. **Monitor network traffic** — Use built-in logging for debugging only; disable in production
|
|
473
|
+
8. **Rotate session secrets** — Treat `jws.secret` as a short-lived session or request-scoped value from your backend
|
|
308
474
|
|
|
309
475
|
## 🐛 Troubleshooting
|
|
310
476
|
|
package/android/build.gradle
CHANGED
|
@@ -76,6 +76,12 @@ android {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
testOptions {
|
|
80
|
+
unitTests.all {
|
|
81
|
+
it.useJUnit()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
79
85
|
lintOptions {
|
|
80
86
|
disable "GradleCompatible"
|
|
81
87
|
}
|
|
@@ -90,6 +96,7 @@ android {
|
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
buildFeatures {
|
|
99
|
+
buildConfig true
|
|
93
100
|
dataBinding true
|
|
94
101
|
}
|
|
95
102
|
}
|
|
@@ -111,5 +118,9 @@ dependencies {
|
|
|
111
118
|
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
|
112
119
|
implementation "com.scottyab:rootbeer-lib:0.1.0"
|
|
113
120
|
implementation "com.github.chuckerteam.chucker:library:4.1.0"
|
|
121
|
+
implementation "androidx.security:security-crypto:1.1.0-alpha06"
|
|
122
|
+
|
|
123
|
+
testImplementation "junit:junit:4.13.2"
|
|
124
|
+
testImplementation "com.facebook.react:react-native:+"
|
|
114
125
|
}
|
|
115
126
|
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
package com.securitysuite;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configurable cryptographic parameters with secure defaults and whitelist validation.
|
|
7
|
+
*/
|
|
8
|
+
public final class CryptoConfig {
|
|
9
|
+
public static final String DEFAULT_KEY_AGREEMENT = "ECDH";
|
|
10
|
+
public static final String DEFAULT_KEY_FACTORY = "EC";
|
|
11
|
+
public static final String DEFAULT_ENCRYPTION_KEY_ALGORITHM = "AES";
|
|
12
|
+
public static final String DEFAULT_HMAC_KEY_ALGORITHM = "HmacSHA256";
|
|
13
|
+
public static final String DEFAULT_CIPHER_TRANSFORMATION = "AES/GCM/NoPadding";
|
|
14
|
+
public static final int DEFAULT_GCM_TAG_LENGTH = 128;
|
|
15
|
+
public static final int DEFAULT_GCM_IV_LENGTH = 12;
|
|
16
|
+
|
|
17
|
+
public final String keyAgreementAlgorithm;
|
|
18
|
+
public final String keyFactoryAlgorithm;
|
|
19
|
+
public final String encryptionKeyAlgorithm;
|
|
20
|
+
public final String hmacKeyAlgorithm;
|
|
21
|
+
public final String cipherTransformation;
|
|
22
|
+
public final int gcmTagLength;
|
|
23
|
+
public final int gcmIvLength;
|
|
24
|
+
|
|
25
|
+
private CryptoConfig(
|
|
26
|
+
String keyAgreementAlgorithm,
|
|
27
|
+
String keyFactoryAlgorithm,
|
|
28
|
+
String encryptionKeyAlgorithm,
|
|
29
|
+
String hmacKeyAlgorithm,
|
|
30
|
+
String cipherTransformation,
|
|
31
|
+
int gcmTagLength,
|
|
32
|
+
int gcmIvLength
|
|
33
|
+
) {
|
|
34
|
+
this.keyAgreementAlgorithm = keyAgreementAlgorithm;
|
|
35
|
+
this.keyFactoryAlgorithm = keyFactoryAlgorithm;
|
|
36
|
+
this.encryptionKeyAlgorithm = encryptionKeyAlgorithm;
|
|
37
|
+
this.hmacKeyAlgorithm = hmacKeyAlgorithm;
|
|
38
|
+
this.cipherTransformation = cipherTransformation;
|
|
39
|
+
this.gcmTagLength = gcmTagLength;
|
|
40
|
+
this.gcmIvLength = gcmIvLength;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static CryptoConfig defaults() {
|
|
44
|
+
return new CryptoConfig(
|
|
45
|
+
DEFAULT_KEY_AGREEMENT,
|
|
46
|
+
DEFAULT_KEY_FACTORY,
|
|
47
|
+
DEFAULT_ENCRYPTION_KEY_ALGORITHM,
|
|
48
|
+
DEFAULT_HMAC_KEY_ALGORITHM,
|
|
49
|
+
DEFAULT_CIPHER_TRANSFORMATION,
|
|
50
|
+
DEFAULT_GCM_TAG_LENGTH,
|
|
51
|
+
DEFAULT_GCM_IV_LENGTH
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public static CryptoConfig fromReadableMap(ReadableMap options) {
|
|
56
|
+
if (options == null) {
|
|
57
|
+
return defaults();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return new CryptoConfig(
|
|
61
|
+
validateKeyAgreement(readString(options, "keyAgreementAlgorithm", DEFAULT_KEY_AGREEMENT)),
|
|
62
|
+
validateKeyFactory(readString(options, "keyFactoryAlgorithm", DEFAULT_KEY_FACTORY)),
|
|
63
|
+
validateEncryptionKeyAlgorithm(
|
|
64
|
+
readString(options, "encryptionKeyAlgorithm", DEFAULT_ENCRYPTION_KEY_ALGORITHM)
|
|
65
|
+
),
|
|
66
|
+
validateHmacKeyAlgorithm(readString(options, "hmacKeyAlgorithm", DEFAULT_HMAC_KEY_ALGORITHM)),
|
|
67
|
+
validateCipherTransformation(
|
|
68
|
+
readString(options, "cipherTransformation", DEFAULT_CIPHER_TRANSFORMATION)
|
|
69
|
+
),
|
|
70
|
+
options.hasKey("gcmTagLength") ? options.getInt("gcmTagLength") : DEFAULT_GCM_TAG_LENGTH,
|
|
71
|
+
options.hasKey("gcmIvLength") ? options.getInt("gcmIvLength") : DEFAULT_GCM_IV_LENGTH
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public CryptoConfig merge(ReadableMap options) {
|
|
76
|
+
return options == null ? this : fromReadableMap(mergeMaps(this, options));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private static ReadableMap mergeMaps(CryptoConfig base, ReadableMap overrides) {
|
|
80
|
+
com.facebook.react.bridge.WritableMap map = com.facebook.react.bridge.Arguments.createMap();
|
|
81
|
+
map.putString("keyAgreementAlgorithm", base.keyAgreementAlgorithm);
|
|
82
|
+
map.putString("keyFactoryAlgorithm", base.keyFactoryAlgorithm);
|
|
83
|
+
map.putString("encryptionKeyAlgorithm", base.encryptionKeyAlgorithm);
|
|
84
|
+
map.putString("hmacKeyAlgorithm", base.hmacKeyAlgorithm);
|
|
85
|
+
map.putString("cipherTransformation", base.cipherTransformation);
|
|
86
|
+
map.putInt("gcmTagLength", base.gcmTagLength);
|
|
87
|
+
map.putInt("gcmIvLength", base.gcmIvLength);
|
|
88
|
+
|
|
89
|
+
if (overrides.hasKey("keyAgreementAlgorithm")) {
|
|
90
|
+
map.putString("keyAgreementAlgorithm", overrides.getString("keyAgreementAlgorithm"));
|
|
91
|
+
}
|
|
92
|
+
if (overrides.hasKey("keyFactoryAlgorithm")) {
|
|
93
|
+
map.putString("keyFactoryAlgorithm", overrides.getString("keyFactoryAlgorithm"));
|
|
94
|
+
}
|
|
95
|
+
if (overrides.hasKey("encryptionKeyAlgorithm")) {
|
|
96
|
+
map.putString("encryptionKeyAlgorithm", overrides.getString("encryptionKeyAlgorithm"));
|
|
97
|
+
}
|
|
98
|
+
if (overrides.hasKey("hmacKeyAlgorithm")) {
|
|
99
|
+
map.putString("hmacKeyAlgorithm", overrides.getString("hmacKeyAlgorithm"));
|
|
100
|
+
}
|
|
101
|
+
if (overrides.hasKey("cipherTransformation")) {
|
|
102
|
+
map.putString("cipherTransformation", overrides.getString("cipherTransformation"));
|
|
103
|
+
}
|
|
104
|
+
if (overrides.hasKey("gcmTagLength")) {
|
|
105
|
+
map.putInt("gcmTagLength", overrides.getInt("gcmTagLength"));
|
|
106
|
+
}
|
|
107
|
+
if (overrides.hasKey("gcmIvLength")) {
|
|
108
|
+
map.putInt("gcmIvLength", overrides.getInt("gcmIvLength"));
|
|
109
|
+
}
|
|
110
|
+
return map;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private static String readString(ReadableMap map, String key, String defaultValue) {
|
|
114
|
+
if (map.hasKey(key) && map.getString(key) != null) {
|
|
115
|
+
return map.getString(key).trim();
|
|
116
|
+
}
|
|
117
|
+
return defaultValue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private static String validateKeyAgreement(String algorithm) {
|
|
121
|
+
if ("ECDH".equals(algorithm)) {
|
|
122
|
+
return algorithm;
|
|
123
|
+
}
|
|
124
|
+
throw new IllegalArgumentException("Unsupported keyAgreementAlgorithm: " + algorithm);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private static String validateKeyFactory(String algorithm) {
|
|
128
|
+
if ("EC".equals(algorithm)) {
|
|
129
|
+
return algorithm;
|
|
130
|
+
}
|
|
131
|
+
throw new IllegalArgumentException("Unsupported keyFactoryAlgorithm: " + algorithm);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private static String validateEncryptionKeyAlgorithm(String algorithm) {
|
|
135
|
+
if ("AES".equals(algorithm)) {
|
|
136
|
+
return algorithm;
|
|
137
|
+
}
|
|
138
|
+
throw new IllegalArgumentException("Unsupported encryptionKeyAlgorithm: " + algorithm);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private static String validateHmacKeyAlgorithm(String algorithm) {
|
|
142
|
+
switch (algorithm) {
|
|
143
|
+
case "HmacSHA256":
|
|
144
|
+
case "HmacSHA384":
|
|
145
|
+
case "HmacSHA512":
|
|
146
|
+
return algorithm;
|
|
147
|
+
default:
|
|
148
|
+
throw new IllegalArgumentException("Unsupported hmacKeyAlgorithm: " + algorithm);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private static String validateCipherTransformation(String transformation) {
|
|
153
|
+
if ("AES/GCM/NoPadding".equals(transformation)) {
|
|
154
|
+
return transformation;
|
|
155
|
+
}
|
|
156
|
+
throw new IllegalArgumentException("Unsupported cipherTransformation: " + transformation);
|
|
157
|
+
}
|
|
158
|
+
}
|