@cap-kit/tls-fingerprint 8.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.
Files changed (51) hide show
  1. package/CapKitTlsFingerprint.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +25 -0
  4. package/README.md +427 -0
  5. package/android/build.gradle +103 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/java/io/capkit/settings/TLSFingerprintImpl.kt +333 -0
  8. package/android/src/main/java/io/capkit/settings/TLSFingerprintPlugin.kt +342 -0
  9. package/android/src/main/java/io/capkit/settings/config/TLSFingerprintConfig.kt +102 -0
  10. package/android/src/main/java/io/capkit/settings/error/TLSFingerprintError.kt +114 -0
  11. package/android/src/main/java/io/capkit/settings/error/TLSFingerprintErrorMessages.kt +27 -0
  12. package/android/src/main/java/io/capkit/settings/logger/TLSFingerprintLogger.kt +85 -0
  13. package/android/src/main/java/io/capkit/settings/model/TLSFingerprintResultModel.kt +32 -0
  14. package/android/src/main/java/io/capkit/settings/utils/TLSFingerprintUtils.kt +91 -0
  15. package/android/src/main/res/.gitkeep +0 -0
  16. package/dist/cli/fingerprint.js +163 -0
  17. package/dist/cli/fingerprint.js.map +1 -0
  18. package/dist/docs.json +386 -0
  19. package/dist/esm/cli/fingerprint.d.ts +1 -0
  20. package/dist/esm/cli/fingerprint.js +161 -0
  21. package/dist/esm/cli/fingerprint.js.map +1 -0
  22. package/dist/esm/definitions.d.ts +244 -0
  23. package/dist/esm/definitions.js +42 -0
  24. package/dist/esm/definitions.js.map +1 -0
  25. package/dist/esm/index.d.ts +13 -0
  26. package/dist/esm/index.js +11 -0
  27. package/dist/esm/index.js.map +1 -0
  28. package/dist/esm/version.d.ts +1 -0
  29. package/dist/esm/version.js +3 -0
  30. package/dist/esm/version.js.map +1 -0
  31. package/dist/esm/web.d.ts +33 -0
  32. package/dist/esm/web.js +47 -0
  33. package/dist/esm/web.js.map +1 -0
  34. package/dist/plugin.cjs +107 -0
  35. package/dist/plugin.cjs.map +1 -0
  36. package/dist/plugin.js +110 -0
  37. package/dist/plugin.js.map +1 -0
  38. package/ios/Sources/TLSFingerprintPlugin/TLSFingerprintDelegate.swift +365 -0
  39. package/ios/Sources/TLSFingerprintPlugin/TLSFingerprintImpl.swift +275 -0
  40. package/ios/Sources/TLSFingerprintPlugin/TLSFingerprintPlugin.swift +219 -0
  41. package/ios/Sources/TLSFingerprintPlugin/Version.swift +16 -0
  42. package/ios/Sources/TLSFingerprintPlugin/config/TLSFingerprintConfig.swift +114 -0
  43. package/ios/Sources/TLSFingerprintPlugin/error/TLSFingerprintError.swift +107 -0
  44. package/ios/Sources/TLSFingerprintPlugin/error/TLSFingerprintErrorMessages.swift +30 -0
  45. package/ios/Sources/TLSFingerprintPlugin/logger/TLSFingerprintLogger.swift +69 -0
  46. package/ios/Sources/TLSFingerprintPlugin/model/TLSFingerprintResult.swift +76 -0
  47. package/ios/Sources/TLSFingerprintPlugin/utils/TLSFingerprintUtils.swift +79 -0
  48. package/ios/Tests/TLSFingerprintPluginTests/TLSFingerprintPluginTests.swift +15 -0
  49. package/package.json +131 -0
  50. package/scripts/chmod.mjs +34 -0
  51. package/scripts/sync-version.mjs +68 -0
