react-native-ssl-manager 1.0.0
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/LICENSE +20 -0
- package/README.md +264 -0
- package/android/build.gradle +97 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/java/com/usesslpinning/UseSslPinningFactory.kt +50 -0
- package/android/src/main/java/com/usesslpinning/UseSslPinningModule.kt +45 -0
- package/android/src/main/java/com/usesslpinning/UseSslPinningPackage.kt +17 -0
- package/ios/UseSslPinning-Bridging-Header.h +2 -0
- package/ios/UseSslPinning.mm +19 -0
- package/ios/UseSslPinning.swift +169 -0
- package/lib/commonjs/index.js +42 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/index.js +33 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/src/index.d.ts +15 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +172 -0
- package/react-native-ssl-manager.podspec +43 -0
- package/src/index.tsx +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Trần Đình Huy
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# react-native-ssl-manager
|
|
2
|
+
|
|
3
|
+
React Native SSL Pinning provides seamless SSL certificate pinning integration for enhanced network security in React Native apps. This module enables developers to easily implement and manage certificate pinning, protecting applications against man-in-the-middle (MITM) attacks. With dynamic configuration options and the ability to toggle SSL pinning, it's particularly useful for development and testing scenarios.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔒 Easy SSL certificate pinning implementation
|
|
8
|
+
- 🔄 Dynamic enabling/disabling of SSL pinning
|
|
9
|
+
- ⚡ Optimized for development and testing workflows
|
|
10
|
+
- 📱 Cross-platform support (iOS & Android)
|
|
11
|
+
- 🛠️ Simple configuration using JSON
|
|
12
|
+
- 🚀 Performance-optimized implementation
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install react-native-ssl-manager
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Basic Setup
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import {
|
|
26
|
+
initializeSslPinning,
|
|
27
|
+
setUseSSLPinning,
|
|
28
|
+
getUseSSLPinning
|
|
29
|
+
} from 'react-native-ssl-manager';
|
|
30
|
+
|
|
31
|
+
// Initialize SSL pinning with configuration
|
|
32
|
+
const sslConfig = {
|
|
33
|
+
"domains": {
|
|
34
|
+
"development": "api.dev.example.com",
|
|
35
|
+
"production": "api.example.com"
|
|
36
|
+
},
|
|
37
|
+
"sha256Keys": {
|
|
38
|
+
"api.dev.example.com": [
|
|
39
|
+
"sha256/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=",
|
|
40
|
+
"sha256/YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY="
|
|
41
|
+
],
|
|
42
|
+
"api.example.com": [
|
|
43
|
+
"sha256/ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ=",
|
|
44
|
+
"sha256/WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW="
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Initialize the SSL pinning
|
|
50
|
+
await initializeSslPinning(JSON.stringify(sslConfig));
|
|
51
|
+
|
|
52
|
+
// Enable SSL pinning
|
|
53
|
+
await setUseSSLPinning(true);
|
|
54
|
+
|
|
55
|
+
// Check if SSL pinning is enabled
|
|
56
|
+
const isEnabled = await getUseSSLPinning();
|
|
57
|
+
console.log('SSL Pinning enabled:', isEnabled);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Configuration File (ssl_config.json)
|
|
61
|
+
|
|
62
|
+
Create a configuration file with your domain certificates. Example structure:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"domains": {
|
|
67
|
+
"development": "api.dev.example.com",
|
|
68
|
+
"production": "api.example.com"
|
|
69
|
+
},
|
|
70
|
+
"sha256Keys": {
|
|
71
|
+
"api.dev.example.com": [
|
|
72
|
+
"sha256/certificate-hash-1=",
|
|
73
|
+
"sha256/certificate-hash-2="
|
|
74
|
+
],
|
|
75
|
+
"api.example.com": [
|
|
76
|
+
"sha256/certificate-hash-3=",
|
|
77
|
+
"sha256/certificate-hash-4="
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### API Reference
|
|
84
|
+
|
|
85
|
+
#### `initializeSslPinning(configJsonString: string): Promise<any>`
|
|
86
|
+
Initializes the SSL pinning configuration with the provided JSON string configuration.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
await initializeSslPinning(JSON.stringify(sslConfig));
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### `setUseSSLPinning(usePinning: boolean): void`
|
|
93
|
+
Enables or disables SSL pinning dynamically.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
await setUseSSLPinning(true); // Enable SSL pinning
|
|
97
|
+
await setUseSSLPinning(false); // Disable SSL pinning
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### `getUseSSLPinning(): Promise<boolean>`
|
|
101
|
+
Retrieves the current state of SSL pinning.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const isEnabled = await getUseSSLPinning();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Important Notes ⚠️
|
|
108
|
+
|
|
109
|
+
### Restarting After SSL Pinning Changes
|
|
110
|
+
|
|
111
|
+
When using `setUseSSLPinning`, a restart of the application is required for changes to take effect. This is because SSL pinning is implemented at the native level.
|
|
112
|
+
|
|
113
|
+
#### Using React Native Restart
|
|
114
|
+
|
|
115
|
+
First, install react-native-restart:
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
# Using npm
|
|
119
|
+
npm install react-native-restart
|
|
120
|
+
|
|
121
|
+
# Using yarn
|
|
122
|
+
yarn add react-native-restart
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
For iOS, run pod install:
|
|
126
|
+
```sh
|
|
127
|
+
cd ios && pod install
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Then use it in your code:
|
|
131
|
+
```typescript
|
|
132
|
+
import RNRestart from 'react-native-restart';
|
|
133
|
+
|
|
134
|
+
const toggleSSLPinning = async (enabled: boolean) => {
|
|
135
|
+
await setUseSSLPinning(enabled);
|
|
136
|
+
// Restart the app to apply changes
|
|
137
|
+
RNRestart.Restart();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Example with user confirmation
|
|
141
|
+
const handleSSLToggle = async (enabled: boolean) => {
|
|
142
|
+
// Save any necessary state
|
|
143
|
+
await saveAppState();
|
|
144
|
+
|
|
145
|
+
// Update SSL pinning
|
|
146
|
+
await setUseSSLPinning(enabled);
|
|
147
|
+
|
|
148
|
+
// Show user message
|
|
149
|
+
Alert.alert(
|
|
150
|
+
'Restart Required',
|
|
151
|
+
'The app needs to restart to apply security changes.',
|
|
152
|
+
[
|
|
153
|
+
{
|
|
154
|
+
text: 'Restart Now',
|
|
155
|
+
onPress: () => RNRestart.Restart()
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Development and Testing Benefits
|
|
163
|
+
|
|
164
|
+
### For Developers
|
|
165
|
+
- **Quick Toggling**: Easily switch SSL pinning on/off during development
|
|
166
|
+
- **Performance Optimization**: Minimize SSL verification overhead during development
|
|
167
|
+
- **Flexible Configuration**: Support multiple environments with different certificates
|
|
168
|
+
|
|
169
|
+
### For QA Teams
|
|
170
|
+
- **Efficient Testing**: Quickly verify API behavior with and without SSL pinning
|
|
171
|
+
- **Issue Investigation**: Easily isolate SSL-related issues
|
|
172
|
+
- **Environment Switching**: Seamlessly test across different environments
|
|
173
|
+
|
|
174
|
+
## Best Practices
|
|
175
|
+
|
|
176
|
+
1. **Environment Management**
|
|
177
|
+
- Keep separate configurations for development and production
|
|
178
|
+
- Store production certificates securely
|
|
179
|
+
|
|
180
|
+
2. **Performance Optimization**
|
|
181
|
+
- Enable SSL pinning only when necessary during development
|
|
182
|
+
- Use development certificates for testing environments
|
|
183
|
+
|
|
184
|
+
3. **Security Considerations**
|
|
185
|
+
- Always enable SSL pinning in production
|
|
186
|
+
- Regularly update certificates before expiration
|
|
187
|
+
- Maintain multiple backup certificates
|
|
188
|
+
|
|
189
|
+
## Roadmap 🗺️
|
|
190
|
+
|
|
191
|
+
We're actively working on expanding the capabilities of react-native-ssl-manager. Here are our planned features:
|
|
192
|
+
|
|
193
|
+
### Upcoming Features
|
|
194
|
+
|
|
195
|
+
- 📱 **Expo Plugin Integration**
|
|
196
|
+
- Native SSL pinning support for Expo projects
|
|
197
|
+
- Seamless configuration through expo-config-plugin
|
|
198
|
+
- Auto-linking capabilities for Expo development builds
|
|
199
|
+
- Support for Expo's development client
|
|
200
|
+
|
|
201
|
+
## Testing with Proxyman 🔍
|
|
202
|
+
|
|
203
|
+
Proxyman is a powerful tool for testing SSL pinning implementation. Here's how you can verify your SSL pinning configuration:
|
|
204
|
+
|
|
205
|
+
### Setup Verification
|
|
206
|
+
|
|
207
|
+
1. **Install Proxyman**
|
|
208
|
+
- Download and install [Proxyman](https://proxyman.io/)
|
|
209
|
+
- Install Proxyman's SSL certificate on your device/simulator
|
|
210
|
+
|
|
211
|
+
2. **Testing SSL Pinning**
|
|
212
|
+
```typescript
|
|
213
|
+
// Enable SSL Pinning
|
|
214
|
+
await setUseSSLPinning(true);
|
|
215
|
+
|
|
216
|
+
// Make API requests through your app
|
|
217
|
+
// If SSL pinning is working correctly:
|
|
218
|
+
// - Requests will fail when Proxyman tries to intercept them
|
|
219
|
+
// - You'll see SSL/TLS handshake errors
|
|
220
|
+
|
|
221
|
+
// Disable SSL Pinning for debugging
|
|
222
|
+
await setUseSSLPinning(false);
|
|
223
|
+
// Now you can intercept and inspect API calls with Proxyman
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Common Test Scenarios
|
|
227
|
+
|
|
228
|
+
1. **Verify SSL Pinning is Active**
|
|
229
|
+
- Enable SSL pinning
|
|
230
|
+
- Attempt to intercept traffic with Proxyman
|
|
231
|
+
- Requests should fail with SSL handshake errors
|
|
232
|
+
|
|
233
|
+
2. **Debug API Calls**
|
|
234
|
+
- Disable SSL pinning temporarily
|
|
235
|
+
- Use Proxyman to inspect API requests/responses
|
|
236
|
+
- Helpful for QA testing and development
|
|
237
|
+
|
|
238
|
+
3. **Certificate Validation**
|
|
239
|
+
- Verify your SSL configuration matches the certificates in ssl_config.json
|
|
240
|
+
- Test against both development and production endpoints
|
|
241
|
+
|
|
242
|
+
### Troubleshooting Tips
|
|
243
|
+
|
|
244
|
+
- If requests succeed with Proxyman while SSL pinning is enabled, check your configuration
|
|
245
|
+
- Verify that the SHA256 hashes in your config match your server certificates
|
|
246
|
+
- Test both development and production environments separately
|
|
247
|
+
|
|
248
|
+
This integration with Proxyman makes it easy to:
|
|
249
|
+
- Verify SSL pinning implementation
|
|
250
|
+
- Debug API communications
|
|
251
|
+
- Validate security configurations
|
|
252
|
+
- Speed up development and testing workflows
|
|
253
|
+
|
|
254
|
+
## Contributing
|
|
255
|
+
|
|
256
|
+
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
For open source projects, say how it is licensed.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
|
|
3
|
+
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["UseSslPinning_kotlinVersion"]
|
|
4
|
+
|
|
5
|
+
repositories {
|
|
6
|
+
google()
|
|
7
|
+
mavenCentral()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
dependencies {
|
|
11
|
+
classpath "com.android.tools.build:gradle:7.2.1"
|
|
12
|
+
// noinspection DifferentKotlinGradleVersion
|
|
13
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def isNewArchitectureEnabled() {
|
|
18
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
apply plugin: "com.android.library"
|
|
22
|
+
apply plugin: "kotlin-android"
|
|
23
|
+
|
|
24
|
+
if (isNewArchitectureEnabled()) {
|
|
25
|
+
apply plugin: "com.facebook.react"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def getExtOrDefault(name) {
|
|
29
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["UseSslPinning_" + name]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def getExtOrIntegerDefault(name) {
|
|
33
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["UseSslPinning_" + name]).toInteger()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def supportsNamespace() {
|
|
37
|
+
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
|
38
|
+
def major = parsed[0].toInteger()
|
|
39
|
+
def minor = parsed[1].toInteger()
|
|
40
|
+
|
|
41
|
+
// Namespace support was added in 7.3.0
|
|
42
|
+
return (major == 7 && minor >= 3) || major >= 8
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
android {
|
|
46
|
+
if (supportsNamespace()) {
|
|
47
|
+
namespace "com.usesslpinning"
|
|
48
|
+
|
|
49
|
+
sourceSets {
|
|
50
|
+
main {
|
|
51
|
+
manifest.srcFile "src/main/AndroidManifestNew.xml"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
57
|
+
|
|
58
|
+
defaultConfig {
|
|
59
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
60
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
buildTypes {
|
|
65
|
+
release {
|
|
66
|
+
minifyEnabled false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
lintOptions {
|
|
71
|
+
disable "GradleCompatible"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
compileOptions {
|
|
75
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
76
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
repositories {
|
|
82
|
+
mavenCentral()
|
|
83
|
+
google()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
87
|
+
|
|
88
|
+
dependencies {
|
|
89
|
+
// For < 0.71, this will be from the local maven repo
|
|
90
|
+
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
|
91
|
+
//noinspection GradleDynamicVersion
|
|
92
|
+
implementation "com.facebook.react:react-native:+"
|
|
93
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
94
|
+
// Add OkHttp dependency for SSL pinning
|
|
95
|
+
implementation "com.squareup.okhttp3:okhttp:4.9.1"
|
|
96
|
+
}
|
|
97
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
package com.usesslpinning
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.facebook.react.modules.network.OkHttpClientFactory
|
|
6
|
+
import com.facebook.react.modules.network.ReactCookieJarContainer
|
|
7
|
+
import okhttp3.CertificatePinner
|
|
8
|
+
import okhttp3.OkHttpClient
|
|
9
|
+
import org.json.JSONObject
|
|
10
|
+
|
|
11
|
+
class UseSslPinningFactory(
|
|
12
|
+
private val context: Context,
|
|
13
|
+
private val configJsonString: String
|
|
14
|
+
) : OkHttpClientFactory {
|
|
15
|
+
|
|
16
|
+
override fun createNewNetworkModuleClient(): OkHttpClient {
|
|
17
|
+
val sharedPreferences = context.getSharedPreferences("AppSettings", Context.MODE_PRIVATE)
|
|
18
|
+
val useSSLPinning = sharedPreferences.getBoolean("useSSLPinning", true)
|
|
19
|
+
|
|
20
|
+
val clientBuilder = OkHttpClient.Builder().cookieJar(ReactCookieJarContainer()).apply {
|
|
21
|
+
if (useSSLPinning) {
|
|
22
|
+
val certificatePinnerBuilder = CertificatePinner.Builder()
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
val configJson = JSONObject(configJsonString)
|
|
26
|
+
addCertificatesToPinner(certificatePinnerBuilder, configJson)
|
|
27
|
+
val certificatePinner = certificatePinnerBuilder.build()
|
|
28
|
+
certificatePinner(certificatePinner)
|
|
29
|
+
} catch (e: Exception) {
|
|
30
|
+
e.printStackTrace()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}.cache(null).build()
|
|
34
|
+
|
|
35
|
+
return clientBuilder
|
|
36
|
+
}
|
|
37
|
+
private fun addCertificatesToPinner(certificatePinnerBuilder: CertificatePinner.Builder, configJson: JSONObject) {
|
|
38
|
+
val sha256Keys = configJson.getJSONObject("sha256Keys")
|
|
39
|
+
val hostnames = sha256Keys.keys()
|
|
40
|
+
while (hostnames.hasNext()) {
|
|
41
|
+
val hostname = hostnames.next()
|
|
42
|
+
val keysArray = sha256Keys.getJSONArray(hostname)
|
|
43
|
+
for (i in 0 until keysArray.length()) {
|
|
44
|
+
val sha256Key = keysArray.getString(i)
|
|
45
|
+
certificatePinnerBuilder.add(hostname, sha256Key)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package com.usesslpinning
|
|
2
|
+
|
|
3
|
+
import android.content.Context // Thêm dòng này
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
7
|
+
import com.facebook.react.bridge.ReactMethod
|
|
8
|
+
import com.facebook.react.bridge.Promise
|
|
9
|
+
import com.facebook.react.modules.network.OkHttpClientProvider
|
|
10
|
+
|
|
11
|
+
class UseSslPinningModule(reactContext: ReactApplicationContext) :
|
|
12
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
13
|
+
|
|
14
|
+
override fun getName(): String {
|
|
15
|
+
return NAME
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@ReactMethod
|
|
19
|
+
fun setUseSSLPinning(usePinning: Boolean) {
|
|
20
|
+
val sharedPreferences = reactApplicationContext.getSharedPreferences("AppSettings", Context.MODE_PRIVATE)
|
|
21
|
+
sharedPreferences.edit().putBoolean("useSSLPinning", usePinning).apply()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@ReactMethod
|
|
25
|
+
fun getUseSSLPinning(promise: Promise) {
|
|
26
|
+
val sharedPreferences = reactApplicationContext.getSharedPreferences("AppSettings", Context.MODE_PRIVATE)
|
|
27
|
+
val usePinning = sharedPreferences.getBoolean("useSSLPinning", true)
|
|
28
|
+
promise.resolve(usePinning)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@ReactMethod
|
|
32
|
+
fun initializeSslPinning(configJsonString: String, promise: Promise) {
|
|
33
|
+
try {
|
|
34
|
+
OkHttpClientProvider.setOkHttpClientFactory(UseSslPinningFactory(reactApplicationContext, configJsonString))
|
|
35
|
+
Log.d("MyLibrary", "SSL Pinning initialized successfully")
|
|
36
|
+
promise.resolve("SSL Pinning initialized successfully")
|
|
37
|
+
} catch (e: Exception) {
|
|
38
|
+
promise.reject("SSL_PINNING_ERROR", "Failed to initialize SSL Pinning", e)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
companion object {
|
|
43
|
+
const val NAME = "UseSslPinning"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package com.usesslpinning
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UseSslPinningPackage : ReactPackage {
|
|
10
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
11
|
+
return listOf(UseSslPinningModule(reactContext))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
15
|
+
return emptyList()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#import <React/RCTBridgeModule.h>
|
|
2
|
+
|
|
3
|
+
@interface RCT_EXTERN_MODULE(UseSslPinning, NSObject)
|
|
4
|
+
|
|
5
|
+
RCT_EXTERN_METHOD(setUseSSLPinning:(BOOL)usePinning)
|
|
6
|
+
|
|
7
|
+
RCT_EXTERN_METHOD(getUseSSLPinning:(RCTPromiseResolveBlock)resolve
|
|
8
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
9
|
+
|
|
10
|
+
RCT_EXTERN_METHOD(initializeSslPinning:(NSString *)configJsonString
|
|
11
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
12
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
13
|
+
|
|
14
|
+
+ (BOOL)requiresMainQueueSetup
|
|
15
|
+
{
|
|
16
|
+
return NO;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import TrustKit
|
|
3
|
+
import TrustKit.TSKPinningValidator
|
|
4
|
+
import TrustKit.TSKPinningValidatorCallback
|
|
5
|
+
|
|
6
|
+
// Add SSLPinningError enum definition
|
|
7
|
+
enum SSLPinningError: Error {
|
|
8
|
+
case invalidConfiguration
|
|
9
|
+
case invalidPinConfiguration(domain: String)
|
|
10
|
+
|
|
11
|
+
var message: String {
|
|
12
|
+
switch self {
|
|
13
|
+
case .invalidConfiguration:
|
|
14
|
+
return "Invalid SSL pinning configuration format"
|
|
15
|
+
case .invalidPinConfiguration(let domain):
|
|
16
|
+
return "Invalid pin configuration for domain: \(domain)"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@objc(UseSslPinning)
|
|
22
|
+
class UseSslPinning: NSObject {
|
|
23
|
+
private static var sharedTrustKit: TrustKit?
|
|
24
|
+
private let userDefaults = UserDefaults.standard
|
|
25
|
+
private let useSSLPinningKey = "useSSLPinning"
|
|
26
|
+
|
|
27
|
+
private func cleanJsonString(_ jsonString: String) -> String {
|
|
28
|
+
var cleaned = jsonString
|
|
29
|
+
.replacingOccurrences(of: "\n", with: "")
|
|
30
|
+
.replacingOccurrences(of: "| ", with: "")
|
|
31
|
+
.replacingOccurrences(of: "\\ ", with: "")
|
|
32
|
+
.replacingOccurrences(of: "\\\"", with: "\"")
|
|
33
|
+
|
|
34
|
+
// Remove any remaining backslashes before quotes
|
|
35
|
+
cleaned = cleaned.replacingOccurrences(of: "\\(?!\")", with: "")
|
|
36
|
+
|
|
37
|
+
// Clean up any double spaces
|
|
38
|
+
cleaned = cleaned.replacingOccurrences(of: " ", with: " ")
|
|
39
|
+
|
|
40
|
+
NSLog("Original JSON: %@", jsonString)
|
|
41
|
+
NSLog("Cleaned JSON: %@", cleaned)
|
|
42
|
+
|
|
43
|
+
return cleaned
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private func validateAndCleanPins(_ pins: [String], for domain: String) throws -> [String] {
|
|
47
|
+
return try pins.map { pin -> String in
|
|
48
|
+
var cleanPin = pin.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
49
|
+
|
|
50
|
+
// Verify pin format
|
|
51
|
+
guard cleanPin.starts(with: "sha256/") else {
|
|
52
|
+
NSLog("Invalid pin format (missing sha256/): %@", cleanPin)
|
|
53
|
+
throw SSLPinningError.invalidPinConfiguration(domain: domain)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Remove sha256/ prefix for TrustKit
|
|
57
|
+
cleanPin = cleanPin.replacingOccurrences(of: "sha256/", with: "")
|
|
58
|
+
|
|
59
|
+
// Verify base64 format
|
|
60
|
+
guard cleanPin.range(of: "^[A-Za-z0-9+/=]+$", options: .regularExpression) != nil else {
|
|
61
|
+
NSLog("Invalid pin format (not base64): %@", cleanPin)
|
|
62
|
+
throw SSLPinningError.invalidPinConfiguration(domain: domain)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return cleanPin
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@objc
|
|
70
|
+
func initializeSslPinning(_ configJsonString: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
71
|
+
// Check if SSL pinning is enabled
|
|
72
|
+
let isSSLPinningEnabled = userDefaults.bool(forKey: useSSLPinningKey)
|
|
73
|
+
|
|
74
|
+
if isSSLPinningEnabled {
|
|
75
|
+
do {
|
|
76
|
+
// Parse JSON configuration
|
|
77
|
+
guard let jsonData = configJsonString.data(using: .utf8),
|
|
78
|
+
let config = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any],
|
|
79
|
+
let sha256Keys = config["sha256Keys"] as? [String: [String]] else {
|
|
80
|
+
throw SSLPinningError.invalidConfiguration
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Build pinned domains configuration
|
|
84
|
+
var pinnedDomains: [String: Any] = [:]
|
|
85
|
+
|
|
86
|
+
// Process each domain and its pins from JSON
|
|
87
|
+
for (domain, pins) in sha256Keys {
|
|
88
|
+
let cleanedPins = try pins.map { pin -> String in
|
|
89
|
+
// Validate and clean the pin
|
|
90
|
+
var cleanPin = pin.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
91
|
+
|
|
92
|
+
// Verify pin format
|
|
93
|
+
guard cleanPin.starts(with: "sha256/") else {
|
|
94
|
+
NSLog("Invalid pin format for domain %@: %@", domain, cleanPin)
|
|
95
|
+
throw SSLPinningError.invalidPinConfiguration(domain: domain)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Remove sha256/ prefix for TrustKit
|
|
99
|
+
cleanPin = cleanPin.replacingOccurrences(of: "sha256/", with: "")
|
|
100
|
+
|
|
101
|
+
return cleanPin
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
pinnedDomains[domain] = [
|
|
105
|
+
kTSKIncludeSubdomains: true,
|
|
106
|
+
kTSKEnforcePinning: true,
|
|
107
|
+
kTSKDisableDefaultReportUri: true,
|
|
108
|
+
kTSKPublicKeyHashes: cleanedPins
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let trustKitConfig: [String: Any] = [
|
|
113
|
+
kTSKSwizzleNetworkDelegates: true,
|
|
114
|
+
kTSKPinnedDomains: pinnedDomains
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
DispatchQueue.main.async {
|
|
118
|
+
// Initialize TrustKit with the configuration
|
|
119
|
+
TrustKit.initSharedInstance(withConfiguration: trustKitConfig)
|
|
120
|
+
|
|
121
|
+
// Set up validation callback
|
|
122
|
+
TrustKit.sharedInstance().pinningValidatorCallback = { result, notedHostname, policy in
|
|
123
|
+
switch result.finalTrustDecision {
|
|
124
|
+
case .shouldBlockConnection:
|
|
125
|
+
NSLog("⛔️ SSL Pinning failed for domain: %@", notedHostname)
|
|
126
|
+
NSLog("Policy details: %@", policy)
|
|
127
|
+
case .shouldAllowConnection:
|
|
128
|
+
NSLog("✅ SSL Pinning succeeded for domain: %@", notedHostname)
|
|
129
|
+
default:
|
|
130
|
+
NSLog("⚠️ Unexpected SSL Pinning result for domain: %@", notedHostname)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
NSLog("✅ TrustKit initialized with config: %@", trustKitConfig)
|
|
135
|
+
resolve([
|
|
136
|
+
"message": "SSL Pinning initialized successfully",
|
|
137
|
+
"domains": Array(pinnedDomains.keys)
|
|
138
|
+
])
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
} catch let error as SSLPinningError {
|
|
142
|
+
NSLog("❌ SSL Pinning Error: %@", error.message)
|
|
143
|
+
reject("SSL_PINNING_ERROR", error.message, error)
|
|
144
|
+
} catch {
|
|
145
|
+
NSLog("❌ Unexpected Error: %@", error.localizedDescription)
|
|
146
|
+
reject("SSL_PINNING_ERROR", "Unexpected error during SSL pinning initialization", error)
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
NSLog("⚠️ SSL Pinning is disabled",isSSLPinningEnabled)
|
|
150
|
+
resolve([
|
|
151
|
+
"message": "SSL Pinning is disabled",
|
|
152
|
+
"domains": [],
|
|
153
|
+
"isSSLPinningEnabled": isSSLPinningEnabled
|
|
154
|
+
])
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@objc
|
|
159
|
+
func setUseSSLPinning(_ usePinning: Bool) {
|
|
160
|
+
userDefaults.set(usePinning, forKey: useSSLPinningKey)
|
|
161
|
+
userDefaults.synchronize()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@objc
|
|
165
|
+
func getUseSSLPinning(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
|
|
166
|
+
let usePinning = userDefaults.bool(forKey: useSSLPinningKey)
|
|
167
|
+
resolve(usePinning)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.setUseSSLPinning = exports.initializeSslPinning = exports.getUseSSLPinning = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
const LINKING_ERROR = `The package 'react-native-ssl-manager' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
|
|
9
|
+
ios: "- You have run 'pod install'\n",
|
|
10
|
+
default: ''
|
|
11
|
+
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
|
12
|
+
const UseSslPinning = _reactNative.NativeModules.UseSslPinning ? _reactNative.NativeModules.UseSslPinning : new Proxy({}, {
|
|
13
|
+
get() {
|
|
14
|
+
throw new Error(LINKING_ERROR);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const setUseSSLPinning = usePinning => {
|
|
18
|
+
UseSslPinning.setUseSSLPinning(usePinning);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Retrieves the current state of SSL pinning usage.
|
|
23
|
+
*
|
|
24
|
+
* @returns A promise that resolves to a boolean indicating whether SSL pinning is being used.
|
|
25
|
+
*/
|
|
26
|
+
exports.setUseSSLPinning = setUseSSLPinning;
|
|
27
|
+
const getUseSSLPinning = async () => {
|
|
28
|
+
return await UseSslPinning.getUseSSLPinning();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Initializes SSL pinning with the provided configuration.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} configJsonString - The JSON string containing the SSL pinning configuration.
|
|
35
|
+
* @returns {Promise<any>} A promise that resolves when the SSL pinning is initialized.
|
|
36
|
+
*/
|
|
37
|
+
exports.getUseSSLPinning = getUseSSLPinning;
|
|
38
|
+
const initializeSslPinning = async configJsonString => {
|
|
39
|
+
return await UseSslPinning.initializeSslPinning(configJsonString);
|
|
40
|
+
};
|
|
41
|
+
exports.initializeSslPinning = initializeSslPinning;
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","UseSslPinning","NativeModules","Proxy","get","Error","setUseSSLPinning","usePinning","exports","getUseSSLPinning","initializeSslPinning","configJsonString"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAMC,aAAa,GACjB,mFAAmF,GACnFC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,aAAa,GAAGC,0BAAa,CAACD,aAAa,GAC7CC,0BAAa,CAACD,aAAa,GAC3B,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACT,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAEE,MAAMU,gBAAgB,GAAIC,UAAmB,IAAW;EAC7DN,aAAa,CAACK,gBAAgB,CAACC,UAAU,CAAC;AAC5C,CAAC;;AAED;AACA;AACA;AACA;AACA;AAJAC,OAAA,CAAAF,gBAAA,GAAAA,gBAAA;AAKO,MAAMG,gBAAgB,GAAG,MAAAA,CAAA,KAA8B;EAC5D,OAAO,MAAMR,aAAa,CAACQ,gBAAgB,CAAC,CAAC;AAC/C,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AALAD,OAAA,CAAAC,gBAAA,GAAAA,gBAAA;AAMO,MAAMC,oBAAoB,GAAG,MAClCC,gBAAwB,IACP;EACjB,OAAO,MAAMV,aAAa,CAACS,oBAAoB,CAACC,gBAAgB,CAAC;AACnE,CAAC;AAACH,OAAA,CAAAE,oBAAA,GAAAA,oBAAA","ignoreList":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { NativeModules, Platform } from 'react-native';
|
|
2
|
+
const LINKING_ERROR = `The package 'react-native-ssl-manager' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
|
|
3
|
+
ios: "- You have run 'pod install'\n",
|
|
4
|
+
default: ''
|
|
5
|
+
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
|
6
|
+
const UseSslPinning = NativeModules.UseSslPinning ? NativeModules.UseSslPinning : new Proxy({}, {
|
|
7
|
+
get() {
|
|
8
|
+
throw new Error(LINKING_ERROR);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
export const setUseSSLPinning = usePinning => {
|
|
12
|
+
UseSslPinning.setUseSSLPinning(usePinning);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Retrieves the current state of SSL pinning usage.
|
|
17
|
+
*
|
|
18
|
+
* @returns A promise that resolves to a boolean indicating whether SSL pinning is being used.
|
|
19
|
+
*/
|
|
20
|
+
export const getUseSSLPinning = async () => {
|
|
21
|
+
return await UseSslPinning.getUseSSLPinning();
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Initializes SSL pinning with the provided configuration.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} configJsonString - The JSON string containing the SSL pinning configuration.
|
|
28
|
+
* @returns {Promise<any>} A promise that resolves when the SSL pinning is initialized.
|
|
29
|
+
*/
|
|
30
|
+
export const initializeSslPinning = async configJsonString => {
|
|
31
|
+
return await UseSslPinning.initializeSslPinning(configJsonString);
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeModules","Platform","LINKING_ERROR","select","ios","default","UseSslPinning","Proxy","get","Error","setUseSSLPinning","usePinning","getUseSSLPinning","initializeSslPinning","configJsonString"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AAEtD,MAAMC,aAAa,GACjB,mFAAmF,GACnFD,QAAQ,CAACE,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,aAAa,GAAGN,aAAa,CAACM,aAAa,GAC7CN,aAAa,CAACM,aAAa,GAC3B,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACP,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAEL,OAAO,MAAMQ,gBAAgB,GAAIC,UAAmB,IAAW;EAC7DL,aAAa,CAACI,gBAAgB,CAACC,UAAU,CAAC;AAC5C,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,gBAAgB,GAAG,MAAAA,CAAA,KAA8B;EAC5D,OAAO,MAAMN,aAAa,CAACM,gBAAgB,CAAC,CAAC;AAC/C,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,oBAAoB,GAAG,MAClCC,gBAAwB,IACP;EACjB,OAAO,MAAMR,aAAa,CAACO,oBAAoB,CAACC,gBAAgB,CAAC;AACnE,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const setUseSSLPinning: (usePinning: boolean) => void;
|
|
2
|
+
/**
|
|
3
|
+
* Retrieves the current state of SSL pinning usage.
|
|
4
|
+
*
|
|
5
|
+
* @returns A promise that resolves to a boolean indicating whether SSL pinning is being used.
|
|
6
|
+
*/
|
|
7
|
+
export declare const getUseSSLPinning: () => Promise<boolean>;
|
|
8
|
+
/**
|
|
9
|
+
* Initializes SSL pinning with the provided configuration.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} configJsonString - The JSON string containing the SSL pinning configuration.
|
|
12
|
+
* @returns {Promise<any>} A promise that resolves when the SSL pinning is initialized.
|
|
13
|
+
*/
|
|
14
|
+
export declare const initializeSslPinning: (configJsonString: string) => Promise<any>;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAmBA,eAAO,MAAM,gBAAgB,eAAgB,OAAO,KAAG,IAEtD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAa,QAAQ,OAAO,CAExD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,qBACb,MAAM,KACvB,QAAQ,GAAG,CAEb,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-ssl-manager",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React Native SSL Pinning provides seamless SSL certificate pinning integration for enhanced network security in React Native apps. This module enables developers to easily implement and manage certificate pinning, protecting applications against man-in-the-middle (MITM) attacks. With dynamic configuration options and the ability to toggle SSL pinning, it's particularly useful for development and testing scenarios.",
|
|
5
|
+
"main": "lib/commonjs/index",
|
|
6
|
+
"module": "lib/module/index",
|
|
7
|
+
"types": "lib/typescript/src/index.d.ts",
|
|
8
|
+
"react-native": {
|
|
9
|
+
"android": {
|
|
10
|
+
"source": "android/src/main/java/com/usesslpinning/UseSslPinningPackage.kt"
|
|
11
|
+
},
|
|
12
|
+
"ios": {
|
|
13
|
+
"podspec": "react-native-ssl-manager.podspec"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"source": "src/index",
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"lib",
|
|
20
|
+
"!**/__tests__",
|
|
21
|
+
"!**/__fixtures__",
|
|
22
|
+
"!**/__mocks__",
|
|
23
|
+
"android",
|
|
24
|
+
"ios",
|
|
25
|
+
"cpp",
|
|
26
|
+
"*.podspec",
|
|
27
|
+
"!lib/typescript/example",
|
|
28
|
+
"!ios/build",
|
|
29
|
+
"!android/build",
|
|
30
|
+
"!android/gradle",
|
|
31
|
+
"!android/gradlew",
|
|
32
|
+
"!android/gradlew.bat",
|
|
33
|
+
"!android/local.properties",
|
|
34
|
+
"!**/.*"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"example": "yarn workspace react-native-ssl-manager-example",
|
|
38
|
+
"test": "jest",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
41
|
+
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
42
|
+
"prepare": "bob build",
|
|
43
|
+
"release": "release-it"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"react-native",
|
|
47
|
+
"ios",
|
|
48
|
+
"android"
|
|
49
|
+
],
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/huytdps13400/react-native-ssl-manager.git"
|
|
53
|
+
},
|
|
54
|
+
"author": "Trần Đình Huy <kingonwork@gmail.com> (https://github.com/huytdps13400)",
|
|
55
|
+
"license": "MIT",
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/huytdps13400/react-native-ssl-manager/issues"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://github.com/huytdps13400/react-native-ssl-manager#readme",
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"registry": "https://registry.npmjs.org/",
|
|
62
|
+
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@commitlint/config-conventional": "^17.0.2",
|
|
66
|
+
"@evilmartians/lefthook": "^1.5.0",
|
|
67
|
+
"@react-native/eslint-config": "^0.73.1",
|
|
68
|
+
"@release-it/conventional-changelog": "^5.0.0",
|
|
69
|
+
"@types/jest": "^29.5.5",
|
|
70
|
+
"@types/react": "^18.2.44",
|
|
71
|
+
"commitlint": "^17.0.2",
|
|
72
|
+
"del-cli": "^5.1.0",
|
|
73
|
+
"eslint": "^8.51.0",
|
|
74
|
+
"eslint-config-prettier": "^9.0.0",
|
|
75
|
+
"eslint-plugin-prettier": "^5.0.1",
|
|
76
|
+
"jest": "^29.7.0",
|
|
77
|
+
"prettier": "^3.0.3",
|
|
78
|
+
"react": "18.2.0",
|
|
79
|
+
"react-native": "0.73.6",
|
|
80
|
+
"react-native-builder-bob": "^0.20.0",
|
|
81
|
+
"release-it": "^15.0.0",
|
|
82
|
+
"turbo": "^1.10.7",
|
|
83
|
+
"typescript": "5.1.x"
|
|
84
|
+
},
|
|
85
|
+
"resolutions": {
|
|
86
|
+
"@types/react": "^18.2.44"
|
|
87
|
+
},
|
|
88
|
+
"peerDependencies": {
|
|
89
|
+
"react": "*",
|
|
90
|
+
"react-native": "*"
|
|
91
|
+
},
|
|
92
|
+
"workspaces": [
|
|
93
|
+
"example"
|
|
94
|
+
],
|
|
95
|
+
"packageManager": "yarn@3.6.1",
|
|
96
|
+
"jest": {
|
|
97
|
+
"preset": "react-native",
|
|
98
|
+
"modulePathIgnorePatterns": [
|
|
99
|
+
"<rootDir>/example/node_modules",
|
|
100
|
+
"<rootDir>/lib/"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"commitlint": {
|
|
104
|
+
"extends": [
|
|
105
|
+
"@commitlint/config-conventional"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"release-it": {
|
|
109
|
+
"git": {
|
|
110
|
+
"commitMessage": "chore: release ${version}",
|
|
111
|
+
"tagName": "v${version}"
|
|
112
|
+
},
|
|
113
|
+
"npm": {
|
|
114
|
+
"publish": true
|
|
115
|
+
},
|
|
116
|
+
"github": {
|
|
117
|
+
"release": true
|
|
118
|
+
},
|
|
119
|
+
"plugins": {
|
|
120
|
+
"@release-it/conventional-changelog": {
|
|
121
|
+
"preset": "angular"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"eslintConfig": {
|
|
126
|
+
"root": true,
|
|
127
|
+
"extends": [
|
|
128
|
+
"@react-native",
|
|
129
|
+
"prettier"
|
|
130
|
+
],
|
|
131
|
+
"rules": {
|
|
132
|
+
"prettier/prettier": [
|
|
133
|
+
"error",
|
|
134
|
+
{
|
|
135
|
+
"quoteProps": "consistent",
|
|
136
|
+
"singleQuote": true,
|
|
137
|
+
"tabWidth": 2,
|
|
138
|
+
"trailingComma": "es5",
|
|
139
|
+
"useTabs": false
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"eslintIgnore": [
|
|
145
|
+
"node_modules/",
|
|
146
|
+
"lib/"
|
|
147
|
+
],
|
|
148
|
+
"prettier": {
|
|
149
|
+
"quoteProps": "consistent",
|
|
150
|
+
"singleQuote": true,
|
|
151
|
+
"tabWidth": 2,
|
|
152
|
+
"trailingComma": "es5",
|
|
153
|
+
"useTabs": false
|
|
154
|
+
},
|
|
155
|
+
"react-native-builder-bob": {
|
|
156
|
+
"source": "src",
|
|
157
|
+
"output": "lib",
|
|
158
|
+
"targets": [
|
|
159
|
+
"commonjs",
|
|
160
|
+
"module",
|
|
161
|
+
[
|
|
162
|
+
"typescript",
|
|
163
|
+
{
|
|
164
|
+
"project": "tsconfig.build.json"
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
"dependencies": {
|
|
170
|
+
"postinstall": "^0.10.3"
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
|
5
|
+
|
|
6
|
+
Pod::Spec.new do |s|
|
|
7
|
+
s.name = "react-native-ssl-manager"
|
|
8
|
+
s.version = package["version"]
|
|
9
|
+
s.summary = package["description"]
|
|
10
|
+
s.homepage = package["homepage"]
|
|
11
|
+
s.license = package["license"]
|
|
12
|
+
s.authors = package["author"]
|
|
13
|
+
|
|
14
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
15
|
+
s.source = { :git => "https://github.com/huytdps13400/react-native-ssl-manager.git", :tag => "#{s.version}" }
|
|
16
|
+
|
|
17
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
18
|
+
|
|
19
|
+
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
|
20
|
+
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|
|
21
|
+
if respond_to?(:install_modules_dependencies, true)
|
|
22
|
+
install_modules_dependencies(s)
|
|
23
|
+
else
|
|
24
|
+
s.dependency "React-Core"
|
|
25
|
+
|
|
26
|
+
# Don't install the dependencies when we run `pod install` in the old architecture.
|
|
27
|
+
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
|
|
28
|
+
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
|
29
|
+
s.pod_target_xcconfig = {
|
|
30
|
+
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
|
31
|
+
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
|
32
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
|
33
|
+
}
|
|
34
|
+
s.dependency "React-Codegen"
|
|
35
|
+
s.dependency "RCT-Folly"
|
|
36
|
+
s.dependency "RCTRequired"
|
|
37
|
+
s.dependency "RCTTypeSafety"
|
|
38
|
+
s.dependency "ReactCommon/turbomodule/core"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
s.dependency "TrustKit"
|
|
43
|
+
end
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { NativeModules, Platform } from 'react-native';
|
|
2
|
+
|
|
3
|
+
const LINKING_ERROR =
|
|
4
|
+
`The package 'react-native-ssl-manager' doesn't seem to be linked. Make sure: \n\n` +
|
|
5
|
+
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
|
6
|
+
'- You rebuilt the app after installing the package\n' +
|
|
7
|
+
'- You are not using Expo Go\n';
|
|
8
|
+
|
|
9
|
+
const UseSslPinning = NativeModules.UseSslPinning
|
|
10
|
+
? NativeModules.UseSslPinning
|
|
11
|
+
: new Proxy(
|
|
12
|
+
{},
|
|
13
|
+
{
|
|
14
|
+
get() {
|
|
15
|
+
throw new Error(LINKING_ERROR);
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export const setUseSSLPinning = (usePinning: boolean): void => {
|
|
21
|
+
UseSslPinning.setUseSSLPinning(usePinning);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Retrieves the current state of SSL pinning usage.
|
|
26
|
+
*
|
|
27
|
+
* @returns A promise that resolves to a boolean indicating whether SSL pinning is being used.
|
|
28
|
+
*/
|
|
29
|
+
export const getUseSSLPinning = async (): Promise<boolean> => {
|
|
30
|
+
return await UseSslPinning.getUseSSLPinning();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initializes SSL pinning with the provided configuration.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} configJsonString - The JSON string containing the SSL pinning configuration.
|
|
37
|
+
* @returns {Promise<any>} A promise that resolves when the SSL pinning is initialized.
|
|
38
|
+
*/
|
|
39
|
+
export const initializeSslPinning = async (
|
|
40
|
+
configJsonString: string
|
|
41
|
+
): Promise<any> => {
|
|
42
|
+
return await UseSslPinning.initializeSslPinning(configJsonString);
|
|
43
|
+
};
|