@@ -0,0 +1,17 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapKitTlsFingerprint'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.ios.deployment_target = '15.0'
15
+ s.dependency 'Capacitor'
16
+ s.swift_version = '5.9'
17
+ end
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CapKit Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/Package.swift ADDED
@@ -0,0 +1,25 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "CapKitTlsFingerprint",
6
+ platforms: [.iOS(.v15)],
7
+ products: [
8
+ .library(
9
+ name: "CapKitTlsFingerprint",
10
+ targets: ["TlsFingerprintPlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.1.0")
14
+ ],
15
+ targets: [
16
+ .target(
17
+ name: "TlsFingerprintPlugin",
18
+ dependencies: [
19
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
+ .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ ],
22
+ path: "ios/Sources/TLSFingerprintPlugin"
23
+ )
24
+ ]
25
+ )
package/README.md ADDED
@@ -0,0 +1,427 @@
1
+ <p align="center">
2
+ <img
3
+ src="https://raw.githubusercontent.com/cap-kit/capacitor-plugins/main/assets/logo.png"
4
+ alt="CapKit Logo"
5
+ width="128"
6
+ />
7
+ </p>
8
+
9
+ <h3 align="center">TLS Fingerprinting</h3>
10
+ <p align="center">
11
+ <strong>
12
+ <code>@cap-kit/tls-fingerprint</code>
13
+ </strong>
14
+ </p>
15
+
16
+ <p align="center">
17
+ Runtime TLS leaf certificate SHA-256 fingerprint validation for Capacitor applications. This plugin establishes a TLS
18
+ connection to a remote HTTPS endpoint, extracts the server’s leaf certificate, computes its SHA-256 fingerprint, and
19
+ compares it against one or more expected fingerprints defined at runtime or via static configuration. It performs
20
+ fingerprint equality validation only and does not override or modify the system trust store.
21
+ </p>
22
+
23
+ <p align="center">
24
+ <a href="https://www.npmjs.com/package/@cap-kit/tls-fingerprint">
25
+ <img src="https://img.shields.io/npm/v/@cap-kit/tls-fingerprint?color=blue&label=npm&logo=npm&style=flat-square" alt="npm version">
26
+ </a>
27
+ <a href="https://github.com/cap-kit/capacitor-plugins/actions">
28
+ <img src="https://img.shields.io/github/actions/workflow/status/cap-kit/capacitor-plugins/ci.yml?branch=main&label=CI&logo=github&style=flat-square" alt="CI Status" />
29
+ </a>
30
+ <a href="https://capacitorjs.com/">
31
+ <img src="https://img.shields.io/badge/Capacitor-Plugin-blue?logo=capacitor&style=flat-square" alt="Capacitor Plugin">
32
+ </a>
33
+ <a href="https://www.npmjs.com/package/@cap-kit/tls-fingerprint">
34
+ <img src="https://img.shields.io/npm/dm/@cap-kit/tls-fingerprint?style=flat-square" alt="Downloads" />
35
+ </a>
36
+ <a href="./LICENSE">
37
+ <img src="https://img.shields.io/npm/l/@cap-kit/tls-fingerprint?style=flat-square&logo=open-source-initiative&logoColor=white&color=green" alt="License" />
38
+ </a>
39
+ <img src="https://img.shields.io/maintenance/yes/2026?style=flat-square" alt="Maintained" />
40
+ </p>
41
+ <br>
42
+
43
+ ## Overview
44
+
45
+ This Capacitor plugin validates the SHA-256 fingerprint of a server's TLS leaf certificate at runtime.
46
+
47
+ ### What this plugin does
48
+
49
+ - Extracts the leaf certificate from an HTTPS connection
50
+ - Computes its SHA-256 fingerprint
51
+ - Compares against expected fingerprints provided at runtime or in static configuration
52
+
53
+ ### What this plugin does NOT do
54
+
55
+ - It does NOT perform anchor-based certificate pinning
56
+ - It does NOT load local certificate files
57
+ - It does NOT modify or override the system trust store
58
+ - It does NOT validate the certificate chain
59
+
60
+ ### Platform Support
61
+
62
+ | Platform | Status |
63
+ | -------- | --------------------------------------------------- |
64
+ | iOS | Supported |
65
+ | Android | Supported |
66
+ | Web | Unsupported - methods reject with `unimplemented()` |
67
+
68
+ ---
69
+
70
+ ## Install
71
+
72
+ ```bash
73
+ pnpm add @cap-kit/tls-fingerprint
74
+ # or
75
+ npm install @cap-kit/tls-fingerprint
76
+ # or
77
+ yarn add @cap-kit/tls-fingerprint
78
+ # then run:
79
+ npx cap sync
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Obtaining Fingerprints
85
+
86
+ To use this plugin, you need the SHA-256 fingerprint of the server certificate.
87
+
88
+ ### Method 1 — Using OpenSSL
89
+
90
+ ```bash
91
+ openssl x509 -noout -fingerprint -sha256 -inform pem -in /path/to/cert.pem
92
+ ```
93
+
94
+ Example output:
95
+
96
+ ```bash
97
+ SHA256 Fingerprint=EF:BA:26:D8:C1:CE:37:79:AC:77:63:0A:90:F8:21:63:A3:D6:89:2E:D6:AF:EE:40:86:72:CF:19:EB:A7:A3:62
98
+ ```
99
+
100
+ > The plugin normalizes fingerprints to lowercase hex with no separators.
101
+ > For example, `EF:BA:26:...` becomes `efba26...`
102
+
103
+ ### Method 2 — Using the Built-in CLI Tool
104
+
105
+ This project includes a CLI utility to retrieve certificates from remote servers:
106
+
107
+ ```bash
108
+ npx cap-kit-tls-fingerprint example.com
109
+ ```
110
+
111
+ ```bash
112
+ npx cap-kit-tls-fingerprint example.com api.example.com --mode multi
113
+ ```
114
+
115
+ The CLI is for development-time certificate inspection only. It does not perform runtime validation.
116
+
117
+ ---
118
+
119
+ ## Configuration
120
+
121
+ <docgen-config>
122
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
123
+
124
+ Configuration options for the TLSFingerprint plugin.
125
+
126
+ | Prop | Type | Description | Default | Since |
127
+ | --------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
128
+ | **`verboseLogging`** | <code>boolean</code> | Enables verbose native logging. When enabled, additional debug information is printed to the native console (Logcat on Android, Xcode on iOS). This option affects native logging behavior only and has no impact on the JavaScript API. | <code>false</code> | 8.0.0 |
129
+ | **`fingerprint`** | <code>string</code> | Default fingerprint used by `checkCertificate()` when `options.fingerprint` is not provided at runtime. | | 8.0.0 |
130
+ | **`fingerprints`** | <code>string[]</code> | Default fingerprints used by `checkCertificates()` when `options.fingerprints` is not provided at runtime. | | 8.0.0 |
131
+ | **`excludedDomains`** | <code>string[]</code> | Domains to bypass. Matches exact domain or subdomains. Do not include schemes or paths. | | 8.0.0 |
132
+
133
+ ### Examples
134
+
135
+ In `capacitor.config.json`:
136
+
137
+ ```json
138
+ {
139
+ "plugins": {
140
+ "TLSFingerprint": {
141
+ "verboseLogging": true,
142
+ "fingerprint": "50:4B:A1:B5:48:96:71:F3:9F:87:7E:0A:09:FD:3E:1B:C0:4F:AA:9F:FC:83:3E:A9:3A:00:78:88:F8:BA:60:26",
143
+ "fingerprints": [
144
+ "50:4B:A1:B5:48:96:71:F3:9F:87:7E:0A:09:FD:3E:1B:C0:4F:AA:9F:FC:83:3E:A9:3A:00:78:88:F8:BA:60:26"
145
+ ]
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ In `capacitor.config.ts`:
152
+
153
+ ```ts
154
+ /// <reference types="@cap-kit/tls-fingerprint" />
155
+
156
+ import { CapacitorConfig } from '@capacitor/cli';
157
+
158
+ const config: CapacitorConfig = {
159
+ plugins: {
160
+ TLSFingerprint: {
161
+ verboseLogging: true,
162
+ fingerprint: '50:4B:A1:B5:48:96:71:F3:9F:87:7E:0A:09:FD:3E:1B:C0:4F:AA:9F:FC:83:3E:A9:3A:00:78:88:F8:BA:60:26',
163
+ fingerprints: ["50:4B:A1:B5:48:96:71:F3:9F:87:7E:0A:09:FD:3E:1B:C0:4F:AA:9F:FC:83:3E:A9:3A:00:78:88:F8:BA:60:26"],
164
+ },
165
+ },
166
+ };
167
+
168
+ export default config;
169
+ ```
170
+
171
+ </docgen-config>
172
+
173
+ > **Note:** All network operations have a 10-second timeout. If the server does not respond within this time, the Promise is rejected with `TLSFingerprintErrorCode.TIMEOUT`.
174
+
175
+ ---
176
+
177
+ ## API
178
+
179
+ <docgen-index>
180
+
181
+ * [`checkCertificate(...)`](#checkcertificate)
182
+ * [`checkCertificates(...)`](#checkcertificates)
183
+ * [`getPluginVersion()`](#getpluginversion)
184
+ * [Interfaces](#interfaces)
185
+ * [Enums](#enums)
186
+
187
+ </docgen-index>
188
+
189
+ <docgen-api>
190
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
191
+
192
+ TLS Fingerprint Capacitor Plugin interface.
193
+
194
+ ### checkCertificate(...)
195
+
196
+ ```typescript
197
+ checkCertificate(options: TLSFingerprintOptions) => Promise<TLSFingerprintResult>
198
+ ```
199
+
200
+ Checks the SSL certificate of a server using a single fingerprint.
201
+
202
+ | Param | Type |
203
+ | ------------- | ----------------------------------------------------------------------- |
204
+ | **`options`** | <code><a href="#tlsfingerprintoptions">TLSFingerprintOptions</a></code> |
205
+
206
+ **Returns:** <code>Promise&lt;<a href="#tlsfingerprintresult">TLSFingerprintResult</a>&gt;</code>
207
+
208
+ **Since:** 8.0.0
209
+
210
+ --------------------
211
+
212
+
213
+ ### checkCertificates(...)
214
+
215
+ ```typescript
216
+ checkCertificates(options: TLSFingerprintMultiOptions) => Promise<TLSFingerprintResult>
217
+ ```
218
+
219
+ Checks the SSL certificate of a server using multiple allowed fingerprints.
220
+
221
+ | Param | Type |
222
+ | ------------- | --------------------------------------------------------------------------------- |
223
+ | **`options`** | <code><a href="#tlsfingerprintmultioptions">TLSFingerprintMultiOptions</a></code> |
224
+
225
+ **Returns:** <code>Promise&lt;<a href="#tlsfingerprintresult">TLSFingerprintResult</a>&gt;</code>
226
+
227
+ **Since:** 8.0.0
228
+
229
+ --------------------
230
+
231
+
232
+ ### getPluginVersion()
233
+
234
+ ```typescript
235
+ getPluginVersion() => Promise<PluginVersionResult>
236
+ ```
237
+
238
+ Returns the native plugin version.
239
+
240
+ The returned version corresponds to the native implementation
241
+ bundled with the application.
242
+
243
+ **Returns:** <code>Promise&lt;<a href="#pluginversionresult">PluginVersionResult</a>&gt;</code>
244
+
245
+ **Since:** 8.0.0
246
+
247
+ #### Example
248
+
249
+ ```ts
250
+ const { version } = await TLSFingerprint.getPluginVersion();
251
+ ```
252
+
253
+ --------------------
254
+
255
+
256
+ ### Interfaces
257
+
258
+
259
+ #### TLSFingerprintResult
260
+
261
+ Result returned by an TLS fingerprint operation.
262
+
263
+ This object is returned for ALL outcomes:
264
+ - Success: `fingerprintMatched: true`
265
+ - Mismatch: `fingerprintMatched: false` with error info (RESOLVED, not rejected)
266
+
267
+ Only operation failures (invalid input, config missing, network errors,
268
+ timeout, internal errors) reject the Promise.
269
+
270
+ | Prop | Type | Description |
271
+ | ------------------------ | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
272
+ | **`actualFingerprint`** | <code>string</code> | The actual SHA-256 fingerprint of the server certificate. Present in fingerprint and excluded modes. |
273
+ | **`fingerprintMatched`** | <code>boolean</code> | Indicates whether the certificate validation succeeded. - true → Pinning passed - false → Pinning failed |
274
+ | **`matchedFingerprint`** | <code>string</code> | The fingerprint that successfully matched, if any. |
275
+ | **`excludedDomain`** | <code>boolean</code> | Indicates that TLS fingerprint was skipped because the request host matched an excluded domain. |
276
+ | **`mode`** | <code>'fingerprint' \| 'excluded'</code> | Indicates which pinning mode was used. - "fingerprint" - "excluded" |
277
+ | **`error`** | <code>string</code> | Human-readable error message when pinning fails. Present when `fingerprintMatched: false`. |
278
+ | **`errorCode`** | <code><a href="#tlsfingerprinterrorcode">TLSFingerprintErrorCode</a></code> | Standardized error code aligned with <a href="#tlsfingerprinterrorcode">TLSFingerprintErrorCode</a>. |
279
+
280
+
281
+ #### TLSFingerprintOptions
282
+
283
+ Options for checking a single SSL certificate.
284
+
285
+ | Prop | Type | Description |
286
+ | ----------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
287
+ | **`url`** | <code>string</code> | HTTPS URL of the server whose SSL certificate must be checked. This value is REQUIRED and cannot be provided via configuration. |
288
+ | **`fingerprint`** | <code>string</code> | Expected SHA-256 fingerprint of the certificate. Resolution order: 1. `options.fingerprint` (runtime) 2. `plugins.TLSFingerprint.fingerprint` (config) If neither is provided, the Promise is rejected with `TLSFingerprintErrorCode.UNAVAILABLE`. |
289
+
290
+
291
+ #### TLSFingerprintMultiOptions
292
+
293
+ Options for checking an SSL certificate using multiple allowed fingerprints.
294
+
295
+ | Prop | Type | Description |
296
+ | ------------------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
297
+ | **`url`** | <code>string</code> | HTTPS URL of the server whose SSL certificate must be checked. This value is REQUIRED and cannot be provided via configuration. |
298
+ | **`fingerprints`** | <code>string[]</code> | Expected SHA-256 fingerprints of the certificate. Resolution order: 1. `options.fingerprints` (runtime) 2. `plugins.TLSFingerprint.fingerprints` (config) If neither is provided, the Promise is rejected with `TLSFingerprintErrorCode.UNAVAILABLE`. |
299
+
300
+
301
+ #### PluginVersionResult
302
+
303
+ Result returned by the getPluginVersion method.
304
+
305
+ | Prop | Type | Description |
306
+ | ------------- | ------------------- | ---------------------------------------- |
307
+ | **`version`** | <code>string</code> | The native version string of the plugin. |
308
+
309
+
310
+ ### Enums
311
+
312
+
313
+ #### TLSFingerprintErrorCode
314
+
315
+ | Members | Value | Description |
316
+ | ----------------------- | -------------------------------- | ------------------------------------------------------------------------------ |
317
+ | **`UNAVAILABLE`** | <code>'UNAVAILABLE'</code> | Required data is missing or the feature is not available. |
318
+ | **`CANCELLED`** | <code>'CANCELLED'</code> | The user cancelled an interactive flow. |
319
+ | **`PERMISSION_DENIED`** | <code>'PERMISSION_DENIED'</code> | The user denied a required permission or the feature is disabled. |
320
+ | **`INIT_FAILED`** | <code>'INIT_FAILED'</code> | The TLS fingerprint operation failed due to a runtime or initialization error. |
321
+ | **`INVALID_INPUT`** | <code>'INVALID_INPUT'</code> | The input provided to the plugin method is invalid, missing, or malformed. |
322
+ | **`UNKNOWN_TYPE`** | <code>'UNKNOWN_TYPE'</code> | Invalid or unsupported input was provided. |
323
+ | **`NOT_FOUND`** | <code>'NOT_FOUND'</code> | The requested resource does not exist. |
324
+ | **`CONFLICT`** | <code>'CONFLICT'</code> | The operation conflicts with the current state. |
325
+ | **`TIMEOUT`** | <code>'TIMEOUT'</code> | The operation did not complete within the expected time. |
326
+ | **`PINNING_FAILED`** | <code>'PINNING_FAILED'</code> | The server certificate fingerprint did not match any expected fingerprint. |
327
+ | **`EXCLUDED_DOMAIN`** | <code>'EXCLUDED_DOMAIN'</code> | The request host matched an excluded domain. |
328
+ | **`NETWORK_ERROR`** | <code>'NETWORK_ERROR'</code> | Network connectivity or TLS handshake error. |
329
+ | **`SSL_ERROR`** | <code>'SSL_ERROR'</code> | SSL/TLS specific error (certificate expired, handshake failure, etc.). |
330
+
331
+ </docgen-api>
332
+
333
+ ---
334
+
335
+ ## Security Considerations
336
+
337
+ This plugin validates fingerprint equality only.
338
+
339
+ ### What this means
340
+
341
+ - The plugin compares the server's leaf certificate SHA-256 fingerprint against expected values
342
+ - It does NOT replace TLS validation
343
+ - It does NOT override trust evaluation
344
+ - Expired or self-signed certificates will validate if the fingerprint matches
345
+
346
+ ### Limitations
347
+
348
+ - Fingerprint validation requires active maintenance
349
+ - Certificate rotation requires configuration updates
350
+ - Misconfiguration may result in loss of network connectivity
351
+
352
+ This plugin is provided as-is, without warranty. Always test thoroughly before production deployment.
353
+
354
+ ---
355
+
356
+ ## Usage Examples
357
+
358
+ ### Single fingerprint check
359
+
360
+ ```ts
361
+ import { TLSFingerprint } from '@cap-kit/tls-fingerprint';
362
+
363
+ const result = await TLSFingerprint.checkCertificate({
364
+ url: 'https://example.com',
365
+ fingerprint: 'aabbccdd...',
366
+ });
367
+
368
+ if (result.fingerprintMatched) {
369
+ console.log('Certificate is trusted');
370
+ } else {
371
+ console.log('Fingerprint mismatch:', result.error);
372
+ }
373
+ ```
374
+
375
+ ### Multiple fingerprints (certificate rotation)
376
+
377
+ ```ts
378
+ import { TLSFingerprint } from '@cap-kit/tls-fingerprint';
379
+
380
+ const result = await TLSFingerprint.checkCertificates({
381
+ url: 'https://example.com',
382
+ fingerprints: ['aabbccdd...', '11223344...'],
383
+ });
384
+
385
+ if (result.fingerprintMatched) {
386
+ console.log('Certificate matched:', result.matchedFingerprint);
387
+ }
388
+ ```
389
+
390
+ ### Using static configuration
391
+
392
+ ```ts
393
+ // capacitor.config.ts
394
+ plugins: {
395
+ TLSFingerprint: {
396
+ fingerprint: 'aabbccdd...',
397
+ excludedDomains: ['localhost', 'analytics.example.com']
398
+ }
399
+ }
400
+
401
+ // App code
402
+ const result = await TLSFingerprint.checkCertificate({
403
+ url: 'https://example.com',
404
+ });
405
+ ```
406
+
407
+ ---
408
+
409
+ ## Contributing
410
+
411
+ Contributions are welcome. Please read the [contributing guide](CONTRIBUTING.md) before submitting a pull request.
412
+
413
+ ---
414
+
415
+ ## Credits
416
+
417
+ This plugin is based on prior work from the community and has been refactored for Capacitor v8 and Swift Package Manager compatibility.
418
+
419
+ Original inspiration:
420
+
421
+ - [https://github.com/mchl18/Capacitor-SSL-Pinning](https://github.com/mchl18/Capacitor-SSL-Pinning)
422
+
423
+ ---
424
+
425
+ ## License
426
+
427
+ MIT
@@ -0,0 +1,103 @@
1
+ buildscript {
2
+ ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '2.2.20'
3
+ repositories {
4
+ google()
5
+ mavenCentral()
6
+ }
7
+ dependencies {
8
+ classpath 'com.android.tools.build:gradle:8.13.2'
9
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
10
+ }
11
+ }
12
+
13
+ plugins {
14
+ id "org.jlleitschuh.gradle.ktlint" version "14.0.1" apply false
15
+ }
16
+
17
+ ext {
18
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
19
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
20
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
21
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
22
+ androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.17.0'
23
+ }
24
+
25
+ apply plugin: 'com.android.library'
26
+ apply plugin: 'kotlin-android'
27
+ apply plugin: 'kotlin-parcelize'
28
+ apply plugin: 'org.jlleitschuh.gradle.ktlint'
29
+
30
+ import groovy.json.JsonSlurper
31
+
32
+ def getPluginVersion() {
33
+ try {
34
+ def packageJsonFile = file('../package.json')
35
+ if (packageJsonFile.exists()) {
36
+ def packageJson = new JsonSlurper().parseText(packageJsonFile.text)
37
+ if (packageJson.version) {
38
+ return packageJson.version
39
+ }
40
+ }
41
+ } catch (Exception e) {
42
+ throw new GradleException("Failed to read plugin version from package.json", e)
43
+ }
44
+
45
+ throw new GradleException("Plugin version not found in ../package.json")
46
+ }
47
+
48
+ def pluginVersion = getPluginVersion()
49
+
50
+ android {
51
+ namespace = "io.capkit.tlsfingerprint"
52
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion as Integer : 36
53
+
54
+ // AGP 8.0+ disables BuildConfig by default for libraries.
55
+ // We need to enable it to inject the plugin version.
56
+ buildFeatures {
57
+ buildConfig = true
58
+ }
59
+
60
+ defaultConfig {
61
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion as Integer : 24
62
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion as Integer : 36
63
+ versionCode 1
64
+
65
+ // Dynamic versioning (feature enabled)
66
+ versionName = pluginVersion
67
+
68
+ // Injects the version into the BuildConfig class ONLY if feature is enabled
69
+ buildConfigField "String", "PLUGIN_VERSION", "\"${pluginVersion}\""
70
+
71
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
72
+ }
73
+ buildTypes {
74
+ release {
75
+ minifyEnabled = false
76
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
77
+ }
78
+ }
79
+ lint {
80
+ abortOnError = false
81
+ }
82
+ compileOptions {
83
+ sourceCompatibility = JavaVersion.VERSION_21
84
+ targetCompatibility = JavaVersion.VERSION_21
85
+ }
86
+ }
87
+
88
+ repositories {
89
+ google()
90
+ mavenCentral()
91
+ }
92
+
93
+ dependencies {
94
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
95
+ implementation project(':capacitor-android')
96
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
97
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
98
+ implementation "androidx.core:core-ktx:$androidxCoreKTXVersion"
99
+
100
+ testImplementation "junit:junit:$junitVersion"
101
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
102
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
103
+ }
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <uses-permission android:name="android.permission.INTERNET"/>
3
+ </manifest>