├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── broken-links.yml │ ├── sonar.yml │ └── test.yml ├── .gitignore ├── .lycheeignore ├── Identity.snk ├── LICENSE ├── Mastercard.Developer.ClientEncryption.Core ├── Encryption │ ├── AES │ │ ├── AesCbc.cs │ │ ├── AesEncryption.cs │ │ └── AesGcm.cs │ ├── EncryptionConfig.cs │ ├── EncryptionConfigBuilder.cs │ ├── EncryptionException.cs │ ├── FieldLevelEncryption.cs │ ├── FieldLevelEncryptionConfig.cs │ ├── FieldLevelEncryptionConfigBuilder.cs │ ├── FieldLevelEncryptionParams.cs │ ├── JWE │ │ ├── JweEncryption.cs │ │ ├── JweHeader.cs │ │ └── JweObject.cs │ ├── JweConfig.cs │ ├── JweConfigBuilder.cs │ └── RSA │ │ └── RsaEncryption.cs ├── Mastercard.Developer.ClientEncryption.Core.csproj └── Utils │ ├── Base64Utils.cs │ ├── ByteUtils.cs │ ├── EncodingUtils.cs │ ├── EncryptionUtils.cs │ ├── JsonUtils.cs │ └── RsaKeyUtils.cs ├── Mastercard.Developer.ClientEncryption.RestSharp ├── Interceptors │ └── RestSharpFieldLevelEncryptionInterceptor.cs └── Mastercard.Developer.ClientEncryption.RestSharp.csproj ├── Mastercard.Developer.ClientEncryption.RestSharpV2 ├── Interceptors │ ├── RestSharpEncryptionInterceptor.cs │ ├── RestSharpFieldLevelEncryptionInterceptor.cs │ └── RestSharpJweEncryptionInterceptor.cs └── Mastercard.Developer.ClientEncryption.RestSharpV2.csproj ├── Mastercard.Developer.ClientEncryption.Tests ├── Mastercard.Developer.ClientEncryption.Tests.csproj ├── Net4x │ └── Mastercard.Developer.ClientEncryption.Tests.Net4x.csproj └── Tests │ ├── Encryption │ ├── AES │ │ └── AesEncryptionTest.cs │ ├── EncryptionExceptionTest.cs │ ├── FieldLevelEncryptionConfigBuilderTest.cs │ ├── FieldLevelEncryptionParamsTest.cs │ ├── FieldLevelEncryptionTest.cs │ ├── JWE │ │ ├── CbcJweObjectTest.cs │ │ ├── GcmJweObjectTest.cs │ │ ├── JweEncryptionTest.cs │ │ └── JweHeaderTest.cs │ ├── JweConfigBuilderTest.cs │ └── RSA │ │ └── RsaEncryptionTest.cs │ ├── Interceptors │ ├── RestResponseDouble.cs │ ├── RestSharpFieldLevelEncryptionInterceptorTest.cs │ ├── RestSharpV2CbcJweEncryptionInterceptorTest.cs │ ├── RestSharpV2FieldLevelEncryptionInterceptorTest.cs │ └── RestSharpV2GcmJweEncryptionInterceptorTest.cs │ ├── Test │ └── TestUtils.cs │ ├── Utils │ ├── Base64UtilsTest.cs │ ├── ByteUtilsTest.cs │ ├── EncodingUtilsTest.cs │ ├── EncryptionUtilsTest.cs │ ├── JsonUtilsTest.cs │ └── RsaKeyUtilsTest.cs │ └── _Resources │ ├── Certificates │ ├── test_certificate-1024.pem │ ├── test_certificate-2048.der │ ├── test_certificate-2048.pem │ ├── test_certificate-4096.pem │ └── test_certificate-512.pem │ └── Keys │ ├── Pkcs1 │ ├── test_key_pkcs1-1024.pem │ ├── test_key_pkcs1-2048.pem │ ├── test_key_pkcs1-2048_uneven_length.pem │ ├── test_key_pkcs1-4096.pem │ └── test_key_pkcs1-512.pem │ ├── Pkcs12 │ └── test_key.p12 │ └── Pkcs8 │ ├── test_invalid_key.der │ ├── test_key_pkcs8-1024.der │ ├── test_key_pkcs8-1024.pem │ ├── test_key_pkcs8-2048.der │ ├── test_key_pkcs8-2048.pem │ ├── test_key_pkcs8-4096.der │ ├── test_key_pkcs8-4096.pem │ ├── test_key_pkcs8-512.der │ └── test_key_pkcs8-512.pem ├── Mastercard.Developer.ClientEncryption.sln ├── Mastercard.Developer.ClientEncryption.sln.DotSettings ├── README.md └── icon.png /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us improve 4 | title: "[BUG] Description" 5 | labels: 'Issue: Bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | #### Bug Report Checklist 11 | 12 | - [ ] Have you provided a code sample to reproduce the issue? 13 | - [ ] Have you tested with the latest release to confirm the issue still exists? 14 | - [ ] Have you searched for related issues/PRs? 15 | - [ ] What's the actual output vs expected output? 16 | 17 | 20 | 21 | **Description** 22 | A clear and concise description of what is the question, suggestion, or issue and why this is a problem for you. 23 | 24 | **To Reproduce** 25 | Steps to reproduce the behavior. 26 | 27 | **Expected behavior** 28 | A clear and concise description of what you expected to happen. 29 | 30 | **Screenshots** 31 | If applicable, add screenshots to help explain your problem. 32 | 33 | **Additional context** 34 | Add any other context about the problem here (OS, language version, etc..). 35 | 36 | 37 | **Related issues/PRs** 38 | Has a similar issue/PR been reported/opened before? 39 | 40 | **Suggest a fix/enhancement** 41 | If you can't fix the bug yourself, perhaps you can point to what might be causing the problem (line of code or commit), or simply make a suggestion. 42 | 43 | *If this is a Feature request, please check out [this](feature_request.md).* 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[REQ] Feature Request Description" 5 | labels: 'Enhancement: Feature' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Is your feature request related to a problem? Please describe. 11 | 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | ### Describe the solution you'd like 15 | 16 | A clear and concise description of what you want to happen. 17 | 18 | ### Describe alternatives you've considered 19 | 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | ### Additional context 23 | 24 | Add any other context or screenshots about the feature request here. 25 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | ### PR checklist 3 | 4 | - [ ] An issue/feature request has been created for this PR 5 | - [ ] Pull Request title clearly describes the work in the pull request and the Pull Request description provides details about how to validate the work. Missing information here may result in a delayed response. 6 | - [ ] File the PR against the `master` branch 7 | - [ ] The code in this PR is covered by unit tests 8 | 9 | #### Link to issue/feature request: *add the link here* 10 | 11 | #### Description 12 | A clear and concise description of what is this PR for and any additional info might be useful for reviewing it. 13 | -------------------------------------------------------------------------------- /.github/workflows/broken-links.yml: -------------------------------------------------------------------------------- 1 | 'on': 2 | push: 3 | branches: 4 | - "**" 5 | schedule: 6 | - cron: 0 16 * * * 7 | workflow_dispatch: 8 | name: broken links? 9 | jobs: 10 | linkChecker: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Link Checker 16 | uses: lycheeverse/lychee-action@v1.8.0 17 | with: 18 | fail: true 19 | -------------------------------------------------------------------------------- /.github/workflows/sonar.yml: -------------------------------------------------------------------------------- 1 | name: Sonar 2 | 'on': 3 | push: 4 | branches: 5 | - "**" 6 | pull_request_target: 7 | branches: 8 | - "**" 9 | types: [opened, synchronize, reopened, labeled] 10 | schedule: 11 | - cron: 0 16 * * * 12 | workflow_dispatch: 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 20 | - name: Check for external PR 21 | if: ${{ !(contains(github.event.pull_request.labels.*.name, 'safe') || 22 | github.event.pull_request.head.repo.full_name == github.repository || 23 | github.event_name != 'pull_request_target') }} 24 | run: echo "Unsecure PR, must be labelled with the 'safe' label, then run the workflow again" && exit 1 25 | - name: Setup .NET 8 26 | uses: actions/setup-dotnet@v4.3.1 27 | with: 28 | dotnet-version: 8.0.x 29 | - name: Setup java 30 | uses: actions/setup-java@v1 31 | with: 32 | java-version: '11' 33 | - name: Test 34 | env: 35 | NETCORE_TEST_PROJECT: >- 36 | Mastercard.Developer.ClientEncryption.Tests/Mastercard.Developer.ClientEncryption.Tests.csproj 37 | GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' 38 | SONAR_TOKEN: '${{ secrets.SONAR_TOKEN }}' 39 | run: > 40 | export PATH="$PATH:$HOME/.dotnet/tools" 41 | 42 | dotnet tool install --global dotnet-sonarscanner 43 | 44 | dotnet sonarscanner begin \ 45 | /k:"Mastercard_client-encryption-csharp" \ 46 | /n:"client-encryption-csharp" \ 47 | /o:"mastercard" \ 48 | /d:sonar.host.url="https://sonarcloud.io" \ 49 | /d:sonar.login="$SONAR_TOKEN" \ 50 | /d:sonar.cs.vstest.reportsPaths="./Mastercard.Developer.ClientEncryption.Tests/bin/tests.trx" \ 51 | /d:sonar.cs.opencover.reportsPaths="./Mastercard.Developer.ClientEncryption.Tests/bin/coverage.xml" 52 | 53 | dotnet test $NETCORE5_TEST_PROJECT -c Release 54 | -l:"trx;LogFileName=tests.trx" -r:"./Mastercard.Developer.ClientEncryption.Tests/bin/" /p:CollectCoverage=true 55 | /p:CoverletOutputFormat="opencover" 56 | /p:CoverletOutput="bin/coverage.xml" 57 | 58 | dotnet publish $NETCORE5_TEST_PROJECT -c Release 59 | 60 | dotnet sonarscanner end /d:sonar.login="$SONAR_TOKEN" 61 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | 'on': 3 | push: 4 | branches: 5 | - "**" 6 | pull_request: 7 | branches: 8 | - "**" 9 | schedule: 10 | - cron: 0 16 * * * 11 | workflow_dispatch: 12 | jobs: 13 | build: 14 | env: 15 | NET_TEST_PROJECT: >- 16 | Mastercard.Developer.ClientEncryption.Tests/Mastercard.Developer.ClientEncryption.Tests.csproj 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | dotnet: 21 | - 8.0.x 22 | include: 23 | - dotnet: 8.0.x 24 | TEST_PROJECT: $NETCORE_TEST_PROJECT 25 | steps: 26 | - name: Checkout code 27 | uses: actions/checkout@v2 28 | - name: Setup .NET 4 29 | uses: actions/setup-dotnet@v4.3.1 30 | with: 31 | dotnet-version: 8.0.x 32 | 33 | - name: Build using .NET 8 34 | run: > 35 | export PATH="$PATH:$HOME/.dotnet/tools" 36 | 37 | dotnet build ${{ env.NETCORE_TEST_PROJECT }} -c Release 38 | - name: Setup .NET [Core] ${{ matrix.dotnet }} 39 | uses: actions/setup-dotnet@v4.3.1 40 | with: 41 | dotnet-version: '${{ matrix.dotnet }}' 42 | - name: List .NET SDKs 43 | run: > 44 | dotnet --list-sdks 45 | - name: Run tests using .NET [Core] ${{ matrix.dotnet }} 46 | run: > 47 | export PATH="$PATH:$HOME/.dotnet/tools" 48 | 49 | dotnet test ${{ matrix.TEST_PROJECT }} -c Debug 50 | -l:"trx;LogFileName=tests.trx" -r:"bin/" /p:CollectCoverage=true 51 | /p:CoverletOutputFormat="opencover" 52 | /p:CoverletOutput="bin/coverage.xml" 53 | - name: Local publish 54 | run: > 55 | dotnet publish ${{ matrix.TEST_PROJECT }} -c Debug 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | .vscode/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # SonarLint 41 | .sonarlint 42 | *.ruleset 43 | 44 | # NUNIT 45 | *.VisualState.xml 46 | TestResult.xml 47 | 48 | # Build Results of an ATL Project 49 | [Dd]ebugPS/ 50 | [Rr]eleasePS/ 51 | dlldata.c 52 | 53 | # Benchmark Results 54 | BenchmarkDotNet.Artifacts/ 55 | 56 | # .NET Core 57 | project.lock.json 58 | project.fragment.lock.json 59 | artifacts/ 60 | **/Properties/launchSettings.json 61 | 62 | # StyleCop 63 | StyleCopReport.xml 64 | 65 | # Files built by Visual Studio 66 | *_i.c 67 | *_p.c 68 | *_i.h 69 | *.ilk 70 | *.meta 71 | *.obj 72 | *.iobj 73 | *.pch 74 | *.pdb 75 | *.ipdb 76 | *.pgc 77 | *.pgd 78 | *.rsp 79 | *.sbr 80 | *.tlb 81 | *.tli 82 | *.tlh 83 | *.tmp 84 | *.tmp_proj 85 | *.log 86 | *.vspscc 87 | *.vssscc 88 | .builds 89 | *.pidb 90 | *.svclog 91 | *.scc 92 | 93 | # Chutzpah Test files 94 | _Chutzpah* 95 | 96 | # Visual C++ cache files 97 | ipch/ 98 | *.aps 99 | *.ncb 100 | *.opendb 101 | *.opensdf 102 | *.sdf 103 | *.cachefile 104 | *.VC.db 105 | *.VC.VC.opendb 106 | 107 | # Visual Studio profiler 108 | *.psess 109 | *.vsp 110 | *.vspx 111 | *.sap 112 | 113 | # Visual Studio Trace Files 114 | *.e2e 115 | 116 | # TFS 2012 Local Workspace 117 | $tf/ 118 | 119 | # Guidance Automation Toolkit 120 | *.gpState 121 | 122 | # ReSharper is a .NET coding add-in 123 | _ReSharper*/ 124 | *.[Rr]e[Ss]harper 125 | *.DotSettings.user 126 | 127 | # JustCode is a .NET coding add-in 128 | .JustCode 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # The packages folder can be ignored because of Package Restore 188 | **/[Pp]ackages/* 189 | # except build/, which is used as an MSBuild target. 190 | !**/[Pp]ackages/build/ 191 | # Uncomment if necessary however generally it will be regenerated when needed 192 | #!**/[Pp]ackages/repositories.config 193 | # NuGet v3's project.json files produces more ignorable files 194 | *.nuget.props 195 | *.nuget.targets 196 | 197 | # Microsoft Azure Build Output 198 | csx/ 199 | *.build.csdef 200 | 201 | # Microsoft Azure Emulator 202 | ecf/ 203 | rcf/ 204 | 205 | # Windows Store app package directories and files 206 | AppPackages/ 207 | BundleArtifacts/ 208 | Package.StoreAssociation.xml 209 | _pkginfo.txt 210 | *.appx 211 | 212 | # Visual Studio cache files 213 | # files ending in .cache can be ignored 214 | *.[Cc]ache 215 | # but keep track of directories ending in .cache 216 | !*.[Cc]ache/ 217 | 218 | # Others 219 | ClientBin/ 220 | ~$* 221 | *~ 222 | *.dbmdl 223 | *.dbproj.schemaview 224 | *.jfm 225 | *.pfx 226 | *.publishsettings 227 | orleans.codegen.cs 228 | 229 | # Including strong name files can present a security risk 230 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 231 | #*.snk 232 | 233 | # Since there are multiple workflows, uncomment next line to ignore bower_components 234 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 235 | #bower_components/ 236 | 237 | # RIA/Silverlight projects 238 | Generated_Code/ 239 | 240 | # Backup & report files from converting an old project file 241 | # to a newer Visual Studio version. Backup files are not needed, 242 | # because we have git ;-) 243 | _UpgradeReport_Files/ 244 | Backup*/ 245 | UpgradeLog*.XML 246 | UpgradeLog*.htm 247 | ServiceFabricBackup/ 248 | *.rptproj.bak 249 | 250 | # SQL Server files 251 | *.mdf 252 | *.ldf 253 | *.ndf 254 | 255 | # Business Intelligence projects 256 | *.rdl.data 257 | *.bim.layout 258 | *.bim_*.settings 259 | *.rptproj.rsuser 260 | 261 | # Microsoft Fakes 262 | FakesAssemblies/ 263 | 264 | # GhostDoc plugin setting file 265 | *.GhostDoc.xml 266 | 267 | # Node.js Tools for Visual Studio 268 | .ntvs_analysis.dat 269 | node_modules/ 270 | 271 | # Visual Studio 6 build log 272 | *.plg 273 | 274 | # Visual Studio 6 workspace options file 275 | *.opt 276 | 277 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 278 | *.vbw 279 | 280 | # Visual Studio LightSwitch build output 281 | **/*.HTMLClient/GeneratedArtifacts 282 | **/*.DesktopClient/GeneratedArtifacts 283 | **/*.DesktopClient/ModelManifest.xml 284 | **/*.Server/GeneratedArtifacts 285 | **/*.Server/ModelManifest.xml 286 | _Pvt_Extensions 287 | 288 | # Paket dependency manager 289 | .paket/paket.exe 290 | paket-files/ 291 | 292 | # FAKE - F# Make 293 | .fake/ 294 | 295 | # JetBrains Rider 296 | .idea/ 297 | *.sln.iml 298 | 299 | # CodeRush 300 | .cr/ 301 | 302 | # Python Tools for Visual Studio (PTVS) 303 | __pycache__/ 304 | *.pyc 305 | 306 | # Cake - Uncomment if you are using it 307 | # tools/** 308 | # !tools/packages.config 309 | 310 | # Tabs Studio 311 | *.tss 312 | 313 | # Telerik's JustMock configuration file 314 | *.jmconfig 315 | 316 | # BizTalk build output 317 | *.btp.cs 318 | *.btm.cs 319 | *.odx.cs 320 | *.xsd.cs 321 | 322 | # OpenCover UI analysis results 323 | OpenCover/ 324 | 325 | # Azure Stream Analytics local run output 326 | ASALocalRun/ 327 | 328 | # MSBuild Binary and Structured Log 329 | *.binlog 330 | 331 | # NVidia Nsight GPU debugger configuration file 332 | *.nvuser 333 | 334 | # MFractors (Xamarin productivity tool) working folder 335 | .mfractor/ 336 | 337 | # Sonar 338 | .sonarqube 339 | 340 | # Generated documentation 341 | **/*.xml 342 | -------------------------------------------------------------------------------- /.lycheeignore: -------------------------------------------------------------------------------- 1 | https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/csharp.md 2 | https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/csharp-netcore.md 3 | -------------------------------------------------------------------------------- /Identity.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mastercard/client-encryption-csharp/7c9250c5a0c5f0d0d8520eb2c69e29ae09787a32/Identity.snk -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 - 2021 Mastercard 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. 22 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/AES/AesCbc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Cryptography; 4 | using Mastercard.Developer.ClientEncryption.Core.Utils; 5 | using Mastercard.Developer.ClientEncryption.Core.Encryption.JWE; 6 | 7 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption.AES 8 | { 9 | internal static class AesCbc 10 | { 11 | public static byte[] Decrypt(byte[] secretKeyBytes, JweObject jweObject) 12 | { 13 | // Extract the encryption key 14 | byte[] aesKey = new byte[16]; 15 | Array.Copy(secretKeyBytes, 16, aesKey, 0, aesKey.Length); 16 | 17 | byte[] plaintext; 18 | using (var aes = Aes.Create()) 19 | { 20 | aes.Key = aesKey; 21 | aes.Mode = CipherMode.CBC; 22 | aes.Padding = PaddingMode.PKCS7; 23 | aes.IV = Base64Utils.URLDecode(jweObject.Iv); 24 | 25 | byte[] ciphertext = Base64Utils.URLDecode(jweObject.CipherText); 26 | using (var decryptor = aes.CreateDecryptor()) 27 | { 28 | using (var memoryStream = new MemoryStream(ciphertext)) 29 | { 30 | using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) 31 | { 32 | var output = new MemoryStream(); 33 | var decrypted = new byte[Math.Min(1024, ciphertext.Length)]; 34 | int byteCount; 35 | while ((byteCount = cryptoStream.Read(decrypted, 0, decrypted.Length)) > 0) 36 | { 37 | output.Write(decrypted, 0, byteCount); 38 | } 39 | plaintext = output.ToArray(); 40 | } 41 | } 42 | } 43 | } 44 | return plaintext; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/AES/AesEncryption.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Mastercard.Developer.ClientEncryption.Core.Utils; 3 | 4 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption.AES 5 | { 6 | static internal class AesEncryption 7 | { 8 | static internal byte[] GenerateIV() 9 | { 10 | byte[] iv = new byte[12]; // 96 bytes as per NIST recommendation.. 11 | using (var rng = RandomNumberGenerator.Create()) 12 | { 13 | rng.GetBytes(iv); 14 | } 15 | return iv; 16 | } 17 | 18 | static internal byte[] GenerateCek(int bitLength) 19 | { 20 | byte[] cekMaterial = new byte[ByteUtils.ByteCount(bitLength)]; 21 | using (var rng = RandomNumberGenerator.Create()) 22 | { 23 | rng.GetBytes(cekMaterial); 24 | } 25 | return cekMaterial; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/AES/AesGcm.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Mastercard.Developer.ClientEncryption.Core.Utils; 3 | using Mastercard.Developer.ClientEncryption.Core.Encryption.JWE; 4 | 5 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption.AES 6 | { 7 | internal class AesGcmAuthenticated 8 | { 9 | public byte[] Ciphertext { get; private set; } 10 | public byte[] AuthTag { get; private set; } 11 | 12 | internal AesGcmAuthenticated(byte[] ciphertext, byte[] authTag) 13 | { 14 | Ciphertext = ciphertext; 15 | AuthTag = authTag; 16 | } 17 | } 18 | 19 | internal static class AesGcm 20 | { 21 | internal static byte[] Decrypt(byte[] secretKeyBytes, JweObject jweObject) 22 | { 23 | #if NETSTANDARD2_1 24 | byte[] plaintext; 25 | using (var aes = new System.Security.Cryptography.AesGcm(secretKeyBytes)) 26 | { 27 | byte[] nonce = Base64Utils.URLDecode(jweObject.Iv); 28 | byte[] aad = Encoding.ASCII.GetBytes(jweObject.RawHeader); 29 | byte[] authTag = Base64Utils.URLDecode(jweObject.AuthTag); 30 | byte[] ciphertext = Base64Utils.URLDecode(jweObject.CipherText); 31 | plaintext = new byte[ciphertext.Length]; 32 | 33 | aes.Decrypt(nonce, ciphertext, authTag, plaintext, aad); 34 | } 35 | return plaintext; 36 | #else 37 | throw new EncryptionException("AES/GCM/NoPadding is unsupported on .NET Standard < 2.1"); 38 | #endif 39 | } 40 | 41 | internal static AesGcmAuthenticated Encrypt(byte[] secretKeyBytes, byte[] nonce, byte[] plaintext, byte[] aad) 42 | { 43 | #if NETSTANDARD2_1 44 | byte[] ciphertext = new byte[plaintext.Length]; 45 | byte[] authTag = new byte[System.Security.Cryptography.AesGcm.TagByteSizes.MaxSize]; 46 | using (var aes = new System.Security.Cryptography.AesGcm(secretKeyBytes)) 47 | { 48 | aes.Encrypt(nonce, plaintext, ciphertext, authTag, aad); 49 | } 50 | return new AesGcmAuthenticated(ciphertext, authTag); 51 | #else 52 | throw new EncryptionException("AES/GCM/NoPadding is unsupported on .NET Standard < 2.1"); 53 | #endif 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/EncryptionConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Security.Cryptography; 3 | using System.Security.Cryptography.X509Certificates; 4 | #pragma warning disable 1591 // "Missing XML comment for publicly visible type or member." 5 | 6 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption 7 | { 8 | /// 9 | /// A POCO for storing the encryption/decryption configuration. 10 | /// 11 | public abstract class EncryptionConfig 12 | { 13 | /// 14 | /// The different methods of encryption 15 | /// 16 | public enum EncryptionScheme 17 | { 18 | Legacy, 19 | Jwe 20 | } 21 | 22 | /// 23 | /// The encryption scheme to be used 24 | /// 25 | public EncryptionScheme Scheme { get; internal set; } 26 | 27 | /// 28 | /// The digest algorithm to be used for the RSA OAEP padding. Example: "SHA-512". 29 | /// 30 | public string OaepPaddingDigestAlgorithm { get; internal set; } 31 | 32 | /// 33 | /// The SHA-256 hex-encoded digest of the key used for encryption (optional, the digest will be 34 | /// automatically computed if this field is null or empty). 35 | /// Example: "c3f8ef7053c4fb306f7476e7d1956f0aa992ff9dfdd5244b912a1d377ff3a84f" 36 | /// 37 | public string EncryptionKeyFingerprint { get; internal set; } 38 | 39 | /// 40 | /// A certificate object whose public key will be used for encryption. 41 | /// 42 | public X509Certificate2 EncryptionCertificate { get; internal set; } 43 | 44 | /// 45 | /// A private key object to be used for decryption. 46 | /// 47 | public RSA DecryptionKey { get; internal set; } 48 | 49 | /// 50 | /// A list of JSON paths to encrypt in request payloads. 51 | /// 52 | /// 53 | /// new Dictionary<string, string> 54 | /// { 55 | /// { "$.path.to.element.to.be.encrypted", "$.path.to.object.where.to.store.encryption.fields" } 56 | /// }; 57 | /// 58 | public Dictionary EncryptionPaths { get; internal set; } = new Dictionary(); 59 | 60 | /// 61 | /// A list of JSON paths to decrypt in response payloads. 62 | /// 63 | /// 64 | /// new Dictionary<string, string> 65 | /// { 66 | /// { "$.path.to.object.with.encryption.fields", "$.path.where.to.write.decrypted.element" } 67 | /// }; 68 | /// 69 | public Dictionary DecryptionPaths { get; internal set; } = new Dictionary(); 70 | 71 | /// 72 | /// The name of the payload field where to write/read the encrypted data value. 73 | /// 74 | public string EncryptedValueFieldName { get; internal set; } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/EncryptionConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable 1591 // "Missing XML comment for publicly visible type or member." 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Security.Cryptography; 6 | using System.Security.Cryptography.X509Certificates; 7 | using Mastercard.Developer.ClientEncryption.Core.Utils; 8 | 9 | using static Mastercard.Developer.ClientEncryption.Core.Utils.JsonUtils; 10 | 11 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption 12 | { 13 | /// 14 | /// A base class for configuration builders. 15 | /// 16 | public abstract class EncryptionConfigBuilder 17 | { 18 | protected internal X509Certificate2 _encryptionCertificate; 19 | protected internal string _encryptionKeyFingerprint; 20 | protected internal RSA _decryptionKey; 21 | protected internal readonly Dictionary _encryptionPaths = new Dictionary(); 22 | protected internal readonly Dictionary _decryptionPaths = new Dictionary(); 23 | protected internal string _encryptedValueFieldName; 24 | protected internal string _oaepPaddingDigestAlgorithm; 25 | 26 | protected internal void CheckJsonPathParameterValues() 27 | { 28 | foreach (var key in _decryptionPaths.Keys) 29 | { 30 | if (!IsPathDefinite(key) || !IsPathDefinite(_decryptionPaths[key])) 31 | { 32 | throw new ArgumentException("JSON paths for decryption must point to a single item!"); 33 | } 34 | } 35 | 36 | foreach (var key in _encryptionPaths.Keys) 37 | { 38 | if (!IsPathDefinite(key) || !IsPathDefinite(_encryptionPaths[key])) 39 | { 40 | throw new ArgumentException("JSON paths for encryption must point to a single item!"); 41 | } 42 | } 43 | } 44 | 45 | protected internal void ComputeEncryptionKeyFingerprintWhenNeeded() 46 | { 47 | try 48 | { 49 | if (_encryptionCertificate == null || !string.IsNullOrEmpty(_encryptionKeyFingerprint)) 50 | { 51 | // No encryption certificate set or certificate fingerprint already provided 52 | return; 53 | } 54 | var encodedKey = RsaKeyUtils.GetEncoded(_encryptionCertificate.PublicKey); 55 | var keyFingerprintBytes = Sha256Digest(encodedKey); 56 | _encryptionKeyFingerprint = EncodingUtils.HexEncode(keyFingerprintBytes); 57 | } 58 | catch (Exception e) 59 | { 60 | throw new EncryptionException("Failed to compute encryption key fingerprint!", e); 61 | } 62 | } 63 | 64 | protected internal static byte[] Sha256Digest(byte[] inputBytes) 65 | { 66 | var sha256 = SHA256.Create(); 67 | return sha256.ComputeHash(inputBytes); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/EncryptionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #pragma warning disable 1591 // "Missing XML comment for publicly visible type or member." 3 | 4 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption 5 | { 6 | public class EncryptionException : Exception 7 | { 8 | public EncryptionException(string message, Exception innerException) : base(message, innerException) {} 9 | public EncryptionException(string message) : base(message) {} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/FieldLevelEncryptionConfig.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable 1591 // "Missing XML comment for publicly visible type or member." 2 | 3 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption 4 | { 5 | /// 6 | /// A POCO for storing the encryption/decryption configuration. 7 | /// 8 | public class FieldLevelEncryptionConfig : EncryptionConfig 9 | { 10 | /// 11 | /// The different ways of encoding the field and header values. 12 | /// 13 | public enum FieldValueEncoding 14 | { 15 | Base64, 16 | Hex 17 | } 18 | 19 | internal FieldLevelEncryptionConfig() {} 20 | 21 | /// 22 | /// The SHA-256 hex-encoded digest of the certificate used for encryption (optional, the digest will be 23 | /// automatically computed if this field is null or empty). 24 | /// Example: "4d9d7540be320429ffc8e6506f054525816e2d0e95a85247d5b58be713f28be0" 25 | /// 26 | public string EncryptionCertificateFingerprint { get; internal set; } 27 | 28 | /// 29 | /// The name of the payload field where to write/read the digest algorithm used for 30 | /// the RSA OAEP padding (optional, the field won't be set if the name is null or empty). 31 | /// 32 | public string OaepPaddingDigestAlgorithmFieldName { get; internal set; } 33 | 34 | /// 35 | /// The name of the HTTP header where to write/read the digest algorithm used for 36 | /// the RSA OAEP padding (optional, the header won't be set if the name is null or empty). 37 | /// 38 | public string OaepPaddingDigestAlgorithmHeaderName { get; internal set; } 39 | 40 | /// 41 | /// The name of the payload field where to write/read the initialization vector value. 42 | /// 43 | public string IvFieldName { get; internal set; } 44 | 45 | /// 46 | /// The name of the header where to write/read the initialization vector value. 47 | /// 48 | public string IvHeaderName { get; internal set; } 49 | 50 | /// 51 | /// The name of the payload field where to write/read the one-time usage encrypted symmetric key. 52 | /// 53 | public string EncryptedKeyFieldName { get; internal set; } 54 | 55 | /// 56 | /// The name of the header where to write/read the one-time usage encrypted symmetric key. 57 | /// 58 | public string EncryptedKeyHeaderName { get; internal set; } 59 | 60 | /// 61 | /// The name of the payload field where to write/read the digest of the encryption 62 | /// certificate (optional, the field won't be set if the name is null or empty). 63 | /// 64 | public string EncryptionCertificateFingerprintFieldName { get; internal set; } 65 | 66 | /// 67 | /// The name of the header where to write/read the digest of the encryption 68 | /// certificate (optional, the header won't be set if the name is null or empty). 69 | /// 70 | public string EncryptionCertificateFingerprintHeaderName { get; internal set; } 71 | 72 | /// 73 | /// The name of the payload field where to write/read the digest of the encryption 74 | /// key (optional, the field won't be set if the name is null or empty). 75 | /// 76 | public string EncryptionKeyFingerprintFieldName { get; internal set; } 77 | 78 | /// 79 | /// The name of the header where to write/read the digest of the encryption 80 | /// key (optional, the header won't be set if the name is null or empty). 81 | /// 82 | public string EncryptionKeyFingerprintHeaderName { get; internal set; } 83 | 84 | /// 85 | /// How the field/header values have to be encoded. 86 | /// 87 | public FieldValueEncoding ValueEncoding { get; internal set; } 88 | 89 | /// 90 | /// If the encryption parameters must be written to/read from HTTP payloads. 91 | /// 92 | public bool UseHttpPayloads() => !string.IsNullOrEmpty(EncryptedKeyFieldName) && !string.IsNullOrEmpty(IvFieldName); 93 | 94 | /// 95 | /// If the encryption parameters must be written to/read from HTTP headers. 96 | /// 97 | public bool UseHttpHeaders() => !string.IsNullOrEmpty(EncryptedKeyHeaderName) && !string.IsNullOrEmpty(IvHeaderName); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/FieldLevelEncryptionParams.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using System.Security.Cryptography.X509Certificates; 4 | using Mastercard.Developer.ClientEncryption.Core.Utils; 5 | #pragma warning disable 1591 // "Missing XML comment for publicly visible type or member." 6 | 7 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption 8 | { 9 | /// 10 | /// Encryption parameters for computing field level encryption/decryption. 11 | /// 12 | public class FieldLevelEncryptionParams 13 | { 14 | private const int SymmetricKeySize = 128; 15 | 16 | /// 17 | /// Initialization vector value. 18 | /// 19 | public string IvValue { get; private set; } 20 | 21 | /// 22 | /// Encrypted key value. 23 | /// 24 | public string EncryptedKeyValue { get; private set; } 25 | 26 | /// 27 | /// Digest algorithm to be used for the RSA OAEP padding. Example: "SHA-512". 28 | /// 29 | public string OaepPaddingDigestAlgorithmValue { get; private set; } 30 | 31 | private FieldLevelEncryptionConfig Config { get; set; } 32 | private byte[] SecretKeyBytes { get; set; } 33 | private byte[] IvBytes { get; set; } 34 | 35 | private FieldLevelEncryptionParams() {} 36 | 37 | public FieldLevelEncryptionParams(FieldLevelEncryptionConfig config, string ivValue, string encryptedKeyValue, string oaepPaddingDigestAlgorithmValue = null) 38 | { 39 | IvValue = ivValue; 40 | EncryptedKeyValue = encryptedKeyValue; 41 | OaepPaddingDigestAlgorithmValue = oaepPaddingDigestAlgorithmValue; 42 | Config = config; 43 | } 44 | 45 | /// 46 | /// Generate encryption parameters. 47 | /// 48 | /// 49 | public static FieldLevelEncryptionParams Generate(FieldLevelEncryptionConfig config) 50 | { 51 | // Generate a random IV 52 | var ivBytes = GenerateIv(); 53 | var ivValue = EncodingUtils.EncodeBytes(ivBytes, config.ValueEncoding); 54 | 55 | // Generate an AES secret key 56 | var secretKeyBytes = GenerateSecretKey(); 57 | 58 | // Encrypt the secret key 59 | var encryptedSecretKeyBytes = RsaEncryption.WrapSecretKey(config.EncryptionCertificate.GetRSAPublicKey(), secretKeyBytes, config.OaepPaddingDigestAlgorithm); 60 | var encryptedKeyValue = EncodingUtils.EncodeBytes(encryptedSecretKeyBytes, config.ValueEncoding); 61 | 62 | // Compute the OAEP padding digest algorithm 63 | var oaepPaddingDigestAlgorithmValue = config.OaepPaddingDigestAlgorithm.Replace("-", string.Empty); 64 | 65 | return new FieldLevelEncryptionParams 66 | { 67 | IvValue = ivValue, 68 | EncryptedKeyValue = encryptedKeyValue, 69 | OaepPaddingDigestAlgorithmValue = oaepPaddingDigestAlgorithmValue, 70 | Config = config, 71 | SecretKeyBytes = secretKeyBytes, 72 | IvBytes = ivBytes 73 | }; 74 | } 75 | 76 | private static byte[] GenerateIv() 77 | { 78 | using (var aes = Aes.Create()) 79 | { 80 | if (aes == null) 81 | { 82 | throw new EncryptionException("Failed to generate IV, AES instance is null!"); 83 | } 84 | 85 | aes.GenerateIV(); 86 | return aes.IV; 87 | } 88 | } 89 | 90 | private static byte[] GenerateSecretKey() 91 | { 92 | using (var aes = Aes.Create()) 93 | { 94 | if (aes == null) 95 | { 96 | throw new EncryptionException("Failed to generate secret key, AES instance is null!"); 97 | } 98 | 99 | aes.KeySize = SymmetricKeySize; 100 | aes.GenerateKey(); 101 | return aes.Key; 102 | } 103 | } 104 | 105 | internal byte[] GetSecretKeyBytes() 106 | { 107 | try 108 | { 109 | if (SecretKeyBytes != null) 110 | { 111 | return SecretKeyBytes; 112 | } 113 | // Decrypt the AES secret key 114 | var encryptedSecretKeyBytes = EncodingUtils.DecodeValue(EncryptedKeyValue, Config.ValueEncoding); 115 | SecretKeyBytes = RsaEncryption.UnwrapSecretKey(Config, encryptedSecretKeyBytes, OaepPaddingDigestAlgorithmValue); 116 | return SecretKeyBytes; 117 | } 118 | catch (Exception e) 119 | { 120 | throw new EncryptionException("Failed to decode and unwrap the provided secret key value!", e); 121 | } 122 | } 123 | 124 | internal byte[] GetIvBytes() 125 | { 126 | try 127 | { 128 | if (IvBytes != null) 129 | { 130 | return IvBytes; 131 | } 132 | // Decode the IV 133 | IvBytes = EncodingUtils.DecodeValue(IvValue, Config.ValueEncoding); 134 | return IvBytes; 135 | } 136 | catch (Exception e) 137 | { 138 | throw new EncryptionException("Failed to decode the provided IV value!", e); 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/JWE/JweEncryption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using Mastercard.Developer.ClientEncryption.Core.Utils; 5 | #pragma warning disable 1591 // "Missing XML comment for publicly visible type or member." 6 | 7 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption.JWE 8 | { 9 | public static class JweEncryption 10 | { 11 | private const string Algorithm = "RSA-OAEP-256"; 12 | private const string Encryption = "A256GCM"; 13 | private const string ContentType = "application/json"; 14 | 15 | public static string EncryptPayload(string payload, JweConfig config) 16 | { 17 | try 18 | { 19 | // Parse the given payload 20 | var payloadToken = JsonUtils.ParsePayload(payload); 21 | 22 | // Encrypt 23 | foreach (var entry in config.EncryptionPaths) 24 | { 25 | var jsonPathIn = entry.Key; 26 | var jsonPathOut = entry.Value; 27 | payloadToken = EncryptPayloadPath(payloadToken, jsonPathIn, jsonPathOut, config); 28 | } 29 | 30 | return payloadToken.ToString(); 31 | } 32 | catch (Exception ex) 33 | { 34 | throw new EncryptionException("Payload encryption failed!", ex); 35 | } 36 | } 37 | 38 | public static string DecryptPayload(string payload, JweConfig config) 39 | { 40 | try 41 | { 42 | // Parse the given payload 43 | var payloadToken = JsonUtils.ParsePayload(payload); 44 | 45 | // Perform decryption 46 | foreach (var entry in config.DecryptionPaths) 47 | { 48 | var jsonPathIn = entry.Key; 49 | var jsonPathOut = entry.Value; 50 | payloadToken = DecryptPayloadPath(payloadToken, jsonPathIn, jsonPathOut, config); 51 | } 52 | return payloadToken.ToString(); 53 | } 54 | catch (Exception ex) 55 | { 56 | throw new EncryptionException("Payload decryption failed!", ex); 57 | } 58 | } 59 | 60 | private static JToken DecryptPayloadPath(JToken payload, string jsonPathIn, string jsonPathOut, JweConfig config) 61 | { 62 | var token = payload.SelectToken(jsonPathIn); 63 | if (JsonUtils.IsNullOrEmptyJson(token)) 64 | { 65 | // Nothing to decrypt 66 | return payload; 67 | } 68 | 69 | // Read and remove encrypted data and encryption fields at the given JSON path 70 | var encryptedValue = ReadAndDeleteJsonKey(payload, token, config.EncryptedValueFieldName); 71 | if (string.IsNullOrEmpty(encryptedValue)) 72 | { 73 | // Nothing to decrypt 74 | return payload; 75 | } 76 | var jweObject = JweObject.Parse(encryptedValue); 77 | var decryptedValue = jweObject.Decrypt(config); 78 | 79 | if ("$".Equals(jsonPathOut)) 80 | { 81 | return JsonUtils.ParsePayload(decryptedValue); 82 | } 83 | 84 | JsonUtils.CheckOrCreateOutObject(payload, jsonPathOut); 85 | JsonUtils.AddDecryptedDataToPayload(payload, decryptedValue, jsonPathOut); 86 | 87 | // Remove the input 88 | token = payload.SelectToken(jsonPathIn); 89 | if (null != token && token.Parent != null) 90 | { 91 | token.Parent.Remove(); 92 | } 93 | return payload; 94 | } 95 | 96 | private static string ReadAndDeleteJsonKey(JToken context, JToken token, string key) 97 | { 98 | if (string.IsNullOrEmpty(key)) return token.ToString(); 99 | var value = context.SelectToken(key); 100 | if (null != value && null != value.Parent) 101 | { 102 | value.Parent.Remove(); 103 | } 104 | return token.ToString(); 105 | } 106 | 107 | private static JToken EncryptPayloadPath(JToken json, string jsonPathIn, string jsonPathOut, JweConfig config) 108 | { 109 | var token = json.SelectToken(jsonPathIn); 110 | if (JsonUtils.IsNullOrEmptyJson(token)) 111 | { 112 | // Nothing to encrypt 113 | return json; 114 | } 115 | 116 | // Encode and encrypt 117 | var inJsonString = JsonUtils.SanitizeJson(token.ToString(Formatting.None)); 118 | var header = new JweHeader(Algorithm, Encryption, config.EncryptionKeyFingerprint, ContentType); 119 | var encrypted = JweObject.Encrypt(config, inJsonString, header); 120 | 121 | // Delete data in the clear 122 | if ("$".Equals(jsonPathIn)) 123 | { 124 | // Create a new object 125 | json = JObject.Parse("{}"); 126 | } 127 | else 128 | { 129 | token.Parent.Remove(); 130 | } 131 | 132 | JsonUtils.CheckOrCreateOutObject(json, jsonPathOut); 133 | var outJsonToken = json.SelectToken(jsonPathOut) as JObject; 134 | JsonUtils.AddOrReplaceJsonKey(outJsonToken, config.EncryptedValueFieldName, encrypted); 135 | return json; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/JWE/JweHeader.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Newtonsoft.Json.Linq; 3 | using Mastercard.Developer.ClientEncryption.Core.Utils; 4 | #pragma warning disable 1591 // "Missing XML comment for publicly visible type or member." 5 | 6 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption.JWE 7 | { 8 | internal sealed class JweHeader 9 | { 10 | private const string Algorithm = "alg"; 11 | private const string KeyId = "kid"; 12 | private const string EncryptionAlgorithm = "enc"; 13 | private const string ContentType = "cty"; 14 | 15 | public string Enc { get; } 16 | public string Kid { get; } 17 | public string Alg { get; } 18 | public string Cty { get; } 19 | 20 | public JweHeader(string alg, string enc, string kid, string cty) 21 | { 22 | Alg = alg; 23 | Enc = enc; 24 | Kid = kid; 25 | Cty = cty; 26 | } 27 | 28 | public JObject Json => 29 | new JObject( 30 | new JProperty(KeyId, Kid), 31 | new JProperty(ContentType, Cty), 32 | new JProperty(EncryptionAlgorithm, Enc), 33 | new JProperty(Algorithm, Alg) 34 | ); 35 | 36 | public static JweHeader Parse(string encoded) 37 | { 38 | // Decode and parse the string 39 | var decoded = Base64Utils.URLDecode(encoded); 40 | var json = Encoding.UTF8.GetString(decoded); 41 | var jobject = JObject.Parse(json); 42 | 43 | // Wrap it up 44 | return new JweHeader( 45 | ((string)jobject[Algorithm]), 46 | ((string)jobject[EncryptionAlgorithm]), 47 | ((string)jobject[KeyId]), 48 | ((string)jobject[ContentType]) 49 | ); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/JWE/JweObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Mastercard.Developer.ClientEncryption.Core.Utils; 4 | using Mastercard.Developer.ClientEncryption.Core.Encryption.AES; 5 | using System.Security.Cryptography.X509Certificates; 6 | #pragma warning disable 1591 // "Missing XML comment for publicly visible type or member." 7 | 8 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption.JWE 9 | { 10 | internal class JweObject 11 | { 12 | private const string A128CBC_HS256 = "A128CBC-HS256"; 13 | private const string A256GCM = "A256GCM"; 14 | private const string A128GCM = "A128GCM"; 15 | private const string A192GCM = "A192GCM"; 16 | 17 | public JweHeader Header { get; private set; } 18 | public string RawHeader { get; private set; } 19 | public string EncryptedKey { get; private set; } 20 | public string Iv { get; private set; } 21 | public string CipherText { get; private set; } 22 | public string AuthTag { get; private set; } 23 | 24 | public string Decrypt(JweConfig config) 25 | { 26 | var unwrappedKey = RsaEncryption.UnwrapSecretKey(config, Base64Utils.URLDecode(EncryptedKey), "SHA-256"); 27 | if (unwrappedKey == null) 28 | { 29 | throw new EncryptionException($"Failed to unwrap key {EncryptedKey}"); 30 | } 31 | 32 | var encryptionMethod = Header.Enc; 33 | 34 | byte[] plaintext; 35 | switch (encryptionMethod) 36 | { 37 | case A256GCM: 38 | plaintext = AesGcm.Decrypt(unwrappedKey, this); 39 | break; 40 | case A128GCM: 41 | plaintext = AesGcm.Decrypt(unwrappedKey, this); 42 | break; 43 | case A192GCM: 44 | plaintext = AesGcm.Decrypt(unwrappedKey, this); 45 | break; 46 | case A128CBC_HS256: 47 | plaintext = AesCbc.Decrypt(unwrappedKey, this); 48 | break; 49 | default: 50 | throw new EncryptionException($"Encryption method {encryptionMethod} is not supported"); 51 | } 52 | return Encoding.UTF8.GetString(plaintext); 53 | } 54 | 55 | public static string Encrypt(JweConfig config, string payload, JweHeader header) 56 | { 57 | var cek = AesEncryption.GenerateCek(256); 58 | var encryptedSecretKeyBytes = RsaEncryption.WrapSecretKey(config.EncryptionCertificate.GetRSAPublicKey(), cek, "SHA-256"); 59 | var encryptedKey = Base64Utils.URLEncode(encryptedSecretKeyBytes); 60 | 61 | var iv = AesEncryption.GenerateIV(); 62 | var payloadBytes = Encoding.UTF8.GetBytes(payload); 63 | 64 | var headerString = header.Json.ToString(); 65 | var encodedHeader = Base64Utils.URLEncode(Encoding.UTF8.GetBytes(headerString)); 66 | var aad = Encoding.ASCII.GetBytes(encodedHeader); 67 | 68 | var encrypted = AesGcm.Encrypt(cek, iv, payloadBytes, aad); 69 | return Serialize(encodedHeader, encryptedKey, Base64Utils.URLEncode(iv), Base64Utils.URLEncode(encrypted.Ciphertext), Base64Utils.URLEncode(encrypted.AuthTag)); 70 | } 71 | 72 | public static JweObject Parse(string encryptedPayload) 73 | { 74 | var fields = encryptedPayload.Trim().Split('.'); 75 | 76 | var jweObject = new JweObject(); 77 | jweObject.RawHeader = fields[0]; 78 | jweObject.Header = JweHeader.Parse(jweObject.RawHeader); 79 | jweObject.EncryptedKey = fields[1]; 80 | jweObject.Iv = fields[2]; 81 | jweObject.CipherText = fields[3]; 82 | jweObject.AuthTag = fields[4]; 83 | return jweObject; 84 | } 85 | 86 | internal static string Serialize(string header, string encryptedKey, string iv, string cipherText, string authTag) 87 | { 88 | return header + "." + 89 | encryptedKey + 90 | '.' + 91 | iv + 92 | '.' + 93 | cipherText + 94 | '.' + 95 | authTag; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/JweConfig.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable 1591 // "Missing XML comment for publicly visible type or member." 2 | 3 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption 4 | { 5 | public class JweConfig : EncryptionConfig 6 | { 7 | internal JweConfig() { } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/JweConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using System.Security.Cryptography.X509Certificates; 5 | 6 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption 7 | { 8 | /// 9 | /// A builder class for . 10 | /// 11 | public class JweConfigBuilder : EncryptionConfigBuilder 12 | { 13 | private JweConfigBuilder() { } 14 | 15 | /// 16 | /// Get an instance of the builder. 17 | /// 18 | public static JweConfigBuilder AJweEncryptionConfig() => new JweConfigBuilder(); 19 | 20 | /// 21 | /// See: 22 | /// 23 | public JweConfigBuilder WithEncryptionCertificate(X509Certificate2 encryptionCertificate) 24 | { 25 | _encryptionCertificate = encryptionCertificate; 26 | return this; 27 | } 28 | 29 | /// 30 | /// See: 31 | /// 32 | public JweConfigBuilder WithEncryptionKeyFingerprint(string encryptionKeyFingerprint) 33 | { 34 | _encryptionKeyFingerprint = encryptionKeyFingerprint; 35 | return this; 36 | } 37 | 38 | /// 39 | /// See: 40 | /// 41 | public JweConfigBuilder WithDecryptionKey(RSA decryptionKey) 42 | { 43 | _decryptionKey = decryptionKey; 44 | return this; 45 | } 46 | 47 | /// 48 | /// See: 49 | /// 50 | public JweConfigBuilder WithEncryptionPath(string jsonPathIn, string jsonPathOut) 51 | { 52 | _encryptionPaths.Add(jsonPathIn, jsonPathOut); 53 | return this; 54 | } 55 | 56 | /// 57 | /// See: 58 | /// 59 | public JweConfigBuilder WithDecryptionPath(string jsonPathIn, string jsonPathOut) 60 | { 61 | _decryptionPaths.Add(jsonPathIn, jsonPathOut); 62 | return this; 63 | } 64 | 65 | /// 66 | /// See: 67 | /// 68 | public JweConfigBuilder WithEncryptedValueFieldName(string encryptedValueFieldName) 69 | { 70 | _encryptedValueFieldName = encryptedValueFieldName; 71 | return this; 72 | } 73 | 74 | /// 75 | /// Build a 76 | /// 77 | public JweConfig Build() 78 | { 79 | CheckParameterValues(); 80 | ComputeEncryptionKeyFingerprintWhenNeeded(); 81 | CheckJsonPathParameterValues(); 82 | 83 | return new JweConfig 84 | { 85 | EncryptionCertificate = _encryptionCertificate, 86 | EncryptionKeyFingerprint = _encryptionKeyFingerprint, 87 | DecryptionKey = _decryptionKey, 88 | EncryptionPaths = _encryptionPaths.Count == 0 ? new Dictionary { { "$", "$" } } : _encryptionPaths, 89 | DecryptionPaths = _decryptionPaths.Count == 0 ? new Dictionary { { "$.encryptedData", "$" } } : _decryptionPaths, 90 | EncryptedValueFieldName = _encryptedValueFieldName ?? "encryptedData", 91 | Scheme = EncryptionConfig.EncryptionScheme.Jwe 92 | }; 93 | } 94 | 95 | private void CheckParameterValues() 96 | { 97 | if (_decryptionKey == null && _encryptionCertificate == null) 98 | { 99 | throw new ArgumentException("You must include at least an encryption certificate or a decryption key"); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Encryption/RSA/RsaEncryption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace Mastercard.Developer.ClientEncryption.Core.Encryption 5 | { 6 | static internal class RsaEncryption 7 | { 8 | internal static byte[] WrapSecretKey(System.Security.Cryptography.RSA publicKey, byte[] keyBytes, String oaepDigestAlgorithm) 9 | { 10 | try 11 | { 12 | return publicKey.Encrypt(keyBytes, 13 | "SHA-256".Equals(oaepDigestAlgorithm) 14 | ? RSAEncryptionPadding.OaepSHA256 15 | : RSAEncryptionPadding.OaepSHA512); 16 | } 17 | catch (Exception e) 18 | { 19 | throw new EncryptionException("Failed to wrap secret key!", e); 20 | } 21 | } 22 | 23 | internal static byte[] UnwrapSecretKey(EncryptionConfig config, byte[] keyBytes, string oaepDigestAlgorithm) 24 | { 25 | try 26 | { 27 | if (!oaepDigestAlgorithm.Contains("-")) 28 | { 29 | oaepDigestAlgorithm = oaepDigestAlgorithm.Replace("SHA", "SHA-"); 30 | } 31 | 32 | var decryptionKey = config.DecryptionKey; 33 | return decryptionKey.Decrypt(keyBytes, 34 | "SHA-256".Equals(oaepDigestAlgorithm) 35 | ? RSAEncryptionPadding.OaepSHA256 36 | : RSAEncryptionPadding.OaepSHA512); 37 | } 38 | catch (Exception e) 39 | { 40 | throw new EncryptionException("Failed to unwrap secret key!", e); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Mastercard.Developer.ClientEncryption.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | true 6 | 1.9.3 7 | Mastercard.Developer.ClientEncryption.Core 8 | Mastercard 9 | Mastercard 10 | LICENSE 11 | https://github.com/Mastercard/client-encryption-csharp 12 | See: https://github.com/Mastercard/client-encryption-csharp/releases 13 | https://github.com/Mastercard/client-encryption-csharp 14 | Library for Mastercard API compliant payload encryption/decryption 15 | 16 | true 17 | ../Identity.snk 18 | 1.0.0.0 19 | 1.9.3 20 | Mastercard.Developer.ClientEncryption.Core.xml 21 | true 22 | snupkg 23 | icon.png 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | True 32 | 33 | 34 | 35 | 36 | 37 | 38 | <_Parameter1>Mastercard.Developer.ClientEncryption.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b50e085c4583209974a6a902505c534b21b205b9e627c583beea690b541d67fdfe8dc53e50c6a0653f0e3e7828d75432a50df4c4b5348ca4e662e42af603cc97636fb2393ef995babf19bc51644c30296a3edf0afd0182cfc21d0c836da5af922e3631a52a2dc5ed2edc8c568f41ff3d41c82fbe27e6bf4fe4df3e50568b65c8 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Utils/Base64Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mastercard.Developer.ClientEncryption.Core.Utils 4 | { 5 | internal static class Base64Utils 6 | { 7 | // c.f. https://datatracker.ietf.org/doc/html/rfc7515#appendix-C 8 | internal static string URLEncode(byte[] bytes) 9 | { 10 | string s = Convert.ToBase64String(bytes); 11 | s = s.Split('=')[0]; 12 | s = s.Replace('+', '-'); 13 | s = s.Replace('/', '_'); 14 | return s; 15 | } 16 | 17 | internal static byte[] URLDecode(string encoded) 18 | { 19 | string s = encoded.Replace('_', '/').Replace('-', '+'); 20 | switch (s.Length % 4) 21 | { 22 | case 2: 23 | s += "=="; 24 | break; 25 | 26 | case 3: 27 | s += "="; 28 | break; 29 | } 30 | return Convert.FromBase64String(s); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Utils/ByteUtils.cs: -------------------------------------------------------------------------------- 1 | namespace Mastercard.Developer.ClientEncryption.Core.Utils 2 | { 3 | internal static class ByteUtils 4 | { 5 | static internal int ByteCount(int bitCount) 6 | { 7 | int byteCount = (bitCount / 8); 8 | return ((bitCount % 8) == 0) 9 | ? byteCount 10 | : (byteCount + 1); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Utils/EncodingUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using static Mastercard.Developer.ClientEncryption.Core.Encryption.FieldLevelEncryptionConfig; 4 | 5 | namespace Mastercard.Developer.ClientEncryption.Core.Utils 6 | { 7 | internal static class EncodingUtils 8 | { 9 | internal static string EncodeBytes(byte[] bytes, FieldValueEncoding encoding) 10 | { 11 | return encoding == FieldValueEncoding.Hex ? HexEncode(bytes) : Convert.ToBase64String(bytes); 12 | } 13 | 14 | internal static byte[] DecodeValue(string value, FieldValueEncoding encoding) 15 | { 16 | return encoding == FieldValueEncoding.Hex ? HexDecode(value) : Convert.FromBase64String(value); 17 | } 18 | 19 | internal static byte[] HexDecode(string value) 20 | { 21 | if (value == null) throw new ArgumentNullException(nameof(value)); 22 | return Enumerable.Range(0, value.Length) 23 | .Where(x => x % 2 == 0) 24 | .Select(x => Convert.ToByte(value.Substring(x, 2), 16)) 25 | .ToArray(); 26 | } 27 | 28 | internal static string HexEncode(byte[] bytes) 29 | { 30 | if (bytes == null) throw new ArgumentNullException(nameof(bytes)); 31 | var hexString = BitConverter.ToString(bytes); 32 | return hexString.Replace("-", string.Empty).ToLower(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Utils/EncryptionUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using System.Security.Cryptography.X509Certificates; 4 | 5 | namespace Mastercard.Developer.ClientEncryption.Core.Utils 6 | { 7 | /// 8 | /// Utility class for loading certificates and keys. 9 | /// 10 | public static class EncryptionUtils 11 | { 12 | /// 13 | /// Populate a X509 encryption certificate object with the certificate data at the given file path. 14 | /// 15 | public static X509Certificate2 LoadEncryptionCertificate(string certificatePath) 16 | { 17 | if (certificatePath == null) throw new ArgumentNullException(nameof(certificatePath)); 18 | return new X509Certificate2(certificatePath); 19 | } 20 | 21 | /// 22 | /// Load a RSA decryption key from a file (PEM or DER). 23 | /// 24 | public static RSA LoadDecryptionKey(string keyFilePath) => RsaKeyUtils.ReadPrivateKeyFile(keyFilePath); 25 | 26 | /// 27 | /// Load a RSA decryption key out of a PKCS#12 container. 28 | /// 29 | public static RSA LoadDecryptionKey(string pkcs12KeyFilePath, string decryptionKeyAlias, string decryptionKeyPassword, 30 | X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet) 31 | { 32 | if (pkcs12KeyFilePath == null) throw new ArgumentNullException(nameof(pkcs12KeyFilePath)); 33 | var certificate = new X509Certificate2(pkcs12KeyFilePath, decryptionKeyPassword, keyStorageFlags); 34 | return certificate.GetRSAPrivateKey(); 35 | } 36 | 37 | /// 38 | /// Load a RSA decryption key from a byte array. 39 | /// 40 | public static RSA LoadDecryptionKey(byte[] key) => RsaKeyUtils.ReadPrivateKey(key); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Utils/JsonUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | using System.Text.RegularExpressions; 4 | using Newtonsoft.Json; 5 | 6 | namespace Mastercard.Developer.ClientEncryption.Core.Utils 7 | { 8 | internal static class JsonUtils 9 | { 10 | private static readonly Regex LastElementInPathRegExp = new Regex(".*(\\['.*'\\])"); // Returns "['obj2']" for "$['obj1']['obj2']" 11 | 12 | /// 13 | /// Get JSON path to the parent of the object at the given JSON path. 14 | /// 15 | internal static string GetParentJsonPath(string jsonPath) 16 | { 17 | if (string.IsNullOrEmpty(jsonPath)) throw new ArgumentException(nameof(jsonPath)); 18 | 19 | if ("$".Equals(jsonPath)) 20 | { 21 | throw new InvalidOperationException($"Unable to find parent for '{jsonPath}'"); 22 | } 23 | 24 | // "$.['path'].['to'].['object']"? 25 | var match = LastElementInPathRegExp.Match(jsonPath); 26 | if (match.Success) 27 | { 28 | return jsonPath.Replace(match.Groups[match.Groups.Count - 1].Value, string.Empty); 29 | } 30 | 31 | // "$.path.to.object"? 32 | var lastIndexOfDot = jsonPath.LastIndexOf(".", StringComparison.Ordinal); 33 | if (lastIndexOfDot != -1) 34 | { 35 | return jsonPath.Substring(0, lastIndexOfDot); 36 | } 37 | 38 | // "object" 39 | return "$"; 40 | } 41 | 42 | /// 43 | /// Get object key at the given JSON path. 44 | /// 45 | internal static string GetJsonElementKey(string jsonPath) 46 | { 47 | if (string.IsNullOrEmpty(jsonPath)) throw new ArgumentException(nameof(jsonPath)); 48 | 49 | if ("$".Equals(jsonPath)) 50 | { 51 | throw new InvalidOperationException($"Unable to find object key for '{jsonPath}'"); 52 | } 53 | 54 | // "$.['path'].['to'].['object']"? 55 | var match = LastElementInPathRegExp.Match(jsonPath); 56 | if (match.Success) 57 | { 58 | return match.Groups[match.Groups.Count - 1].Value.Replace("['", "").Replace("']", ""); 59 | } 60 | 61 | // "$.path.to.object"? 62 | var lastIndexOfDot = jsonPath.LastIndexOf(".", StringComparison.Ordinal); 63 | if (lastIndexOfDot != -1) 64 | { 65 | return jsonPath.Substring(lastIndexOfDot + 1, jsonPath.Length - lastIndexOfDot - 1); 66 | } 67 | 68 | // "object" 69 | return jsonPath; 70 | } 71 | 72 | /// 73 | /// Checks if a JSON path points to a single item or if it potentially returns multiple items. 74 | /// See: https://github.com/json-path/JsonPath 75 | /// 76 | internal static bool IsPathDefinite(string path) 77 | { 78 | return !path.Contains("*") && !path.Contains("..") && !path.Contains("@") && !path.Contains(","); 79 | } 80 | /// 81 | /// Parses the Json payload with specified parameters 82 | /// 83 | /// payloadToken 84 | internal static JToken ParsePayload(string payload) 85 | { 86 | var jsonReader = new JsonTextReader(new System.IO.StringReader(payload)) { 87 | DateParseHandling = DateParseHandling.None 88 | }; 89 | 90 | return JToken.ReadFrom(jsonReader); 91 | } 92 | 93 | internal static void CheckOrCreateOutObject(JToken payloadObject, string jsonPathOut) 94 | { 95 | var outJsonToken = payloadObject.SelectToken(jsonPathOut); 96 | if (null != outJsonToken) 97 | { 98 | // Object already exists 99 | AssertIsObject(outJsonToken, jsonPathOut); 100 | return; 101 | } 102 | 103 | // Path does not exist: if parent exists then we create a new object under the parent 104 | var parentJsonPath = GetParentJsonPath(jsonPathOut); 105 | var parentJsonObject = payloadObject.SelectToken(parentJsonPath); 106 | if (parentJsonObject == null) 107 | { 108 | throw new InvalidOperationException($"Parent path not found in payload: '{parentJsonPath}'!"); 109 | } 110 | var elementKey = JsonUtils.GetJsonElementKey(jsonPathOut); 111 | (parentJsonObject as JObject)?.Add(elementKey, new JObject()); 112 | } 113 | 114 | internal static void AssertIsObject(JToken jToken, string jsonPath) 115 | { 116 | if (!(jToken is JObject)) 117 | { 118 | throw new InvalidOperationException($"JSON object expected at path: '{jsonPath}'!"); 119 | } 120 | } 121 | 122 | internal static string SanitizeJson(string json) 123 | { 124 | return json.Replace("\n", string.Empty) 125 | .Replace("\r", string.Empty) 126 | .Replace("\t", string.Empty) 127 | .Replace(Environment.NewLine, string.Empty); 128 | } 129 | 130 | internal static void AddOrReplaceJsonKey(JObject jsonObject, string key, JToken value) 131 | { 132 | jsonObject.Remove(key); 133 | jsonObject.Add(key, value); 134 | } 135 | 136 | internal static bool IsNullOrEmptyJson(JToken token) 137 | { 138 | if (token == null) 139 | { 140 | return true; 141 | } 142 | switch (token.Type) 143 | { 144 | case JTokenType.Array: 145 | case JTokenType.Object: 146 | return !token.HasValues; 147 | 148 | case JTokenType.String: 149 | return token.ToString() == String.Empty; 150 | 151 | case JTokenType.Null: 152 | return true; 153 | } 154 | return false; 155 | } 156 | 157 | internal static void AddDecryptedDataToPayload(JToken payloadObject, string decryptedValue, string jsonPathOut) 158 | { 159 | try 160 | { 161 | // Object? 162 | var decryptedValueObject = JObject.Parse(decryptedValue); 163 | var outJsonObject = payloadObject.SelectToken(jsonPathOut) as JObject; 164 | outJsonObject?.Merge(decryptedValueObject); // Merge the two objects 165 | } 166 | catch 167 | { 168 | try 169 | { 170 | // Array? 171 | var decryptedValueObject = JArray.Parse(decryptedValue); 172 | payloadObject.SelectToken(jsonPathOut).Replace(decryptedValueObject); 173 | } 174 | catch 175 | { 176 | // Primitive type 177 | payloadObject.SelectToken(jsonPathOut).Replace(AsPrimitiveValue(decryptedValue)); 178 | } 179 | } 180 | } 181 | 182 | private static JToken AsPrimitiveValue(string value) 183 | { 184 | // Boolean? 185 | if ("true".Equals(value.ToLower()) || "false".Equals(value.ToLower())) 186 | { 187 | return bool.Parse(value); 188 | } 189 | 190 | // Numeric? 191 | try 192 | { 193 | return long.Parse(value); 194 | } 195 | catch 196 | { 197 | // Not a number, do nothing 198 | } 199 | 200 | // String 201 | return value; 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Core/Utils/RsaKeyUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Security.Cryptography.X509Certificates; 6 | using System.Text; 7 | 8 | namespace Mastercard.Developer.ClientEncryption.Core.Utils 9 | { 10 | internal static class RsaKeyUtils 11 | { 12 | private const string Pkcs1PemHeader = "-----BEGIN RSA PRIVATE KEY-----"; 13 | private const string Pkcs1PemFooter = "-----END RSA PRIVATE KEY-----"; 14 | private const string Pkcs8PemHeader = "-----BEGIN PRIVATE KEY-----"; 15 | private const string Pkcs8PemFooter = "-----END PRIVATE KEY-----"; 16 | 17 | internal static RSA ReadPrivateKeyFile(string keyFilePath) 18 | { 19 | if (keyFilePath == null) throw new ArgumentNullException(nameof(keyFilePath)); 20 | var key = File.ReadAllBytes(keyFilePath); 21 | return ReadPrivateKey(key); 22 | } 23 | 24 | internal static RSA ReadPrivateKey(byte[] key) 25 | { 26 | var keyString = Encoding.UTF8.GetString(key); 27 | if (keyString.Contains(Pkcs1PemHeader)) 28 | { 29 | // OpenSSL / PKCS#1 Base64 PEM encoded file 30 | keyString = keyString.Replace(Pkcs1PemHeader, string.Empty); 31 | keyString = keyString.Replace(Pkcs1PemFooter, string.Empty); 32 | keyString = keyString.Replace(Environment.NewLine, string.Empty); 33 | return ReadPkcs1Key(Convert.FromBase64String(keyString)); 34 | } 35 | 36 | if (keyString.Contains(Pkcs8PemHeader)) 37 | { 38 | // PKCS#8 Base64 PEM encoded file 39 | keyString = keyString.Replace(Pkcs8PemHeader, string.Empty); 40 | keyString = keyString.Replace(Pkcs8PemFooter, string.Empty); 41 | keyString = keyString.Replace(Environment.NewLine, string.Empty); 42 | return ReadPkcs8Key(Convert.FromBase64String(keyString)); 43 | } 44 | return ReadPkcs8Key(key); 45 | } 46 | 47 | private static RSA ReadPkcs8Key(byte[] pkcs8Bytes) 48 | { 49 | try 50 | { 51 | var memoryStream = new MemoryStream(pkcs8Bytes); 52 | var keyLength = (int) memoryStream.Length; 53 | var reader = new BinaryReader(memoryStream); 54 | 55 | var bytes = reader.ReadUInt16(); 56 | if (bytes == 0x8130) 57 | { 58 | reader.ReadByte(); 59 | } 60 | else if (bytes == 0x8230) 61 | { 62 | reader.ReadByte(); 63 | reader.ReadByte(); 64 | } 65 | else 66 | { 67 | throw new ArgumentException("Failed to parse PKCS#8 key, 0x8130 or 0x8230 was expected!"); 68 | } 69 | 70 | bytes = reader.ReadByte(); 71 | if (bytes != 0x02) 72 | { 73 | throw new ArgumentException("Failed to parse PKCS#8 key, 0x02 was expected!"); 74 | } 75 | 76 | bytes = reader.ReadUInt16(); 77 | if (bytes != 0x0001) 78 | { 79 | throw new ArgumentException("Failed to parse PKCS#8 key, 0x0001 was expected!"); 80 | } 81 | 82 | if (!reader.ReadBytes(15).SequenceEqual(new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 })) 83 | { 84 | throw new ArgumentException("Failed to parse PKCS#8 key, RSA OID was expected!"); 85 | } 86 | 87 | bytes = reader.ReadByte(); 88 | if (bytes != 0x04) 89 | { 90 | throw new ArgumentException("Failed to parse PKCS#8 key, 0x04 was expected!"); 91 | } 92 | 93 | bytes = reader.ReadByte(); 94 | if (bytes == 0x81) 95 | { 96 | reader.ReadByte(); 97 | } 98 | else if (bytes == 0x82) 99 | { 100 | reader.ReadByte(); 101 | reader.ReadByte(); 102 | } 103 | 104 | var pkcs1Bytes = reader.ReadBytes((int) (keyLength - memoryStream.Position)); 105 | return ReadPkcs1Key(pkcs1Bytes); 106 | } 107 | catch (ArgumentException) 108 | { 109 | throw; 110 | } 111 | catch (Exception e) 112 | { 113 | throw new ArgumentException("Failed to parse PKCS#8 key!", e); 114 | } 115 | } 116 | 117 | private static RSA ReadPkcs1Key(byte[] pkcs1Bytes) 118 | { 119 | try 120 | { 121 | var memoryStream = new MemoryStream(pkcs1Bytes); 122 | var reader = new BinaryReader(memoryStream); 123 | 124 | var bytes = reader.ReadUInt16(); 125 | if (bytes == 0x8130) 126 | { 127 | reader.ReadByte(); 128 | } 129 | else if (bytes == 0x8230) 130 | { 131 | reader.ReadByte(); 132 | reader.ReadByte(); 133 | } 134 | else 135 | { 136 | throw new ArgumentException("Failed to parse PKCS#1 key, 0x8130 or 0x8230 was expected!"); 137 | } 138 | 139 | var versionBytes = reader.ReadUInt16(); 140 | if (versionBytes != 0x0102) 141 | { 142 | throw new ArgumentException("Failed to parse PKCS#1 key, 0x0102 was expected!"); 143 | } 144 | 145 | bytes = reader.ReadByte(); 146 | if (bytes != 0x00) 147 | { 148 | throw new ArgumentException("Failed to parse PKCS#1 key, 0x00 was expected!"); 149 | } 150 | 151 | byte[] padded(byte[] array, int totalLength) 152 | { 153 | var currentLength = array.Length; 154 | if(currentLength >= totalLength) { 155 | return array; 156 | } 157 | 158 | var paddedArray = new byte[totalLength]; 159 | Array.Copy(array, 0, paddedArray, totalLength - currentLength, currentLength); 160 | 161 | return paddedArray; 162 | } 163 | 164 | var modulus = reader.ReadBytes(GetIntegerSize(reader)); 165 | 166 | var modulusLength = modulus.Length; 167 | var modulusHalfLength = (modulus.Length + 1) / 2; // half length rounded up 168 | 169 | var publicExponent = reader.ReadBytes(GetIntegerSize(reader)); 170 | var privateExponent = padded(reader.ReadBytes(GetIntegerSize(reader)), modulusLength); 171 | var prime1 = padded(reader.ReadBytes(GetIntegerSize(reader)), modulusHalfLength); 172 | var prime2 = padded(reader.ReadBytes(GetIntegerSize(reader)), modulusHalfLength); 173 | var exponent1 = padded(reader.ReadBytes(GetIntegerSize(reader)), modulusHalfLength); 174 | var exponent2 = padded(reader.ReadBytes(GetIntegerSize(reader)), modulusHalfLength); 175 | var coefficient = padded(reader.ReadBytes(GetIntegerSize(reader)), modulusHalfLength); 176 | 177 | var rsa = CreateRsa(); 178 | rsa.ImportParameters(new RSAParameters 179 | { 180 | Modulus = modulus, 181 | Exponent = publicExponent, 182 | D = privateExponent , 183 | P = prime1, 184 | Q = prime2, 185 | DP = exponent1, 186 | DQ = exponent2, 187 | InverseQ = coefficient 188 | }); 189 | return rsa; 190 | } 191 | catch (ArgumentException) 192 | { 193 | throw; 194 | } 195 | catch (Exception e) 196 | { 197 | throw new ArgumentException("Failed to parse PKCS#1 key!", e); 198 | } 199 | } 200 | 201 | private static RSA CreateRsa() 202 | { 203 | var rsa = RSA.Create(); 204 | switch (rsa.GetType().Name) 205 | { 206 | case "RSACryptoServiceProvider": 207 | { 208 | // .NET Framework 4.6+: we need a RSACng key for OaepSHA256 and OaepSHA512 209 | var rsaCngType = Type.GetType("System.Security.Cryptography.RSACng, System.Security.Cryptography.Cng"); 210 | if (null == rsaCngType) 211 | { 212 | throw new NotSupportedException("Failed to create a RSACng key! Consider adding System.Security.Cryptography.Cng to your project."); 213 | } 214 | return Activator.CreateInstance(rsaCngType) as RSA; 215 | } 216 | default: 217 | return rsa; 218 | } 219 | } 220 | 221 | /// 222 | /// Returns the DER-encoded form of a key (exact same binary as https://docs.oracle.com/javase/7/docs/api/java/security/Key.html#getEncoded()) 223 | /// 224 | internal static byte[] GetEncoded(PublicKey publicRsaKey) 225 | { 226 | var rawKeyBytes = publicRsaKey.EncodedKeyValue.RawData; 227 | var rawKeyLength = rawKeyBytes.Length + 1; 228 | byte[] sequenceBytes; 229 | byte[] bitStringBytes; 230 | byte[] oidBytes = { 0x30, 0xD, 0x6, 0x9, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0xD, 0x1, 0x1, 0x1, 0x5, 0x0 }; // 1.2.840.113549.1.1.1, NULL 231 | 232 | if (rawKeyLength > 270) 233 | { 234 | // 2048, 4096 bits 235 | var totalLength = rawKeyLength + 19; 236 | sequenceBytes = new byte[] { 0x30, 0x82, (byte) ((totalLength >> 8) & 0xff), (byte) (totalLength & 0xff) }; 237 | bitStringBytes = new byte[] { 0x3, 0x82, (byte) ((rawKeyLength >> 8) & 0xff), (byte) (rawKeyLength & 0xff) }; 238 | } 239 | else if (rawKeyLength > 140) 240 | { 241 | // 1024 bits 242 | var totalLength = rawKeyLength + 18; 243 | sequenceBytes = new byte[] { 0x30, 0x81, (byte) (totalLength & 0xff) }; 244 | bitStringBytes = new byte[] { 0x3, 0x81, (byte) (rawKeyLength & 0xff) }; 245 | } 246 | else 247 | { 248 | // 512 bits 249 | var totalLength = rawKeyLength + 17; 250 | sequenceBytes = new byte[] { 0x30, (byte) (totalLength & 0xff) }; 251 | bitStringBytes = new byte[] { 0x3, (byte) (rawKeyLength & 0xff) }; 252 | } 253 | 254 | return sequenceBytes.Concat(oidBytes) 255 | .Concat(bitStringBytes) 256 | .Concat(new byte[] { 0x0 }) 257 | .Concat(rawKeyBytes) 258 | .ToArray(); 259 | } 260 | 261 | private static int GetIntegerSize(BinaryReader reader) 262 | { 263 | int size; 264 | var bytes = reader.ReadByte(); 265 | if (bytes != 0x02) 266 | { 267 | return 0; 268 | } 269 | 270 | bytes = reader.ReadByte(); 271 | if (bytes == 0x81) 272 | { 273 | size = reader.ReadByte(); 274 | } 275 | else if (bytes == 0x82) 276 | { 277 | var high = reader.ReadByte(); 278 | var low = reader.ReadByte(); 279 | size = BitConverter.ToInt32(new byte[] { low, high, 0x00, 0x00 }, 0); 280 | } 281 | else 282 | { 283 | size = bytes; 284 | } 285 | 286 | while (reader.ReadByte() == 0x00) 287 | { 288 | size -= 1; 289 | } 290 | 291 | reader.BaseStream.Seek(-1, SeekOrigin.Current); 292 | return size; 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.RestSharp/Interceptors/RestSharpFieldLevelEncryptionInterceptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 5 | using RestSharp.Portable; 6 | 7 | namespace Mastercard.Developer.ClientEncryption.RestSharp.Interceptors 8 | { 9 | /// 10 | /// A class for encrypting RestSharp requests and decrypting RestSharp responses. 11 | /// 12 | public class RestSharpFieldLevelEncryptionInterceptor 13 | { 14 | private readonly FieldLevelEncryptionConfig _config; 15 | 16 | /// 17 | /// Constructor. 18 | /// 19 | public RestSharpFieldLevelEncryptionInterceptor(FieldLevelEncryptionConfig config) 20 | { 21 | _config = config; 22 | } 23 | 24 | /// 25 | /// Encrypt RestSharp request payloads. 26 | /// 27 | /// A RestSharp request object 28 | public void InterceptRequest(IRestRequest request) 29 | { 30 | if (request == null) throw new ArgumentNullException(nameof(request)); 31 | 32 | try 33 | { 34 | // Check request actually has a payload 35 | var bodyParam = request.Parameters.FirstOrDefault(param => param.Type == ParameterType.RequestBody); 36 | if (bodyParam == null) 37 | { 38 | // Nothing to encrypt 39 | return; 40 | } 41 | var payload = bodyParam.Value.ToString(); 42 | if (string.IsNullOrEmpty(payload)) 43 | { 44 | // Nothing to encrypt 45 | return; 46 | } 47 | 48 | // Encrypt fields & update headers 49 | string encryptedPayload; 50 | if (_config.UseHttpHeaders()) 51 | { 52 | // Generate encryption params and add them as HTTP headers 53 | var parameters = FieldLevelEncryptionParams.Generate(_config); 54 | UpdateRequestHeader(request, _config.IvHeaderName, parameters.IvValue); 55 | UpdateRequestHeader(request, _config.EncryptedKeyHeaderName, parameters.EncryptedKeyValue); 56 | UpdateRequestHeader(request, _config.EncryptionCertificateFingerprintHeaderName, _config.EncryptionCertificateFingerprint); 57 | UpdateRequestHeader(request, _config.EncryptionKeyFingerprintHeaderName, _config.EncryptionKeyFingerprint); 58 | UpdateRequestHeader(request, _config.OaepPaddingDigestAlgorithmHeaderName, parameters.OaepPaddingDigestAlgorithmValue); 59 | encryptedPayload = FieldLevelEncryption.EncryptPayload(payload, _config, parameters); 60 | } 61 | else 62 | { 63 | // Encryption params will be stored in the payload 64 | encryptedPayload = FieldLevelEncryption.EncryptPayload(payload, _config); 65 | } 66 | 67 | // Update body and content length 68 | bodyParam.Value = encryptedPayload; 69 | UpdateRequestHeader(request, "Content-Length", encryptedPayload.Length); 70 | } 71 | catch (EncryptionException) 72 | { 73 | throw; 74 | } 75 | catch (Exception e) 76 | { 77 | throw new EncryptionException("Failed to intercept and encrypt request!", e); 78 | } 79 | } 80 | 81 | /// 82 | /// Decrypt RestSharp response payloads. 83 | /// 84 | /// A RestSharp response object 85 | public void InterceptResponse(IRestResponse response) 86 | { 87 | if (response == null) throw new ArgumentNullException(nameof(response)); 88 | 89 | try 90 | { 91 | // Read response payload 92 | var encryptedPayload = response.Content; 93 | if (string.IsNullOrEmpty(encryptedPayload)) 94 | { 95 | // Nothing to decrypt 96 | return; 97 | } 98 | 99 | // Decrypt fields & update headers 100 | string decryptedPayload; 101 | if (_config.UseHttpHeaders()) 102 | { 103 | // Read encryption params from HTTP headers and delete headers 104 | var ivValue = ReadAndRemoveHeader(response, _config.IvHeaderName); 105 | var encryptedKeyValue = ReadAndRemoveHeader(response, _config.EncryptedKeyHeaderName); 106 | var oaepPaddingDigestAlgorithmValue = ReadAndRemoveHeader(response, _config.OaepPaddingDigestAlgorithmHeaderName); 107 | ReadAndRemoveHeader(response, _config.EncryptionCertificateFingerprintHeaderName); 108 | ReadAndRemoveHeader(response, _config.EncryptionKeyFingerprintHeaderName); 109 | var parameters = new FieldLevelEncryptionParams(_config, ivValue, encryptedKeyValue, oaepPaddingDigestAlgorithmValue); 110 | decryptedPayload = FieldLevelEncryption.DecryptPayload(encryptedPayload, _config, parameters); 111 | } 112 | else 113 | { 114 | // Encryption params are stored in the payload 115 | decryptedPayload = FieldLevelEncryption.DecryptPayload(encryptedPayload, _config); 116 | } 117 | 118 | // Update body and content length 119 | var contentTypeInfo = response.GetType().GetTypeInfo().GetDeclaredField("_content"); 120 | contentTypeInfo.SetValue(response, new Lazy(() => decryptedPayload)); 121 | } 122 | catch (EncryptionException) 123 | { 124 | throw; 125 | } 126 | catch (Exception e) 127 | { 128 | throw new EncryptionException("Failed to intercept and decrypt response!", e); 129 | } 130 | } 131 | 132 | private static void UpdateRequestHeader(IRestRequest request, string name, object value) 133 | { 134 | if (string.IsNullOrEmpty(name)) 135 | { 136 | // Do nothing 137 | return; 138 | } 139 | 140 | request.AddOrUpdateHeader(name, value); 141 | } 142 | 143 | private static string ReadAndRemoveHeader(IRestResponse response, string name) 144 | { 145 | if (string.IsNullOrEmpty(name) || !response.Headers.Contains(name)) 146 | { 147 | // Do nothing 148 | return null; 149 | } 150 | 151 | var value = response.Headers.GetValue(name); 152 | response.Headers.Remove(name); 153 | return value; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.RestSharp/Mastercard.Developer.ClientEncryption.RestSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | true 6 | 1.9.3 7 | Mastercard.Developer.ClientEncryption.RestSharp 8 | Mastercard 9 | Mastercard 10 | LICENSE 11 | https://github.com/Mastercard/client-encryption-csharp 12 | See: https://github.com/Mastercard/client-encryption-csharp/releases 13 | https://github.com/Mastercard/client-encryption-csharp 14 | RestSharp Portable extension for Mastercard API compliant payload encryption/decryption 15 | 16 | true 17 | ../Identity.snk 18 | 1.0.0.0 19 | 1.9.3 20 | Mastercard.Developer.ClientEncryption.RestSharp.xml 21 | true 22 | snupkg 23 | icon.png 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | True 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.RestSharpV2/Interceptors/RestSharpEncryptionInterceptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text.Json; 4 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 5 | using Header = RestSharp.HeaderParameter; 6 | using RestSharp; 7 | using System.Collections.Generic; 8 | 9 | 10 | namespace Mastercard.Developer.ClientEncryption.RestSharpV2.Interceptors 11 | { 12 | /// 13 | /// A base class for encrypting RestSharp requests and decrypting RestSharp responses. 14 | /// 15 | public abstract class RestSharpEncryptionInterceptor 16 | { 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | public static RestSharpEncryptionInterceptor From(EncryptionConfig config) 23 | { 24 | if (config.Scheme.Equals(EncryptionConfig.EncryptionScheme.Jwe)) 25 | { 26 | return new RestSharpJweEncryptionInterceptor((JweConfig) config); 27 | } 28 | return new RestSharpFieldLevelEncryptionInterceptor((FieldLevelEncryptionConfig)config); 29 | } 30 | 31 | /// 32 | /// Encrypt RestSharp request payloads. 33 | /// 34 | /// A RestSharp request object 35 | public void InterceptRequest(RestRequest request) 36 | { 37 | if (request == null) throw new ArgumentNullException(nameof(request)); 38 | 39 | try 40 | { 41 | // We will have to intercept the response later 42 | request.OnBeforeDeserialization = InterceptResponse; 43 | 44 | // Check request actually has a payload 45 | var bodyParam = request.Parameters.FirstOrDefault(param => param.Type == ParameterType.RequestBody); 46 | if (bodyParam?.Value == null) 47 | { 48 | // Nothing to encrypt 49 | return; 50 | } 51 | 52 | var payload = bodyParam.Value; 53 | if (!(payload is string)) 54 | { 55 | payload = JsonSerializer.Serialize(payload); 56 | } 57 | 58 | // Encrypt fields & update headers 59 | string encryptedPayload = EncryptPayload(request, payload.ToString()); 60 | 61 | // Update body and content length 62 | request.RemoveParameter(bodyParam); 63 | request.AddBody(encryptedPayload); 64 | UpdateRequestHeader(request, "Content-Length", encryptedPayload.Length); 65 | } 66 | catch (EncryptionException) 67 | { 68 | throw; 69 | } 70 | catch (Exception e) 71 | { 72 | throw new EncryptionException("Failed to intercept and encrypt request!", e); 73 | } 74 | } 75 | 76 | /// 77 | /// Decrypt RestSharp response payloads. 78 | /// 79 | /// A RestSharp response object 80 | public void InterceptResponse(RestResponse response) 81 | { 82 | if (response == null) throw new ArgumentNullException(nameof(response)); 83 | 84 | try 85 | { 86 | // Read response payload 87 | var encryptedPayload = response.Content; 88 | if (string.IsNullOrEmpty(encryptedPayload)) 89 | { 90 | // Nothing to decrypt 91 | return; 92 | } 93 | 94 | // Decrypt fields & return 95 | string decryptedPayload = DecryptPayload(response, encryptedPayload); 96 | response.Content = decryptedPayload; 97 | UpdateResponseHeader(response, "Content-Length", decryptedPayload.Length); 98 | } 99 | catch (EncryptionException) 100 | { 101 | throw; 102 | } 103 | catch (Exception e) 104 | { 105 | throw new EncryptionException("Failed to intercept and decrypt response!", e); 106 | } 107 | } 108 | 109 | /// 110 | /// Encrypt a RestSharp request payload. 111 | /// 112 | /// A RestSharp request object 113 | /// The payload to be encrypted 114 | /// The encrypted payload 115 | internal abstract string EncryptPayload(RestRequest request, string payload); 116 | 117 | /// 118 | /// Decrypt a RestSharp response payload 119 | /// 120 | /// A RestSharp response object 121 | /// The encrypted payload to be decrypted 122 | /// The decrypted payload 123 | internal abstract string DecryptPayload(RestResponse response, string encryptedPayload); 124 | 125 | internal static void UpdateResponseHeader(RestResponse response, string name, object value) 126 | { 127 | if (string.IsNullOrEmpty(name)) 128 | { 129 | // Do nothing 130 | return; 131 | } 132 | 133 | // Scan 134 | List
updatedHeaders = response.Headers.ToList(); 135 | foreach (Header p in response.Headers) 136 | { 137 | if (p.Name.Equals(name)) 138 | { 139 | updatedHeaders.Remove(p); 140 | updatedHeaders.Add(new Header(name, value.ToString())); 141 | response.Headers = updatedHeaders; 142 | return; 143 | } 144 | } 145 | 146 | // If we get here, there is no such header, so add one 147 | updatedHeaders.Add(new Header(name, value.ToString())); 148 | response.Headers = updatedHeaders; 149 | } 150 | 151 | internal static void UpdateRequestHeader(RestRequest request, string name, object value) 152 | { 153 | if (string.IsNullOrEmpty(name)) 154 | { 155 | // Do nothing 156 | return; 157 | } 158 | 159 | request.AddHeader(name, value.ToString()); 160 | } 161 | 162 | internal static string ReadAndRemoveHeader(RestResponse response, string name) 163 | { 164 | var header = response.Headers.ToList().Find(h => h.Name == name); 165 | if (string.IsNullOrEmpty(name) || header == null) 166 | { 167 | // Do nothing 168 | return null; 169 | } 170 | 171 | var updatedHeaders = response.Headers.ToList().FindAll(h => h.Name != name); 172 | response.Headers = updatedHeaders; 173 | return header.Value?.ToString() ?? string.Empty; 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.RestSharpV2/Interceptors/RestSharpFieldLevelEncryptionInterceptor.cs: -------------------------------------------------------------------------------- 1 | using RestSharp; 2 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 3 | 4 | namespace Mastercard.Developer.ClientEncryption.RestSharpV2.Interceptors 5 | { 6 | /// 7 | /// A class for encrypting RestSharp requests and decrypting RestSharp responses, 8 | /// using Field Level Encryption 9 | /// 10 | public class RestSharpFieldLevelEncryptionInterceptor : RestSharpEncryptionInterceptor 11 | { 12 | private readonly FieldLevelEncryptionConfig _config; 13 | 14 | /// 15 | /// Constructor. 16 | /// 17 | public RestSharpFieldLevelEncryptionInterceptor(FieldLevelEncryptionConfig config) 18 | { 19 | _config = config; 20 | } 21 | 22 | /// 23 | /// Encrypt a RestSharp request payload. 24 | /// 25 | /// A RestSharp request object 26 | /// The payload to be encrypted 27 | /// The encrypted payload 28 | internal override string EncryptPayload(RestRequest request, string payload) 29 | { 30 | // Encrypt fields & update headers 31 | string encryptedPayload; 32 | if (_config.UseHttpHeaders()) 33 | { 34 | // Generate encryption params and add them as HTTP headers 35 | var parameters = FieldLevelEncryptionParams.Generate(_config); 36 | UpdateRequestHeader(request, _config.IvHeaderName, parameters.IvValue); 37 | UpdateRequestHeader(request, _config.EncryptedKeyHeaderName, parameters.EncryptedKeyValue); 38 | UpdateRequestHeader(request, _config.EncryptionCertificateFingerprintHeaderName, _config.EncryptionCertificateFingerprint); 39 | UpdateRequestHeader(request, _config.EncryptionKeyFingerprintHeaderName, _config.EncryptionKeyFingerprint); 40 | UpdateRequestHeader(request, _config.OaepPaddingDigestAlgorithmHeaderName, parameters.OaepPaddingDigestAlgorithmValue); 41 | encryptedPayload = FieldLevelEncryption.EncryptPayload(payload.ToString(), _config, parameters); 42 | } 43 | else 44 | { 45 | // Encryption params will be stored in the payload 46 | encryptedPayload = FieldLevelEncryption.EncryptPayload(payload.ToString(), _config); 47 | } 48 | return encryptedPayload; 49 | } 50 | 51 | /// 52 | /// Decrypt a RestSharp response payload 53 | /// 54 | /// A RestSharp response object 55 | /// The encrypted payload to be decrypted 56 | /// The decrypted payload 57 | internal override string DecryptPayload(RestResponse response, string encryptedPayload) 58 | { 59 | // Decrypt fields & update headers 60 | string decryptedPayload; 61 | if (_config.UseHttpHeaders()) 62 | { 63 | // Read encryption params from HTTP headers and delete headers 64 | var ivValue = ReadAndRemoveHeader(response, _config.IvHeaderName); 65 | var encryptedKeyValue = ReadAndRemoveHeader(response, _config.EncryptedKeyHeaderName); 66 | var oaepPaddingDigestAlgorithmValue = ReadAndRemoveHeader(response, _config.OaepPaddingDigestAlgorithmHeaderName); 67 | ReadAndRemoveHeader(response, _config.EncryptionCertificateFingerprintHeaderName); 68 | ReadAndRemoveHeader(response, _config.EncryptionKeyFingerprintHeaderName); 69 | var parameters = new FieldLevelEncryptionParams(_config, ivValue, encryptedKeyValue, oaepPaddingDigestAlgorithmValue); 70 | decryptedPayload = FieldLevelEncryption.DecryptPayload(encryptedPayload, _config, parameters); 71 | } 72 | else 73 | { 74 | // Encryption params are stored in the payload 75 | decryptedPayload = FieldLevelEncryption.DecryptPayload(encryptedPayload, _config); 76 | } 77 | return decryptedPayload; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.RestSharpV2/Interceptors/RestSharpJweEncryptionInterceptor.cs: -------------------------------------------------------------------------------- 1 | using RestSharp; 2 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 3 | using Mastercard.Developer.ClientEncryption.Core.Encryption.JWE; 4 | 5 | namespace Mastercard.Developer.ClientEncryption.RestSharpV2.Interceptors 6 | { 7 | /// 8 | /// A class for encrypting RestSharp requests and decrypting RestSharp responses, 9 | /// using JSON Web Encryption. 10 | /// 11 | internal class RestSharpJweEncryptionInterceptor : RestSharpEncryptionInterceptor 12 | { 13 | private readonly JweConfig _config; 14 | 15 | /// 16 | /// Constructor. 17 | /// 18 | public RestSharpJweEncryptionInterceptor(JweConfig config) 19 | { 20 | _config = config; 21 | } 22 | 23 | /// 24 | /// Encrypt a RestSharp request payload. 25 | /// 26 | /// A RestSharp request object 27 | /// The payload to be encrypted 28 | /// The encrypted payload 29 | internal override string EncryptPayload(RestRequest request, string payload) 30 | { 31 | return JweEncryption.EncryptPayload(payload, _config); 32 | } 33 | 34 | /// 35 | /// Decrypt a RestSharp response payload 36 | /// 37 | /// A RestSharp response object 38 | /// The encrypted payload to be decrypted 39 | /// The decrypted payload 40 | internal override string DecryptPayload(RestResponse response, string encryptedPayload) 41 | { 42 | return JweEncryption.DecryptPayload(encryptedPayload, _config); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.RestSharpV2/Mastercard.Developer.ClientEncryption.RestSharpV2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | true 5 | 1.9.3 6 | Mastercard.Developer.ClientEncryption.RestSharpV2 7 | Mastercard 8 | Mastercard 9 | LICENSE 10 | https://github.com/Mastercard/client-encryption-csharp 11 | See: https://github.com/Mastercard/client-encryption-csharp/releases 12 | https://github.com/Mastercard/client-encryption-csharp 13 | RestSharp extension for Mastercard API compliant payload encryption/decryption 14 | 15 | true 16 | ../Identity.snk 17 | 1.0.0.0 18 | 1.9.3 19 | Mastercard.Developer.ClientEncryption.RestSharpV2.xml 20 | true 21 | snupkg 22 | icon.png 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | True 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | <_Parameter1>Mastercard.Developer.ClientEncryption.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b50e085c4583209974a6a902505c534b21b205b9e627c583beea690b541d67fdfe8dc53e50c6a0653f0e3e7828d75432a50df4c4b5348ca4e662e42af603cc97636fb2393ef995babf19bc51644c30296a3edf0afd0182cfc21d0c836da5af922e3631a52a2dc5ed2edc8c568f41ff3d41c82fbe27e6bf4fe4df3e50568b65c8 46 | 47 | 48 | 49 | 50 | 51 | <_Parameter1>Mastercard.Developer.ClientEncryption.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b50e085c4583209974a6a902505c534b21b205b9e627c583beea690b541d67fdfe8dc53e50c6a0653f0e3e7828d75432a50df4c4b5348ca4e662e42af603cc97636fb2393ef995babf19bc51644c30296a3edf0afd0182cfc21d0c836da5af922e3631a52a2dc5ed2edc8c568f41ff3d41c82fbe27e6bf4fe4df3e50568b65c8 52 | 53 | 54 | 55 | 56 | 57 | 58 | <_Parameter1>Mastercard.Developer.ClientEncryption.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b50e085c4583209974a6a902505c534b21b205b9e627c583beea690b541d67fdfe8dc53e50c6a0653f0e3e7828d75432a50df4c4b5348ca4e662e42af603cc97636fb2393ef995babf19bc51644c30296a3edf0afd0182cfc21d0c836da5af922e3631a52a2dc5ed2edc8c568f41ff3d41c82fbe27e6bf4fe4df3e50568b65c8 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Mastercard.Developer.ClientEncryption.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8 4 | Debug 5 | AnyCPU 6 | {DE07F478-E087-4FAA-80E2-220671C70418} 7 | Tests 8 | Properties 9 | Mastercard.Developer.ClientEncryption.Tests 10 | Mastercard.Developer.ClientEncryption.Tests 11 | 512 12 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 13 | False 14 | UnitTest 15 | 16 | 17 | 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | true 38 | 39 | 40 | ../Identity.snk 41 | 42 | 43 | 44 | _Resources\Certificates\test_certificate-1024.pem 45 | Always 46 | 47 | 48 | _Resources\Certificates\test_certificate-2048.der 49 | Always 50 | 51 | 52 | _Resources\Certificates\test_certificate-2048.pem 53 | Always 54 | 55 | 56 | _Resources\Certificates\test_certificate-4096.pem 57 | Always 58 | 59 | 60 | _Resources\Certificates\test_certificate-512.pem 61 | Always 62 | 63 | 64 | _Resources\Keys\Pkcs1\test_key_pkcs1-1024.pem 65 | Always 66 | 67 | 68 | _Resources\Keys\Pkcs1\test_key_pkcs1-2048.pem 69 | Always 70 | 71 | 72 | _Resources\Keys\Pkcs1\test_key_pkcs1-2048_uneven_length.pem 73 | Always 74 | 75 | 76 | _Resources\Keys\Pkcs1\test_key_pkcs1-4096.pem 77 | Always 78 | 79 | 80 | _Resources\Keys\Pkcs1\test_key_pkcs1-512.pem 81 | Always 82 | 83 | 84 | _Resources\Keys\Pkcs12\test_key.p12 85 | Always 86 | 87 | 88 | _Resources\Keys\Pkcs8\test_invalid_key.der 89 | Always 90 | 91 | 92 | _Resources\Keys\Pkcs8\test_key_pkcs8-1024.der 93 | Always 94 | 95 | 96 | _Resources\Keys\Pkcs8\test_key_pkcs8-1024.pem 97 | Always 98 | 99 | 100 | _Resources\Keys\Pkcs8\test_key_pkcs8-2048.der 101 | Always 102 | 103 | 104 | _Resources\Keys\Pkcs8\test_key_pkcs8-2048.pem 105 | Always 106 | 107 | 108 | _Resources\Keys\Pkcs8\test_key_pkcs8-4096.der 109 | Always 110 | 111 | 112 | _Resources\Keys\Pkcs8\test_key_pkcs8-4096.pem 113 | Always 114 | 115 | 116 | _Resources\Keys\Pkcs8\test_key_pkcs8-512.der 117 | Always 118 | 119 | 120 | _Resources\Keys\Pkcs8\test_key_pkcs8-512.pem 121 | Always 122 | 123 | 124 | 125 | 126 | {8ffa4f79-9d2c-4807-bad7-8e2f0a15e099} 127 | Mastercard.Developer.ClientEncryption.Core 128 | 129 | 130 | {bc6f2c72-16a3-44cb-ad68-3acd8a465640} 131 | Mastercard.Developer.ClientEncryption.RestSharpV2 132 | 133 | 134 | {4abdad88-8dc4-4463-91ec-74a711d6acfb} 135 | Mastercard.Developer.ClientEncryption.RestSharp 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Encryption/AES/AesEncryptionTest.cs: -------------------------------------------------------------------------------- 1 | using Mastercard.Developer.ClientEncryption.Core.Encryption.AES; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.AES 5 | { 6 | [TestClass] 7 | public class AesEncryptionTest 8 | { 9 | [TestMethod] 10 | public void TestGenerateIV() 11 | { 12 | // WHEN 13 | byte[] iv = AesEncryption.GenerateIV(); 14 | 15 | // THEN 16 | Assert.AreEqual(12, iv.Length); 17 | } 18 | 19 | [TestMethod] 20 | public void TestGenerateCek() 21 | { 22 | // WHEN 23 | byte[] cek = AesEncryption.GenerateCek(256); 24 | 25 | // THEN 26 | Assert.AreEqual(32, cek.Length); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Encryption/EncryptionExceptionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Encryption 7 | { 8 | [TestClass] 9 | public class EncryptionExceptionTest 10 | { 11 | [TestMethod] 12 | public void TestConstructor1() 13 | { 14 | try 15 | { 16 | throw new EncryptionException("Something happened!", new IOException()); 17 | } 18 | catch (Exception e) 19 | { 20 | Assert.AreEqual("Something happened!", e.Message); 21 | Assert.IsTrue(e.InnerException is IOException); 22 | } 23 | } 24 | 25 | [TestMethod] 26 | public void TestConstructor2() 27 | { 28 | try 29 | { 30 | throw new EncryptionException("Something happened!"); 31 | } 32 | catch (Exception e) 33 | { 34 | Assert.AreEqual("Something happened!", e.Message); 35 | Assert.IsNull(e.InnerException); 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Encryption/FieldLevelEncryptionParamsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 3 | using Mastercard.Developer.ClientEncryption.Tests.NetCore.Test; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Encryption 7 | { 8 | [TestClass] 9 | public class FieldLevelEncryptionParamsTest 10 | { 11 | [TestMethod] 12 | public void TestGenerate_Nominal() 13 | { 14 | // GIVEN 15 | var config = TestUtils.GetTestFieldLevelEncryptionConfigBuilder().Build(); 16 | 17 | // WHEN 18 | var parameters = FieldLevelEncryptionParams.Generate(config); 19 | 20 | // THEN 21 | Assert.IsNotNull(parameters.IvValue); 22 | Assert.IsNotNull(parameters.GetIvBytes()); 23 | Assert.IsNotNull(parameters.EncryptedKeyValue); 24 | Assert.IsNotNull(parameters.GetSecretKeyBytes()); 25 | Assert.AreEqual("SHA256", parameters.OaepPaddingDigestAlgorithmValue); 26 | } 27 | 28 | [TestMethod] 29 | [ExpectedException(typeof(EncryptionException))] 30 | public void TestGetIvBytes_ShouldThrowEncryptionException_WhenFailsToDecodeIV() 31 | { 32 | try 33 | { 34 | // GIVEN 35 | var config = TestUtils.GetTestFieldLevelEncryptionConfigBuilder().Build(); 36 | var parameters = new FieldLevelEncryptionParams(config, "INVALID VALUE", null); 37 | 38 | // WHEN 39 | var ivBytes = parameters.GetIvBytes(); 40 | Assert.Fail($"Unexpected {ivBytes}"); 41 | } 42 | catch (Exception e) 43 | { 44 | // THEN 45 | Assert.AreEqual("Failed to decode the provided IV value!", e.Message); 46 | throw; 47 | } 48 | } 49 | 50 | [TestMethod] 51 | [ExpectedException(typeof(EncryptionException))] 52 | public void TestGetSecretKeyBytes_ShouldThrowEncryptionException_WhenFailsToReadEncryptedKey() 53 | { 54 | try 55 | { 56 | // GIVEN 57 | var config = TestUtils.GetTestFieldLevelEncryptionConfigBuilder().Build(); 58 | var parameters = new FieldLevelEncryptionParams(config, null, "INVALID VALUE"); 59 | 60 | // WHEN 61 | var secretKeyBytes = parameters.GetSecretKeyBytes(); 62 | Assert.Fail($"Unexpected {secretKeyBytes}"); 63 | } 64 | catch (Exception e) 65 | { 66 | // THEN 67 | Assert.AreEqual("Failed to decode and unwrap the provided secret key value!", e.Message); 68 | throw; 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Encryption/JWE/CbcJweObjectTest.cs: -------------------------------------------------------------------------------- 1 | using Mastercard.Developer.ClientEncryption.Core.Encryption.JWE; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Mastercard.Developer.ClientEncryption.Tests.NetCore.Test; 4 | 5 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Encryption.JWE 6 | { 7 | [TestClass] 8 | public class CbcJweObjectTest 9 | { 10 | [TestMethod] 11 | public void TestDecrypt_ShouldReturnDecryptedPayload_WhenPayloadIsCbcEncrypted() 12 | { 13 | // GIVEN 14 | JweObject jweObject = TestUtils.GetTestCbcJweObject(); 15 | 16 | // WHEN 17 | string decryptedPayload = jweObject.Decrypt(TestUtils.GetTestJweConfigBuilder().Build()); 18 | 19 | // THEN 20 | Assert.AreEqual("bar", decryptedPayload); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Encryption/JWE/GcmJweObjectTest.cs: -------------------------------------------------------------------------------- 1 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 2 | using Mastercard.Developer.ClientEncryption.Core.Encryption.JWE; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using Mastercard.Developer.ClientEncryption.Tests.NetCore.Test; 5 | 6 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Encryption.JWE 7 | { 8 | [TestClass] 9 | public class GcmJweObjectTest 10 | { 11 | [TestMethod] 12 | #if !NETCOREAPP3_1 && !NET5_0_OR_GREATER 13 | [ExpectedException(typeof(EncryptionException), "AES/GCM/NoPadding is unsupported on .NET Standard < 2.1")] 14 | #endif 15 | public void TestDecrypt_ShouldReturnDecryptedPayload_WhenPayloadIs256GcmEncrypted() 16 | { 17 | // GIVEN 18 | JweObject jweObject = TestUtils.GetTest256GcmJweObject(); 19 | 20 | // WHEN 21 | string decryptedPayload = jweObject.Decrypt(TestUtils.GetTestJweConfigBuilder().Build()); 22 | 23 | // THEN 24 | Assert.AreEqual("{\"foo\":\"bar\"}", decryptedPayload); 25 | } 26 | 27 | public void TestDecrypt_ShouldReturnDecryptedPayload_WhenPayloadIs192GcmEncrypted() 28 | { 29 | // GIVEN 30 | JweObject jweObject = TestUtils.GetTest192GcmJweObject(); 31 | 32 | // WHEN 33 | string decryptedPayload = jweObject.Decrypt(TestUtils.GetTestJweConfigBuilder().Build()); 34 | 35 | // THEN 36 | Assert.AreEqual("{\"foo\":\"bar\"}", decryptedPayload); 37 | } 38 | 39 | public void TestDecrypt_ShouldReturnDecryptedPayload_WhenPayloadIs128GcmEncrypted() 40 | { 41 | // GIVEN 42 | JweObject jweObject = TestUtils.GetTest128GcmJweObject(); 43 | 44 | // WHEN 45 | string decryptedPayload = jweObject.Decrypt(TestUtils.GetTestJweConfigBuilder().Build()); 46 | 47 | // THEN 48 | Assert.AreEqual("{\"foo\":\"bar\"}", decryptedPayload); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Encryption/JWE/JweEncryptionTest.cs: -------------------------------------------------------------------------------- 1 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 2 | using Mastercard.Developer.ClientEncryption.Core.Encryption.JWE; 3 | using Mastercard.Developer.ClientEncryption.Tests.NetCore.Test; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Encryption.JWE 8 | { 9 | [TestClass] 10 | public class JweEncryptionTest 11 | { 12 | [TestMethod] 13 | #if !NETCOREAPP3_1 && !NET5_0_OR_GREATER 14 | [ExpectedException(typeof(EncryptionException), "AES/GCM/NoPadding is unsupported on .NET Standard < 2.1")] 15 | #endif 16 | public void TestEncryptPayload_ShouldEncryptRootArrays() 17 | { 18 | // GIVEN 19 | const string payload = @"[ 20 | {}, 21 | {} 22 | ]"; 23 | var config = TestUtils.GetTestJweConfigBuilder() 24 | .WithEncryptionPath("$", "$") 25 | .WithDecryptionPath("$.encryptedData", "$") 26 | .Build(); 27 | 28 | // WHEN 29 | var encryptedPayload = JweEncryption.EncryptPayload(payload, config); 30 | 31 | // THEN 32 | var encryptedPayloadObject = JObject.Parse(encryptedPayload); 33 | Assert.IsNotNull(encryptedPayloadObject); 34 | TestUtils.AssertDecryptedJweEquals("[{},{}]", encryptedPayload, config); 35 | } 36 | 37 | [TestMethod] 38 | #if !NETCOREAPP3_1 && !NET5_0_OR_GREATER 39 | [ExpectedException(typeof(EncryptionException), "AES/GCM/NoPadding is unsupported on .NET Standard < 2.1")] 40 | #endif 41 | public void TestEncryptPayload_ShouldCreateEncryptedValue_WhenOutPathParentDoesNotExistInPayload() 42 | { 43 | // GIVEN 44 | const string payload = @"{""data"": {}}"; 45 | var config = TestUtils.GetTestJweConfigBuilder() 46 | .WithEncryptionPath("$", "$.encryptedDataParent") 47 | .Build(); 48 | 49 | // WHEN 50 | var encryptedPayload = JweEncryption.EncryptPayload(payload, config); 51 | 52 | // THEN 53 | var encryptedPayloadObject = JObject.Parse(encryptedPayload); 54 | Assert.IsNull(encryptedPayloadObject["data"]); 55 | Assert.IsNotNull(encryptedPayloadObject["encryptedDataParent"]); 56 | Assert.IsNotNull(encryptedPayloadObject["encryptedDataParent"]["encryptedData"]); 57 | } 58 | 59 | [TestMethod] 60 | #if !NETCOREAPP3_1 && !NET5_0_OR_GREATER 61 | [ExpectedException(typeof(EncryptionException), "AES/GCM/NoPadding is unsupported on .NET Standard < 2.1")] 62 | #endif 63 | public void TestDecryptPayload_ShouldDecryptRootArrays() 64 | { 65 | // GIVEN 66 | const string encryptedPayload = @"{ 67 | ""encryptedData"": ""ew0KICAia2lkIjogIjc2MWIwMDNjMWVhZGUzYTU0OTBlNTAwMGQzNzg4N2JhYTVlNmVjMGUyMjZjMDc3MDZlNTk5NDUxZmMwMzJhNzkiLA0KICAiY3R5IjogImFwcGxpY2F0aW9uL2pzb24iLA0KICAiZW5jIjogIkEyNTZHQ00iLA0KICAiYWxnIjogIlJTQS1PQUVQLTI1NiINCn0.ycN7kc7qfknufAz96VcgA6L-oTeY38S9oD6iFNTyw72sA4r82dyJeHL14z_Zd7V2NoOh1XEro6zPaG78jdRpq62F0Ajyqbupem2a-E5LtKRXXH3CucVlVtMoLfbt2Ao8u_oFLwytMjV_lVO7YH5UsOXjfzL2Bw7KNRdz4wQEQ2yXQpf8ajLPp98RBSLMxStv7vhIDuZxVpCRBx_KqKuB8Y0kF_CxRUQZVlgumCv97rGG_nsW01XfnfekMuAJntnG9Bi9NmrX8bwkLV3YZf7iO_kzR_D-kuyFtaXqUvXBLpUkchESgLX8IXYMLXxevk04VNl2WiPH5gs_VVzSCfXbHw.8l1G7U85BKOQh5Tu.Omcqk8Sung.TNzFb9bt57TioF-zpEOZBA"" 68 | }"; 69 | var config = TestUtils.GetTestJweConfigBuilder() 70 | .WithDecryptionPath("$.encryptedData", "$") 71 | .Build(); 72 | 73 | // WHEN 74 | var payload = JweEncryption.DecryptPayload(encryptedPayload, config); 75 | 76 | // THEN 77 | TestUtils.AssertPayloadEquals("[{},{}]", payload); 78 | } 79 | 80 | [TestMethod] 81 | #if !NETCOREAPP3_1 && !NET5_0_OR_GREATER 82 | [ExpectedException(typeof(EncryptionException), "AES/GCM/NoPadding is unsupported on .NET Standard < 2.1")] 83 | #endif 84 | public void TestDecryptPayload_ShouldSupportPayloadWithEncryptedValueParent() 85 | { 86 | // GIVEN 87 | const string encryptedPayload = @"{ 88 | ""encryptedDataParent"": { 89 | ""encryptedData"": ""ew0KICAia2lkIjogIjc2MWIwMDNjMWVhZGUzYTU0OTBlNTAwMGQzNzg4N2JhYTVlNmVjMGUyMjZjMDc3MDZlNTk5NDUxZmMwMzJhNzkiLA0KICAiY3R5IjogImFwcGxpY2F0aW9uL2pzb24iLA0KICAiZW5jIjogIkEyNTZHQ00iLA0KICAiYWxnIjogIlJTQS1PQUVQLTI1NiINCn0.QHdpgZKcR7e4TNddxHQOTk6R8IYhB4wfnHwUu5d3UhCcr6x6u8KWgPLrn2M95WV1xkjF7jnbOVYO-xE0e5Kx5laIVQNvjYpPxxYqzOQ-F16NwkkGgiZ8UnVi8qYYR1RO2fuqt6xP0_9VNnWpO541_kEMcCrk-6HFRjPU_4AF-wKAUdxInXOrUCOWi7-2uocraAYyLuh1PR0W002p9nVoOY7KXbA-Pf3gmEO9TgCDBFxmtn2dKwYxRPI6tkBYCB2n6h1rIIjQNI4kb-ErV8_vdppIPlB0-o0ONVlHWfnDbgZ3b8SIBEnZNn01MAPbC8visu41ODoNwhuKirD_MzvhTQ.GFYD8LCwXkc2eHPC.T1v8SZikvVAd76g.NbdYvwvKsUVbbSUqaX3Afw"" 90 | } 91 | }"; 92 | var config = TestUtils.GetTestJweConfigBuilder() 93 | .WithDecryptionPath("$.encryptedDataParent.encryptedData", "$") 94 | .Build(); 95 | 96 | // WHEN 97 | var payload = JweEncryption.DecryptPayload(encryptedPayload, config); 98 | 99 | // THEN 100 | TestUtils.AssertPayloadEquals("{\"data\": {}}", payload); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Encryption/JWE/JweHeaderTest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Mastercard.Developer.ClientEncryption.Core.Encryption.JWE; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Encryption.JWE 6 | { 7 | [TestClass] 8 | public class JweHeaderTest 9 | { 10 | [TestMethod] 11 | public void TestToJson_ShouldReturnJsonJweHeader() 12 | { 13 | JweHeader header = new JweHeader("RSA-OAEP-256", "A256GCM", "123", "application/json"); 14 | 15 | Assert.AreEqual( 16 | "{\"kid\":\"123\",\"cty\":\"application/json\",\"enc\":\"A256GCM\",\"alg\":\"RSA-OAEP-256\"}", 17 | header.Json.ToString(Formatting.None) 18 | ); 19 | } 20 | 21 | [TestMethod] 22 | public void TestParseJweHeader_ShouldCorrectlyParseJweHeader() 23 | { 24 | JweHeader header = JweHeader.Parse("eyJraWQiOiI3NjFiMDAzYzFlYWRlM2E1NDkwZTUwMDBkMzc4ODdiYWE1ZTZlYzBlMjI2YzA3NzA2ZTU5OTQ1MWZjMDMyYTc5IiwiY3R5IjoiYXBwbGljYXRpb25cL2pzb24iLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0"); 25 | 26 | Assert.AreEqual("A256GCM", header.Enc); 27 | Assert.AreEqual("RSA-OAEP-256", header.Alg); 28 | Assert.AreEqual("application/json", header.Cty); 29 | Assert.AreEqual("761b003c1eade3a5490e5000d37887baa5e6ec0e226c07706e599451fc032a79", header.Kid); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Encryption/JweConfigBuilderTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 5 | using Mastercard.Developer.ClientEncryption.Tests.NetCore.Test; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Encryption 9 | { 10 | [TestClass] 11 | public class JweConfigBuilderTest 12 | { 13 | [TestMethod] 14 | public void TestBuild_Nominal() 15 | { 16 | var config = JweConfigBuilder.AJweEncryptionConfig() 17 | .WithEncryptionPath("$.payload", "$.encryptedPayload") 18 | .WithEncryptionCertificate(TestUtils.GetTestEncryptionCertificate()) 19 | .WithDecryptionPath("$.encryptedPayload", "$.payload") 20 | .WithDecryptionKey(TestUtils.GetTestDecryptionKey()) 21 | .WithEncryptedValueFieldName("encryptedValue") 22 | .Build(); 23 | 24 | Assert.IsNotNull(config); 25 | Assert.AreEqual(1, config.EncryptionPaths.Count); 26 | Assert.IsNotNull(config.EncryptionCertificate); 27 | Assert.AreEqual(1, config.DecryptionPaths.Count); 28 | Assert.IsNotNull(config.DecryptionKey); 29 | Assert.AreEqual("encryptedValue", config.EncryptedValueFieldName); 30 | Assert.AreEqual(EncryptionConfig.EncryptionScheme.Jwe, config.Scheme); 31 | } 32 | 33 | [TestMethod] 34 | public void TestBuild_ResultShouldBeAssignableToGenericEncryptionConfig() 35 | { 36 | EncryptionConfig config = TestUtils.GetTestJweConfigBuilder().Build(); 37 | Assert.IsNotNull(config); 38 | } 39 | 40 | [TestMethod] 41 | public void TestBuild_ShouldComputeCertificateKeyFingerprint_WhenFingerprintNotSet() 42 | { 43 | EncryptionConfig config = TestUtils.GetTestJweConfigBuilder().Build(); 44 | Assert.AreEqual("761b003c1eade3a5490e5000d37887baa5e6ec0e226c07706e599451fc032a79", config.EncryptionKeyFingerprint); 45 | } 46 | 47 | [TestMethod] 48 | public void TestBuild_ShouldUseEncryptionKeyFingerprint_WhenFingerprintIsSet() 49 | { 50 | EncryptionConfig config = TestUtils.GetTestJweConfigBuilder().WithEncryptionKeyFingerprint("this-is-a-test-fingerprint").Build(); 51 | Assert.AreEqual("this-is-a-test-fingerprint", config.EncryptionKeyFingerprint); 52 | } 53 | 54 | [TestMethod] 55 | [ExpectedException(typeof(EncryptionException))] 56 | public void TestBuild_ShouldThrowEncryptionException_WhenInvalidEncryptionCertificate() 57 | { 58 | TestUtils.GetTestJweConfigBuilder() 59 | .WithEncryptionCertificate(TestUtils.GetTestInvalidEncryptionCertificate() ) 60 | .Build(); 61 | } 62 | 63 | [TestMethod] 64 | public void TestBuild_ShouldFallbackToDefaults() 65 | { 66 | // WHEN 67 | var config = JweConfigBuilder.AJweEncryptionConfig() 68 | .WithEncryptionCertificate(TestUtils.GetTestEncryptionCertificate()) 69 | .Build(); 70 | 71 | // THEN 72 | var expectedDecryptionPaths = new Dictionary {{"$.encryptedData", "$"}}; 73 | var expectedEncryptionPaths = new Dictionary {{ "$", "$" }}; 74 | Assert.AreEqual(expectedDecryptionPaths.Count, config.DecryptionPaths.Count); 75 | Assert.AreEqual(expectedEncryptionPaths.Count, config.EncryptionPaths.Count); 76 | Assert.AreEqual(0, expectedDecryptionPaths.Except(config.DecryptionPaths).Count()); 77 | Assert.AreEqual(0, expectedDecryptionPaths.Except(config.DecryptionPaths).Count()); 78 | Assert.AreEqual("encryptedData", config.EncryptedValueFieldName); 79 | } 80 | 81 | [TestMethod] 82 | [ExpectedException(typeof(ArgumentException))] 83 | public void TestBuild_ShouldThrowArgumentException_WhenMissingDecryptionKey() 84 | { 85 | try 86 | { 87 | JweConfigBuilder.AJweEncryptionConfig().Build(); 88 | } 89 | catch (Exception e) 90 | { 91 | Assert.AreEqual("You must include at least an encryption certificate or a decryption key", e.Message); 92 | throw; 93 | } 94 | } 95 | 96 | [TestMethod] 97 | [ExpectedException(typeof(ArgumentException))] 98 | public void TestBuild_ShouldThrowArgumentException_WhenNotDefiniteDecryptionPath() 99 | { 100 | try 101 | { 102 | JweConfigBuilder.AJweEncryptionConfig() 103 | .WithDecryptionPath("$.encryptedPayloads[*]", "$.payload") 104 | .WithDecryptionKey(TestUtils.GetTestDecryptionKey()) 105 | .Build(); 106 | } 107 | catch (Exception e) 108 | { 109 | Assert.AreEqual("JSON paths for decryption must point to a single item!", e.Message); 110 | throw; 111 | } 112 | } 113 | 114 | [TestMethod] 115 | [ExpectedException(typeof(ArgumentException))] 116 | public void TestBuild_ShouldThrowArgumentException_WhenNotDefiniteEncryptionPath() 117 | { 118 | try 119 | { 120 | JweConfigBuilder.AJweEncryptionConfig() 121 | .WithEncryptionPath("$.payloads[*]", "$.encryptedPayload") 122 | .WithEncryptionCertificate(TestUtils.GetTestEncryptionCertificate()) 123 | .Build(); 124 | } 125 | catch (Exception e) 126 | { 127 | Assert.AreEqual("JSON paths for encryption must point to a single item!", e.Message); 128 | throw; 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Encryption/RSA/RsaEncryptionTest.cs: -------------------------------------------------------------------------------- 1 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 2 | using Mastercard.Developer.ClientEncryption.Tests.NetCore.Test; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using System; 5 | using System.Linq; 6 | using System.Security.Cryptography.X509Certificates; 7 | 8 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Encryption 9 | { 10 | [TestClass] 11 | public class RsaEncryptionTest 12 | { 13 | [TestMethod] 14 | public void TestWrapUnwrapSecretKey_ShouldReturnTheOriginalKey() 15 | { 16 | // GIVEN 17 | var config = TestUtils.GetTestFieldLevelEncryptionConfigBuilder().Build(); 18 | var originalKeyBytes = Convert.FromBase64String("mZzmzoURXI3Vk0vdsPkcFw=="); 19 | 20 | // WHEN 21 | var wrappedKeyBytes = RsaEncryption.WrapSecretKey(config.EncryptionCertificate.GetRSAPublicKey(), originalKeyBytes, config.OaepPaddingDigestAlgorithm); 22 | var unwrappedKeyBytes = RsaEncryption.UnwrapSecretKey(config, wrappedKeyBytes, config.OaepPaddingDigestAlgorithm); 23 | 24 | // THEN 25 | Assert.IsTrue(originalKeyBytes.SequenceEqual(unwrappedKeyBytes)); 26 | } 27 | 28 | [TestMethod] 29 | public void TestUnwrapSecretKey_InteroperabilityTest_OaepSha256() 30 | { 31 | // GIVEN 32 | var config = TestUtils.GetTestFieldLevelEncryptionConfigBuilder() 33 | .WithOaepPaddingDigestAlgorithm("SHA-256") 34 | .Build(); 35 | const string wrappedKey = "ZLB838BRWW2/BtdFFAWBRYShw/gBxXSwItpxEZ9zaSVEDHo7n+SyVYU7mayd+9vHkR8OdpqwpXM68t0VOrWI8LD8A2pRaYx8ICyhVFya4OeiWlde05Rhsk+TNwwREPbiw1RgjT8aedRJJYbAZdLb9XEI415Kb/UliHyvsdHMb6vKyYIjUHB/pSGAAmgds56IhIJGfvnBLPZfSHmGgiBT8WXLRuuf1v48aIadH9S0FfoyVGTaLYr+2eznSTAFC0ZBnzebM3mQI5NGQNviTnEJ0y+uZaLE/mthiKgkv1ZybyDPx2xJK2n05sNzfIWKmnI/SOb65RZLlo1Q+N868l2m9g=="; 36 | var wrappedKeyBytes = Convert.FromBase64String(wrappedKey); 37 | 38 | // WHEN 39 | var unwrappedKeyBytes = RsaEncryption.UnwrapSecretKey(config, wrappedKeyBytes, config.OaepPaddingDigestAlgorithm); 40 | 41 | // THEN 42 | var expectedKeyBytes = Convert.FromBase64String("mZzmzoURXI3Vk0vdsPkcFw=="); 43 | Assert.IsTrue(expectedKeyBytes.SequenceEqual(unwrappedKeyBytes)); 44 | } 45 | 46 | [TestMethod] 47 | public void TestUnwrapSecretKey_InteroperabilityTest_OaepSha512() 48 | { 49 | // GIVEN 50 | var config = TestUtils.GetTestFieldLevelEncryptionConfigBuilder() 51 | .WithOaepPaddingDigestAlgorithm("SHA-512") 52 | .Build(); 53 | const string wrappedKey = "RuruMYP5rG6VP5vS4kVznIrSOjUzXyOhtD7bYlVqwniWTvxxZC73UDluwDhpLwX5QJCsCe8TcwGiQRX1u+yWpBveHDRmDa03hrc3JRJALEKPyN5tnt5w7aI4dLRnLuNoXbYoTSc4V47Z3gaaK6q2rEjydx2sQ/SyVmeUJN7NgxkhtHTyVWTymEM1ythL+AaaQ5AaXedhpWKhG06XYZIX4KV7T9cHEn+See6RVGGB2RUPHBJjrxJo5JoVSfnWN0gkTMyuwbmVaTWfsowbvh8GFibFT7h3uXyI3b79NiauyB7scXp9WidGues3MrTx4dKZrSbs3uHxzPKmCDZimuKfwg=="; 54 | var wrappedKeyBytes = Convert.FromBase64String(wrappedKey); 55 | 56 | // WHEN 57 | var unwrappedKeyBytes = RsaEncryption.UnwrapSecretKey(config, wrappedKeyBytes, config.OaepPaddingDigestAlgorithm); 58 | 59 | // THEN 60 | var expectedKeyBytes = Convert.FromBase64String("mZzmzoURXI3Vk0vdsPkcFw=="); 61 | Assert.IsTrue(expectedKeyBytes.SequenceEqual(unwrappedKeyBytes)); 62 | } 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Interceptors/RestResponseDouble.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using RestSharp; 5 | using RestRequest = RestSharp.RestRequest; 6 | using RestResponse = RestSharp.RestResponse; 7 | 8 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Interceptors 9 | { 10 | internal class RestResponseDouble : RestResponse 11 | { 12 | public new RestRequest Request { get; set; } = null; 13 | public new Uri ResponseUri { get; set; } = null; 14 | public new string Server { get; set; } 15 | public new byte[] RawBytes { get; set; } = null; 16 | public new string ContentType { get; set; } = null; 17 | public new long ContentLength { get; set; } 18 | public new string ContentEncoding { get; set; } 19 | public new IList Headers { get; } 20 | public new ResponseStatus ResponseStatus { get; set; } 21 | public new string ErrorMessage { get; set; } 22 | public new Exception ErrorException { get; set; } 23 | public Version ProtocolVersion { get; set; } 24 | public new HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK; 25 | public new bool IsSuccessful => true; 26 | 27 | public new string StatusDescription { get; set; } = null; 28 | 29 | private Lazy _content; 30 | 31 | public RestResponseDouble(IList headers, string content) 32 | { 33 | Headers = headers; 34 | Content = content; 35 | base.Headers = (IReadOnlyCollection)headers; 36 | base.Content = content; 37 | } 38 | 39 | public new string Content 40 | { 41 | get => _content.Value; 42 | set => _content = new Lazy(() => value); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Interceptors/RestSharpV2CbcJweEncryptionInterceptorTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 3 | using Mastercard.Developer.ClientEncryption.RestSharpV2.Interceptors; 4 | using Mastercard.Developer.ClientEncryption.Tests.NetCore.Test; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using Method = RestSharp.Method; 7 | using Header = RestSharp.HeaderParameter; 8 | using ParameterType = RestSharp.ParameterType; 9 | using RestRequest = RestSharp.RestRequest; 10 | using RestSharp; 11 | 12 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Interceptors 13 | { 14 | [TestClass] 15 | public class RestSharpV2CbcJweEncryptionInterceptorTest 16 | { 17 | [TestMethod] 18 | public void TestFrom_ShouldReturnTheCorrectInterceptor() 19 | { 20 | // GIVEN 21 | EncryptionConfig config = TestUtils.GetTestJweConfigBuilder().Build(); 22 | 23 | // WHEN 24 | RestSharpEncryptionInterceptor interceptor = RestSharpEncryptionInterceptor.From(config); 25 | 26 | // THEN 27 | Assert.IsTrue(interceptor is RestSharpJweEncryptionInterceptor); 28 | } 29 | 30 | [TestMethod] 31 | public void TestIntercept_ShouldDoNothing_WhenNoPayload() 32 | { 33 | // GIVEN 34 | var config = TestUtils.GetTestJweConfigBuilder().Build(); 35 | 36 | // WHEN 37 | var request = new RestRequest 38 | { 39 | Method = Method.Get, 40 | Resource = "/service" 41 | }; 42 | 43 | // THEN 44 | var fixture = RestSharpEncryptionInterceptor.From(config); 45 | fixture.InterceptRequest(request); 46 | } 47 | 48 | [TestMethod] 49 | public void TestInterceptResponse_ShouldDecryptWithA128CBC_HS256Encryption() 50 | { 51 | // GIVEN 52 | string encryptedPayload = "{" + 53 | "\"encryptedPayload\":\"eyJraWQiOiI3NjFiMDAzYzFlYWRlM2E1NDkwZTUwMDBkMzc4ODdiYWE1ZTZlYzBlMjI2YzA3NzA2ZTU5OTQ1MWZjMDMyYTc5IiwiY3R5IjoiYXBwbGljYXRpb25cL2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.5bsamlChk0HR3Nqg2UPJ2Fw4Y0MvC2pwWzNv84jYGkOXyqp1iwQSgETGaplIa7JyLg1ZWOqwNHEx3N7gsN4nzwAnVgz0eta6SsoQUE9YQ-5jek0COslUkoqIQjlQYJnYur7pqttDibj87fcw13G2agle5fL99j1QgFPjNPYqH88DMv481XGFa8O3VfJhW93m73KD2gvE5GasOPOkFK9wjKXc9lMGSgSArp3Awbc_oS2Cho_SbsvuEQwkhnQc2JKT3IaSWu8yK7edNGwD6OZJLhMJzWJlY30dUt2Eqe1r6kMT0IDRl7jHJnVIr2Qpe56CyeZ9V0aC5RH1mI5dYk4kHg.yI0CS3NdBrz9CCW2jwBSDw.6zr2pOSmAGdlJG0gbH53Eg.UFgf3-P9UjgMocEu7QA_vQ\"}"; 54 | var config = TestUtils.GetTestJweConfigBuilder() 55 | .WithDecryptionPath("$.encryptedPayload", "$.foo") 56 | .Build(); 57 | 58 | // WHEN 59 | var response = RestResponseWithContentLength(encryptedPayload); 60 | var fixture = RestSharpEncryptionInterceptor.From(config); 61 | fixture.InterceptResponse(response); 62 | 63 | // THEN 64 | var payload = response.Content; 65 | TestUtils.AssertPayloadEquals("{\"foo\":\"bar\"}", payload); 66 | var contentLengthHeaderParam = response.Headers.FirstOrDefault(param => param.Type == ParameterType.HttpHeader); 67 | Assert.IsNotNull(contentLengthHeaderParam); 68 | Assert.AreEqual(payload.Length.ToString(), contentLengthHeaderParam.Value); 69 | } 70 | 71 | [TestMethod] 72 | public void TestInterceptResponse_ShouldDoNothing_WhenNoPayload() 73 | { 74 | // GIVEN 75 | var config = TestUtils.GetTestJweConfigBuilder().Build(); 76 | var response = new RestResponseDouble(null, null); 77 | 78 | // WHEN 79 | var fixture = RestSharpEncryptionInterceptor.From(config); 80 | fixture.InterceptResponse(response); 81 | } 82 | 83 | [TestMethod] 84 | [ExpectedException(typeof(EncryptionException))] // <-- THEN 85 | public void TestInterceptResponse_ShouldThrowException_WhenDecryptionFails() 86 | { 87 | // GIVEN 88 | string encryptedPayload = "{\"encryptedPayload\":\"NOT-VALID\"}"; 89 | var config = TestUtils.GetTestJweConfigBuilder() 90 | .WithDecryptionPath("$", "$") 91 | .WithEncryptedValueFieldName("encryptedPayload") 92 | .Build(); 93 | var response = RestResponseWithContentLength(encryptedPayload); 94 | 95 | // WHEN 96 | var fixture = RestSharpEncryptionInterceptor.From(config); 97 | fixture.InterceptResponse(response); 98 | } 99 | 100 | internal static RestResponse RestResponseWithContentLength(string content) 101 | { 102 | Header[] headers = { new Header("Content-Length", content.Length.ToString()) }; 103 | return new RestResponseDouble(headers, content); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Interceptors/RestSharpV2GcmJweEncryptionInterceptorTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 3 | using Mastercard.Developer.ClientEncryption.RestSharpV2.Interceptors; 4 | using Mastercard.Developer.ClientEncryption.Tests.NetCore.Test; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using RestSharp; 7 | using Method = RestSharp.Method; 8 | using ParameterType = RestSharp.ParameterType; 9 | using RestRequest = RestSharp.RestRequest; 10 | 11 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Interceptors 12 | { 13 | [TestClass] 14 | public class RestSharpV2GcmJweEncryptionInterceptorTest 15 | { 16 | [TestMethod] 17 | #if !NETCOREAPP3_1 && !NET5_0_OR_GREATER 18 | [ExpectedException(typeof(EncryptionException), "AES/GCM/NoPadding is unsupported on .NET Standard < 2.1")] 19 | # endif 20 | public void TestIntercept_ShouldEncryptRequestPayloadAndUpdateContentLengthHeader() 21 | { 22 | // GIVEN 23 | var config = TestUtils.GetTestJweConfigBuilder() 24 | .WithEncryptionPath("$","$") 25 | .WithEncryptedValueFieldName("encryptedFoo") 26 | .Build(); 27 | var request = new RestRequest 28 | { 29 | Method = Method.Post, 30 | Resource = "/service" 31 | }; 32 | request.AddBody("{\"foo\":\"bar\"}"); 33 | 34 | // WHEN 35 | var fixture = RestSharpEncryptionInterceptor.From(config); 36 | fixture.InterceptRequest(request); 37 | 38 | // THEN 39 | var encryptedPayloadParam = request.Parameters.FirstOrDefault(param => param.Type == ParameterType.RequestBody); 40 | Assert.IsNotNull(encryptedPayloadParam); 41 | var encryptedPayload = encryptedPayloadParam.Value?.ToString(); 42 | Assert.IsNotNull(encryptedPayload); 43 | Assert.IsFalse(encryptedPayload.Contains("foo")); 44 | Assert.IsTrue(encryptedPayload.Contains("encryptedFoo")); 45 | var contentLengthHeaderParam = request.Parameters.FirstOrDefault(param => param.Type == ParameterType.HttpHeader); 46 | Assert.IsNotNull(contentLengthHeaderParam); 47 | Assert.AreEqual(encryptedPayload.Length.ToString(), contentLengthHeaderParam.Value); 48 | } 49 | 50 | [TestMethod] 51 | #if !NETCOREAPP3_1 && !NET5_0_OR_GREATER 52 | [ExpectedException(typeof(EncryptionException), "AES/GCM/NoPadding is unsupported on .NET Standard < 2.1")] 53 | # endif 54 | public void TestInterceptResponse_ShouldDecryptResponsePayloadAndUpdateContentLengthHeader() 55 | { 56 | // GIVEN 57 | const string encryptedPayload = "{" + 58 | "\"encryptedPayload\":\"eyJraWQiOiI3NjFiMDAzYzFlYWRlM2E1NDkwZTUwMDBkMzc4ODdiYWE1ZTZlYzBlMjI2YzA3NzA2ZTU5OTQ1MWZjMDMyYTc5IiwiY3R5IjoiYXBwbGljYXRpb25cL2pzb24iLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.8c6vxeZOUBS8A9SXYUSrRnfl1ht9xxciB7TAEv84etZhQQ2civQKso-htpa2DWFBSUm-UYlxb6XtXNXZxuWu-A0WXjwi1K5ZAACc8KUoYnqPldEtC9Q2bhbQgc_qZF_GxeKrOZfuXc9oi45xfVysF_db4RZ6VkLvY2YpPeDGEMX_nLEjzqKaDz_2m0Ae_nknr0p_Nu0m5UJgMzZGR4Sk1DJWa9x-WJLEyo4w_nRDThOjHJshOHaOU6qR5rdEAZr_dwqnTHrjX9Qm9N9gflPGMaJNVa4mvpsjz6LJzjaW3nJ2yCoirbaeJyCrful6cCiwMWMaDMuiBDPKa2ovVTy0Sw.w0Nkjxl0T9HHNu4R.suRZaYu6Ui05Z3-vsw.akknMr3Dl4L0VVTGPUszcA\"}"; 59 | var config = TestUtils.GetTestJweConfigBuilder() 60 | .WithDecryptionPath("$.encryptedPayload", "$") 61 | .Build(); 62 | 63 | // WHEN 64 | var response = RestSharpV2CbcJweEncryptionInterceptorTest.RestResponseWithContentLength(encryptedPayload); 65 | var fixture = RestSharpEncryptionInterceptor.From(config); 66 | fixture.InterceptResponse(response); 67 | 68 | // THEN 69 | var payload = response.Content; 70 | TestUtils.AssertPayloadEquals("{\"foo\":\"bar\"}", payload); 71 | var contentLengthHeaderParam = response.Headers.FirstOrDefault(param => param.Type == ParameterType.HttpHeader); 72 | Assert.IsNotNull(contentLengthHeaderParam); 73 | Assert.AreEqual(payload.Length.ToString(), contentLengthHeaderParam.Value); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Test/TestUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Security.Cryptography.X509Certificates; 3 | using Mastercard.Developer.ClientEncryption.Core.Encryption; 4 | using Mastercard.Developer.ClientEncryption.Core.Encryption.JWE; 5 | using Mastercard.Developer.ClientEncryption.Core.Utils; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using Newtonsoft.Json.Linq; 8 | // ReSharper disable once InconsistentNaming 9 | 10 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Test 11 | { 12 | internal static class TestUtils 13 | { 14 | internal static X509Certificate2 GetTestEncryptionCertificate() 15 | { 16 | return EncryptionUtils.LoadEncryptionCertificate("./_Resources/Certificates/test_certificate-2048.pem"); 17 | } 18 | 19 | internal static X509Certificate2 GetTestInvalidEncryptionCertificate() => new X509Certificate2(); // No key 20 | 21 | internal static RSA GetTestDecryptionKey() 22 | { 23 | return EncryptionUtils.LoadDecryptionKey("./_Resources/Keys/Pkcs8/test_key_pkcs8-2048.der"); 24 | } 25 | 26 | internal static FieldLevelEncryptionConfigBuilder GetTestFieldLevelEncryptionConfigBuilder() 27 | { 28 | return FieldLevelEncryptionConfigBuilder.AFieldLevelEncryptionConfig() 29 | .WithEncryptionCertificate(GetTestEncryptionCertificate()) 30 | .WithDecryptionKey(GetTestDecryptionKey()) 31 | .WithOaepPaddingDigestAlgorithm("SHA-256") 32 | .WithEncryptedValueFieldName("encryptedValue") 33 | .WithEncryptedKeyFieldName("encryptedKey") 34 | .WithIvFieldName("iv") 35 | .WithOaepPaddingDigestAlgorithmFieldName("oaepHashingAlgorithm") 36 | .WithEncryptionCertificateFingerprintFieldName("encryptionCertificateFingerprint") 37 | .WithEncryptionCertificateFingerprint("80810fc13a8319fcf0e2ec322c82a4c304b782cc3ce671176343cfe8160c2279") 38 | .WithEncryptionKeyFingerprintFieldName("encryptionKeyFingerprint") 39 | .WithEncryptionKeyFingerprint("761b003c1eade3a5490e5000d37887baa5e6ec0e226c07706e599451fc032a79") 40 | .WithValueEncoding(FieldLevelEncryptionConfig.FieldValueEncoding.Hex); 41 | } 42 | 43 | internal static void AssertDecryptedPayloadEquals(string expectedPayload, string encryptedPayload, FieldLevelEncryptionConfig config) 44 | { 45 | var payloadString = FieldLevelEncryption.DecryptPayload(encryptedPayload, config); 46 | AssertPayloadEquals(expectedPayload, payloadString); 47 | } 48 | 49 | internal static void AssertDecryptedJweEquals(string expectedPayload, string encryptedPayload, JweConfig config) 50 | { 51 | var payloadString = JweEncryption.DecryptPayload(encryptedPayload, config); 52 | AssertPayloadEquals(expectedPayload, payloadString); 53 | } 54 | 55 | internal static void AssertPayloadEquals(string expectedPayload, string payload) 56 | { 57 | var expectedPayloadToken = JsonUtils.ParsePayload(expectedPayload); 58 | var payloadToken = JsonUtils.ParsePayload(payload); 59 | Assert.AreEqual(expectedPayloadToken.ToString(), payloadToken.ToString()); 60 | } 61 | 62 | internal static JweConfigBuilder GetTestJweConfigBuilder() 63 | { 64 | return JweConfigBuilder.AJweEncryptionConfig() 65 | .WithEncryptionCertificate(GetTestEncryptionCertificate()) 66 | .WithDecryptionKey(GetTestDecryptionKey()); 67 | } 68 | 69 | internal static JweObject GetTestCbcJweObject() 70 | { 71 | return JweObject.Parse("eyJraWQiOiI3NjFiMDAzYzFlYWRlM2E1NDkwZTUwMDBkMzc4ODdiYWE1ZTZlYzBlMjI2YzA3NzA2ZTU5OTQ1MWZjMDMyYTc5IiwiY3R5IjoiYXBwbGljYXRpb25cL2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.5bsamlChk0HR3Nqg2UPJ2Fw4Y0MvC2pwWzNv84jYGkOXyqp1iwQSgETGaplIa7JyLg1ZWOqwNHEx3N7gsN4nzwAnVgz0eta6SsoQUE9YQ-5jek0COslUkoqIQjlQYJnYur7pqttDibj87fcw13G2agle5fL99j1QgFPjNPYqH88DMv481XGFa8O3VfJhW93m73KD2gvE5GasOPOkFK9wjKXc9lMGSgSArp3Awbc_oS2Cho_SbsvuEQwkhnQc2JKT3IaSWu8yK7edNGwD6OZJLhMJzWJlY30dUt2Eqe1r6kMT0IDRl7jHJnVIr2Qpe56CyeZ9V0aC5RH1mI5dYk4kHg.yI0CS3NdBrz9CCW2jwBSDw.6zr2pOSmAGdlJG0gbH53Eg.UFgf3-P9UjgMocEu7QA_vQ"); 72 | } 73 | 74 | internal static JweObject GetTest256GcmJweObject() 75 | { 76 | return JweObject.Parse("eyJraWQiOiI3NjFiMDAzYzFlYWRlM2E1NDkwZTUwMDBkMzc4ODdiYWE1ZTZlYzBlMjI2YzA3NzA2ZTU5OTQ1MWZjMDMyYTc5IiwiY3R5IjoiYXBwbGljYXRpb25cL2pzb24iLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.8c6vxeZOUBS8A9SXYUSrRnfl1ht9xxciB7TAEv84etZhQQ2civQKso-htpa2DWFBSUm-UYlxb6XtXNXZxuWu-A0WXjwi1K5ZAACc8KUoYnqPldEtC9Q2bhbQgc_qZF_GxeKrOZfuXc9oi45xfVysF_db4RZ6VkLvY2YpPeDGEMX_nLEjzqKaDz_2m0Ae_nknr0p_Nu0m5UJgMzZGR4Sk1DJWa9x-WJLEyo4w_nRDThOjHJshOHaOU6qR5rdEAZr_dwqnTHrjX9Qm9N9gflPGMaJNVa4mvpsjz6LJzjaW3nJ2yCoirbaeJyCrful6cCiwMWMaDMuiBDPKa2ovVTy0Sw.w0Nkjxl0T9HHNu4R.suRZaYu6Ui05Z3-vsw.akknMr3Dl4L0VVTGPUszcA"); 77 | } 78 | 79 | internal static JweObject GetTest128GcmJweObject() 80 | { 81 | return JweObject.Parse("eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.WtvYljbsjdEv-Ttxx1p6PgyIrOsLpj1FMF9NQNhJUAHlKchAo5QImgEgIdgJE7HC2KfpNcHiQVqKKZq_y201FVzpicDkNzlPJr5kIH4Lq-oC5iP0agWeou9yK5vIxFRP__F_B8HSuojBJ3gDYT_KdYffUIHkm_UysNj4PW2RIRlafJ6RKYanVzk74EoKZRG7MIr3pTU6LIkeQUW41qYG8hz6DbGBOh79Nkmq7Oceg0ZwCn1_MruerP-b15SGFkuvOshStT5JJp7OOq82gNAOkMl4fylEj2-vADjP7VSK8GlqrA7u9Tn-a4Q28oy0GOKr1Z-HJgn_CElknwkUTYsWbg.PKl6_kvZ4_4MjmjW.AH6pGFkn7J49hBQcwg.zdyD73TcuveImOy4CRnVpw"); 82 | } 83 | 84 | internal static JweObject GetTest192GcmJweObject() 85 | { 86 | return JweObject.Parse("eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.FWC8PVaZoR2TRKwKO4syhSJReezVIvtkxU_yKh4qODNvlVr8t8ttvySJ-AjM8xdI6vNyIg9jBMWASG4cE49jT9FYuQ72fP4R-Td4vX8wpB8GonQj40yLqZyfRLDrMgPR20RcQDW2ThzLXsgI55B5l5fpwQ9Nhmx8irGifrFWOcJ_k1dUSBdlsHsYxkjRKMENu5x4H6h12gGZ21aZSPtwAj9msMYnKLdiUbdGmGG_P8a6gPzc9ih20McxZk8fHzXKujjukr_1p5OO4o1N4d3qa-YI8Sns2fPtf7xPHnwi1wipmCC6ThFLU80r3173RXcpyZkF8Y3UacOS9y1f8eUfVQ.JRE7kZLN4Im1Rtdb.eW_lJ-U330n0QHqZnQ._r5xYVvMCrvICwLz4chjdw"); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Utils/Base64UtilsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mastercard.Developer.ClientEncryption.Core.Utils; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Utils 6 | { 7 | [TestClass] 8 | public class Base64UtilsTest 9 | { 10 | private static readonly Tuple TestVector1 = Tuple.Create("Ct6FrAEg4OVsWaeI+2wNIG0RSCFEW/PLWzueZqUbwDw=", "Ct6FrAEg4OVsWaeI-2wNIG0RSCFEW_PLWzueZqUbwDw"); 11 | private static readonly Tuple TestVector2 = Tuple.Create("zRbseYGWpTaOpr4o+aBDjwQ9Pn/yHTxm5uPt0JKIH5nRdg==", "zRbseYGWpTaOpr4o-aBDjwQ9Pn_yHTxm5uPt0JKIH5nRdg"); 12 | 13 | [TestMethod] 14 | public void TestBase64URLEncode() 15 | { 16 | var vectors = new Tuple[]{ TestVector1, TestVector2 }; 17 | foreach (var vector in vectors) 18 | { 19 | Assert.AreEqual( 20 | vector.Item2, 21 | Base64Utils.URLEncode( 22 | Convert.FromBase64String(vector.Item1) 23 | ) 24 | ); 25 | } 26 | } 27 | 28 | [TestMethod] 29 | public void TestBase64URLDecode() 30 | { 31 | var vectors = new Tuple[] { TestVector1, TestVector2 }; 32 | foreach (var vector in vectors) 33 | { 34 | CollectionAssert.AreEqual( 35 | Convert.FromBase64String(vector.Item1), 36 | Base64Utils.URLDecode(vector.Item2) 37 | ); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Utils/ByteUtilsTest.cs: -------------------------------------------------------------------------------- 1 | using Mastercard.Developer.ClientEncryption.Core.Utils; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Utils 5 | { 6 | [TestClass] 7 | public class ByteUtilsTest 8 | { 9 | [TestMethod] 10 | public void TestByteCountWithRemainder() 11 | { 12 | // GIVEN 13 | const int bitLength = 33; 14 | 15 | // WHEN 16 | int byteCount = ByteUtils.ByteCount(bitLength); 17 | 18 | // THEN 19 | Assert.AreEqual(5, byteCount); 20 | } 21 | 22 | [TestMethod] 23 | public void TestByteCountWithoutRemainder() 24 | { 25 | // GIVEN 26 | const int bitLength = 64; 27 | 28 | // WHEN 29 | int byteCount = ByteUtils.ByteCount(bitLength); 30 | 31 | // THEN 32 | Assert.AreEqual(8, byteCount); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Utils/EncodingUtilsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | using Mastercard.Developer.ClientEncryption.Core.Utils; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Utils 9 | { 10 | [TestClass] 11 | public class EncodingUtilsTest 12 | { 13 | [TestMethod] 14 | public void TestHexEncode() 15 | { 16 | Assert.AreEqual("00", EncodingUtils.HexEncode(new byte[1])); 17 | Assert.AreEqual("736f6d652064617461", EncodingUtils.HexEncode(Encoding.ASCII.GetBytes("some data"))); 18 | Assert.AreEqual("", EncodingUtils.HexEncode(Encoding.ASCII.GetBytes(""))); 19 | } 20 | 21 | [TestMethod] 22 | [ExpectedException(typeof(ArgumentNullException))] 23 | public void TestHexEncode_ShouldThrowArgumentNullException_WhenNullValue() 24 | { 25 | EncodingUtils.HexEncode(null); 26 | } 27 | 28 | [TestMethod] 29 | public void TestHexEncode_ShouldKeepLeadingZeros() 30 | { 31 | var hex = EncodingUtils.HexEncode(SHA256.Create().ComputeHash(Encoding.ASCII.GetBytes("WIDDIES"))); 32 | Assert.AreEqual("000000c71f1bda5b63f5165243e10394bc9ebf62e394ef7c6e049c920ea1b181", hex); 33 | } 34 | 35 | [TestMethod] 36 | public void TestHexDecode() 37 | { 38 | Assert.IsTrue(new byte[1].SequenceEqual(EncodingUtils.HexDecode("00"))); 39 | Assert.IsTrue(Encoding.ASCII.GetBytes("some data").SequenceEqual(EncodingUtils.HexDecode("736f6d652064617461"))); 40 | Assert.IsTrue(Encoding.ASCII.GetBytes("some data").SequenceEqual(EncodingUtils.HexDecode("736F6D652064617461"))); 41 | Assert.IsTrue(Encoding.ASCII.GetBytes("").SequenceEqual(EncodingUtils.HexDecode(""))); 42 | } 43 | 44 | [TestMethod] 45 | [ExpectedException(typeof(FormatException))] 46 | public void TestHexDecode_ShouldThrowFormatException_WhenNotAnHexValue() 47 | { 48 | EncodingUtils.HexDecode("not an hex string!"); 49 | } 50 | 51 | [TestMethod] 52 | [ExpectedException(typeof(ArgumentNullException))] 53 | public void TestHexDecode_ShouldThrowArgumentNullException_WhenNullValue() 54 | { 55 | EncodingUtils.HexDecode(null); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Utils/EncryptionUtilsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Cryptography.X509Certificates; 4 | using Mastercard.Developer.ClientEncryption.Core.Utils; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Utils 8 | { 9 | [TestClass] 10 | public class EncryptionUtilsTest 11 | { 12 | [TestMethod] 13 | public void TestLoadEncryptionCertificate_ShouldSupportDer() 14 | { 15 | // GIVEN 16 | const string certificatePath = "./_Resources/Certificates/test_certificate-2048.der"; 17 | 18 | // WHEN 19 | var certificate = EncryptionUtils.LoadEncryptionCertificate(certificatePath); 20 | 21 | // THEN 22 | Assert.IsNotNull(certificate); 23 | Assert.AreEqual("X509", certificate.GetFormat()); 24 | Assert.IsNotNull(certificate.GetRSAPublicKey()); 25 | Assert.AreNotEqual("RSACryptoServiceProvider", certificate.GetRSAPublicKey().GetType().Name); // We expect a RSACng (Windows) or a RSAOpenSsl (Linux, macOS) 26 | } 27 | 28 | [TestMethod] 29 | public void TestLoadEncryptionCertificate_ShouldSupportPem() 30 | { 31 | // GIVEN 32 | const string certificatePath = "./_Resources/Certificates/test_certificate-2048.pem"; 33 | 34 | // WHEN 35 | var certificate = EncryptionUtils.LoadEncryptionCertificate(certificatePath); 36 | 37 | // THEN 38 | Assert.IsNotNull(certificate); 39 | Assert.AreEqual("X509", certificate.GetFormat()); 40 | Assert.IsNotNull(certificate.GetRSAPublicKey()); 41 | Assert.AreNotEqual("RSACryptoServiceProvider", certificate.GetRSAPublicKey().GetType().Name); 42 | } 43 | 44 | [TestMethod] 45 | public void TestLoadDecryptionKey_ShouldSupportUnencryptedKeyFile() 46 | { 47 | // GIVEN 48 | const string keyPath = "./_Resources/Keys/Pkcs8/test_key_pkcs8-2048.der"; 49 | 50 | // WHEN 51 | var privateKey = EncryptionUtils.LoadDecryptionKey(keyPath); 52 | 53 | // THEN 54 | Assert.IsNotNull(privateKey); 55 | Assert.AreNotEqual("RSACryptoServiceProvider", privateKey.GetType().Name); 56 | Assert.AreEqual(2048, privateKey.KeySize); 57 | } 58 | 59 | [TestMethod] 60 | public void TestLoadDecryptionKey_ShouldSupportPkcs12() 61 | { 62 | // GIVEN 63 | const string keyContainerPath = "./_Resources/Keys/Pkcs12/test_key.p12"; 64 | const string keyAlias = "mykeyalias"; 65 | const string keyPassword = "Password1"; 66 | 67 | // WHEN 68 | const X509KeyStorageFlags flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable; // https://github.com/dotnet/corefx/issues/14745 69 | var privateKey = EncryptionUtils.LoadDecryptionKey(keyContainerPath, keyAlias, keyPassword, flags); 70 | 71 | // THEN 72 | Assert.AreNotEqual("RSACryptoServiceProvider", privateKey.GetType().Name); 73 | Assert.AreEqual(2048, privateKey.KeySize); 74 | } 75 | 76 | [TestMethod] 77 | public void TestLoadDecryptionKey_ShouldSupportByteArray() 78 | { 79 | // GIVEN 80 | const string keyPath = "./_Resources/Keys/Pkcs8/test_key_pkcs8-2048.der"; 81 | var keyBytes = File.ReadAllBytes(keyPath); 82 | 83 | // WHEN 84 | var privateKey = EncryptionUtils.LoadDecryptionKey(keyBytes); 85 | 86 | // THEN 87 | Assert.AreNotEqual("RSACryptoServiceProvider", privateKey.GetType().Name); 88 | Assert.AreEqual(2048, privateKey.KeySize); 89 | } 90 | 91 | [TestMethod] 92 | [ExpectedException(typeof(ArgumentException))] // THEN 93 | public void TestLoadDecryptionKey_ShouldThrowArgumentException_WhenInvalidKey() 94 | { 95 | // GIVEN 96 | const string keyPath = "./_Resources/Keys/Pkcs8/test_invalid_key.der"; 97 | 98 | // WHEN 99 | EncryptionUtils.LoadDecryptionKey(keyPath); 100 | } 101 | 102 | [TestMethod] 103 | [ExpectedException(typeof(FileNotFoundException))] // THEN 104 | public void TestLoadDecryptionKey_ShouldThrowFileNotFoundException_WhenKeyFileDoesNotExist() 105 | { 106 | // GIVEN 107 | const string keyPath = "./_Resources/Keys/some_file"; 108 | 109 | // WHEN 110 | EncryptionUtils.LoadDecryptionKey(keyPath); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Utils/JsonUtilsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mastercard.Developer.ClientEncryption.Core.Utils; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Utils 7 | { 8 | [TestClass] 9 | public class JsonUtilsTest 10 | { 11 | [TestMethod] 12 | public void TestGetParentJsonPath_Nominal() 13 | { 14 | // GIVEN 15 | const string jsonPath1 = "$['obj1']['obj2']['obj3']"; 16 | const string jsonPath2 = "obj1.obj2"; 17 | const string jsonPath3 = "$.obj1.obj2"; 18 | const string jsonPath4 = "obj1"; 19 | 20 | // WHEN 21 | var parentJsonPath1 = JsonUtils.GetParentJsonPath(jsonPath1); 22 | var parentJsonPath2 = JsonUtils.GetParentJsonPath(jsonPath2); 23 | var parentJsonPath3 = JsonUtils.GetParentJsonPath(jsonPath3); 24 | var parentJsonPath4 = JsonUtils.GetParentJsonPath(jsonPath4); 25 | 26 | // THEN 27 | Assert.AreEqual("$['obj1']['obj2']", parentJsonPath1); 28 | Assert.AreEqual("obj1", parentJsonPath2); 29 | Assert.AreEqual("$.obj1", parentJsonPath3); 30 | Assert.AreEqual("$", parentJsonPath4); 31 | } 32 | 33 | [TestMethod] 34 | public void TestGetJsonElementKey_Nominal() 35 | { 36 | // GIVEN 37 | const string jsonPath1 = "$['obj0']['obj1']['obj2']"; 38 | const string jsonPath2 = "obj1.obj2"; 39 | const string jsonPath3 = "$.obj1.obj2"; 40 | const string jsonPath4 = "obj2"; 41 | 42 | // WHEN 43 | var jsonElementKey1 = JsonUtils.GetJsonElementKey(jsonPath1); 44 | var jsonElementKey2 = JsonUtils.GetJsonElementKey(jsonPath2); 45 | var jsonElementKey3 = JsonUtils.GetJsonElementKey(jsonPath3); 46 | var jsonElementKey4 = JsonUtils.GetJsonElementKey(jsonPath4); 47 | 48 | // THEN 49 | Assert.AreEqual("obj2", jsonElementKey1); 50 | Assert.AreEqual("obj2", jsonElementKey2); 51 | Assert.AreEqual("obj2", jsonElementKey3); 52 | Assert.AreEqual("obj2", jsonElementKey4); 53 | } 54 | 55 | [TestMethod] 56 | [ExpectedException(typeof(ArgumentException))] // THEN 57 | public void TestGetParentJsonPath_ShouldThrowArgumentException_WhenJsonPathNullOrEmpty() 58 | { 59 | try 60 | { 61 | // GIVEN 62 | const string jsonPath = ""; 63 | 64 | // WHEN 65 | JsonUtils.GetParentJsonPath(jsonPath); 66 | } 67 | catch (Exception e) 68 | { 69 | // THEN 70 | Assert.AreEqual("jsonPath", e.Message); 71 | throw; 72 | } 73 | 74 | } 75 | 76 | [TestMethod] 77 | [ExpectedException(typeof(ArgumentException))] // THEN 78 | public void TestGetJsonElementKey_ShouldThrowArgumentException_WhenJsonPathNullOrEmpty() 79 | { 80 | try 81 | { 82 | // GIVEN 83 | const string jsonPath = ""; 84 | 85 | // WHEN 86 | JsonUtils.GetJsonElementKey(jsonPath); 87 | } 88 | catch (Exception e) 89 | { 90 | // THEN 91 | Assert.AreEqual("jsonPath", e.Message); 92 | throw; 93 | } 94 | } 95 | 96 | [TestMethod] 97 | [ExpectedException(typeof(InvalidOperationException))] // THEN 98 | public void TestGetParentJsonPath_ShouldThrowInvalidOperationException_WhenNoParent() 99 | { 100 | try 101 | { 102 | // GIVEN 103 | const string jsonPath = "$"; 104 | 105 | // WHEN 106 | JsonUtils.GetParentJsonPath(jsonPath); 107 | } 108 | catch (Exception e) 109 | { 110 | // THEN 111 | Assert.AreEqual("Unable to find parent for '$'", e.Message); 112 | throw; 113 | } 114 | } 115 | 116 | [TestMethod] 117 | [ExpectedException(typeof(InvalidOperationException))] // THEN 118 | public void TestGetJsonElementKey_ShouldThrowInvalidOperationException_WhenNoKey() 119 | { 120 | try 121 | { 122 | // GIVEN 123 | const string jsonPath = "$"; 124 | 125 | // WHEN 126 | JsonUtils.GetJsonElementKey(jsonPath); 127 | } 128 | catch (Exception e) 129 | { 130 | // THEN 131 | Assert.AreEqual("Unable to find object key for '$'", e.Message); 132 | throw; 133 | } 134 | } 135 | 136 | [TestMethod] 137 | public void TestParsePayload() 138 | { 139 | // GIVEN 140 | const string payload = "{\"num\":123, timestamp:\"2024-02-12T00:00:00-05:00\"}"; 141 | JToken token = JsonUtils.ParsePayload(payload); 142 | 143 | // WHEN 144 | string num = token.SelectToken("num").ToString(); 145 | string timestamp = token.SelectToken("timestamp").ToString(); 146 | // THEN 147 | Assert.AreEqual(num, "123"); 148 | Assert.AreEqual(timestamp, "2024-02-12T00:00:00-05:00"); 149 | 150 | // WHEN 151 | var defaultToken = JToken.Parse(payload); 152 | num = defaultToken.SelectToken("num").ToString(); 153 | timestamp = defaultToken.SelectToken("timestamp").ToString(); 154 | 155 | // THEN 156 | Assert.AreEqual(num, "123"); 157 | Assert.AreNotEqual(timestamp, "2024-02-12T00:00:00-05:00"); 158 | 159 | 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/Utils/RsaKeyUtilsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Mastercard.Developer.ClientEncryption.Core.Utils; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Mastercard.Developer.ClientEncryption.Tests.NetCore.Utils 7 | { 8 | [TestClass] 9 | public class RsaKeyUtilsTest 10 | { 11 | 12 | [TestMethod] 13 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-512.pem", 512)] 14 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-1024.pem", 1024)] 15 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-2048.pem", 2048)] 16 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-4096.pem", 4096)] 17 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-512.der", 512)] 18 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-1024.der", 1024)] 19 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-2048.der", 2048)] 20 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-4096.der", 4096)] 21 | [DataRow("./_Resources/Keys/Pkcs1/test_key_pkcs1-512.pem", 512)] 22 | [DataRow("./_Resources/Keys/Pkcs1/test_key_pkcs1-1024.pem", 1024)] 23 | [DataRow("./_Resources/Keys/Pkcs1/test_key_pkcs1-2048.pem", 2048)] 24 | [DataRow("./_Resources/Keys/Pkcs1/test_key_pkcs1-4096.pem", 4096)] 25 | public void TestReadPrivateKeyFile(string keyPath, int expectedKeySize) 26 | { 27 | // WHEN 28 | var rsa = RsaKeyUtils.ReadPrivateKeyFile(keyPath); 29 | 30 | // THEN 31 | Assert.AreNotEqual("RSACryptoServiceProvider", rsa.GetType().Name); // We expect a RSACng (Windows) or a RSAOpenSsl (Linux, macOS) 32 | Assert.AreEqual(expectedKeySize, rsa.KeySize); 33 | } 34 | 35 | [TestMethod] 36 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-512.pem", 512)] 37 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-1024.pem", 1024)] 38 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-2048.pem", 2048)] 39 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-4096.pem", 4096)] 40 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-512.der", 512)] 41 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-1024.der", 1024)] 42 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-2048.der", 2048)] 43 | [DataRow("./_Resources/Keys/Pkcs8/test_key_pkcs8-4096.der", 4096)] 44 | [DataRow("./_Resources/Keys/Pkcs1/test_key_pkcs1-512.pem", 512)] 45 | [DataRow("./_Resources/Keys/Pkcs1/test_key_pkcs1-1024.pem", 1024)] 46 | [DataRow("./_Resources/Keys/Pkcs1/test_key_pkcs1-2048.pem", 2048)] 47 | //[DataRow("./_Resources/Keys/Pkcs1/test_key_pkcs1-2048_uneven_length.pem", 2048)] -- uncomment for local testing. 48 | [DataRow("./_Resources/Keys/Pkcs1/test_key_pkcs1-4096.pem", 4096)] 49 | public void TestReadPrivateKey(string keyPath, int expectedKeySize) 50 | { 51 | // GIVEN 52 | var bytes = File.ReadAllBytes(keyPath); 53 | // WHEN 54 | var rsa = RsaKeyUtils.ReadPrivateKey(bytes); 55 | 56 | // THEN 57 | Assert.AreNotEqual("RSACryptoServiceProvider", rsa.GetType().Name); // We expect a RSACng (Windows) or a RSAOpenSsl (Linux, macOS) 58 | Assert.AreEqual(expectedKeySize, rsa.KeySize); 59 | } 60 | 61 | [TestMethod] 62 | public void TestGetEncoded_ShouldSupportPublicKey512bits() 63 | { 64 | // GIVEN 65 | const string certificatePath = "./_Resources/Certificates/test_certificate-512.pem"; 66 | var certificate = EncryptionUtils.LoadEncryptionCertificate(certificatePath); 67 | 68 | // WHEN 69 | var encodedBytes = RsaKeyUtils.GetEncoded(certificate.PublicKey); 70 | 71 | // THEN 72 | const string javaGetEncodedKeyValue = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANgkcDW0LBw2whiJld9zMq2fs56NdZcPxaM4kbN5NJKcWMv/120mOUrRlqsxdN0slsqvnyxG+D+weHdEQVFcUn8CAwEAAQ=="; 73 | Assert.AreEqual(javaGetEncodedKeyValue, Convert.ToBase64String(encodedBytes)); 74 | } 75 | 76 | [TestMethod] 77 | public void TestGetEncoded_ShouldSupportPublicKey1024bits() 78 | { 79 | // GIVEN 80 | const string certificatePath = "./_Resources/Certificates/test_certificate-1024.pem"; 81 | var certificate = EncryptionUtils.LoadEncryptionCertificate(certificatePath); 82 | 83 | // WHEN 84 | var encodedBytes = RsaKeyUtils.GetEncoded(certificate.PublicKey); 85 | 86 | // THEN 87 | const string javaGetEncodedKeyValue = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDih8akxCCa30Pv5epkBWt4KzpGaXqeyB/ydbj3Hq7ylvHnJwrb9cJ4MbsKzox2JUbtYg/AVXCaQTDlsXfql6+O51ptKLWWilyzAZb5/okpOx2DlzHe4c/crrDfMnF0UA2sFbmzqSUVpNY7NjE7tquRhYueJTT2RpAGGANMReMhjwIDAQAB"; 88 | Assert.AreEqual(javaGetEncodedKeyValue, Convert.ToBase64String(encodedBytes)); 89 | } 90 | 91 | [TestMethod] 92 | public void TestGetEncoded_ShouldSupportPublicKey2048bits() 93 | { 94 | // GIVEN 95 | const string certificatePath = "./_Resources/Certificates/test_certificate-2048.pem"; 96 | var certificate = EncryptionUtils.LoadEncryptionCertificate(certificatePath); 97 | 98 | // WHEN 99 | var encodedBytes = RsaKeyUtils.GetEncoded(certificate.PublicKey); 100 | 101 | // THEN 102 | const string javaGetEncodedKeyValue = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9Mp6gEFp9E+/1SS5XrUyYKMbE7eU0dyJCfmJPz8YOkOYV7ohqwXQvjlaP/YazZ6bbmYfa2WCraOpW0o2BYijHgQ7z2a2Az87rKdAtCpZSKFW82Ijnsw++lx7EABI3tFF282ZV7LT13n9m4th5Kldukk9euy+TuJqCvPu4xzE/NE+l4LFMr8rfD47EPQkrun5w/TXwkmJrdnG9ejl3BLQO06Ns6Bs516geiYZ7RYxtI8Xnu0ZC0fpqDqjCPZBTORkiFeLocEPRbTgo1H+0xQFNdsMH1/0F1BI+hvdxlbc3+kHZFZFoeBMkR3jC8jDXOXNCMNWb13Tin6HqPReO0KW8wIDAQAB"; 103 | Assert.AreEqual(javaGetEncodedKeyValue, Convert.ToBase64String(encodedBytes)); 104 | } 105 | 106 | [TestMethod] 107 | public void TestGetEncoded_ShouldSupportPublicKey4096bits() 108 | { 109 | // GIVEN 110 | const string certificatePath = "./_Resources/Certificates/test_certificate-4096.pem"; 111 | var certificate = EncryptionUtils.LoadEncryptionCertificate(certificatePath); 112 | 113 | // WHEN 114 | var encodedBytes = RsaKeyUtils.GetEncoded(certificate.PublicKey); 115 | 116 | // THEN 117 | const string javaGetEncodedValue = "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAygN/8OUwDKbTIt5zguBP4sBF1GCWpKsV6Nt2llfMNqugd/9xM2BI4WyINNSzQDA69Qp+ce2mAkqxO72l7BhBdGitHDRrU4QAun/ls+ECrWZ5ug9iFZD2dsrLxj08gLRlybwFCUjuLLK5QUjB4HfbgAxkgYfVBgluIgA6G7Lcr9rQr57F53IZ5hM9ppcbNeb9HlW5wHV+tHW/1l9ZcOS5TJPP5ptV3aHneMpVKB8TJIMjSiWZt+6bs1VoCB2jaxaVNC2jkgzInezsqGur+G7Zq5oofcRDKyDt8imXf71i3BcM7d7fOur/r5gB0k6Kc+UGSzQK2oVP54T/0X52+2eKV8NqGF3Lpu5Poy5Nq0rID3L1Db81P7qUrrISuMkLP+uPPbFk4A6ZuczNAbxXARjSwOFhshUHEVKIW7FWeWWCCY5Ti21eWG9Recl5ahA7aHLogTFqyhAqV5h4l7D/icoxr2TAOkllFGLCRZ3ad43ET7gXPttiqu3SPstUFbuqDbmt3Yp4ZuoDA3+KEeSXYPaIn+J9Drm4B6gMTrTh/i0eRdKc9VFYmIgVJGRH4X6KCAO3erhzAUuFRKELJl1g6i0xZnPLICSktG+dBWkiFSqkLIsxPc9K4o9ZEOmhALih8gLEitXIcze7TT4ohhJsuA9T4GVMBRaQiAHo9fK2PF7h+WcCAwEAAQ=="; 118 | Assert.AreEqual(javaGetEncodedValue, Convert.ToBase64String(encodedBytes)); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Certificates/test_certificate-1024.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICZjCCAc+gAwIBAgIUBq7cHZY6mebdaqpq+GoZfipDAhYwDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xOTAzMjgxOTUzMDRaFw0yOTAz 5 | MjUxOTUzMDRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwgZ8wDQYJKoZIhvcNAQEB 7 | BQADgY0AMIGJAoGBAOKHxqTEIJrfQ+/l6mQFa3grOkZpep7IH/J1uPcervKW8ecn 8 | Ctv1wngxuwrOjHYlRu1iD8BVcJpBMOWxd+qXr47nWm0otZaKXLMBlvn+iSk7HYOX 9 | Md7hz9yusN8ycXRQDawVubOpJRWk1js2MTu2q5GFi54lNPZGkAYYA0xF4yGPAgMB 10 | AAGjUzBRMB0GA1UdDgQWBBS164KE51INf9Utho+9oZokMMqdEDAfBgNVHSMEGDAW 11 | gBS164KE51INf9Utho+9oZokMMqdEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 12 | DQEBCwUAA4GBANNwxvtZVZjRStZ3SsoeHxAUZyqL+ZAWzgWOXv2pwIeJUR/rKlKJ 13 | 71894OktqM8YH/eA3eSXPcs8tF5r8mUL+F0yF+ipmkb379oQnUO6dka1KL68+SZE 14 | 0p9NWB1A8fqbMdJC0s5Ba1g1k1WFoGX/jKULasrrgGWORnxYoykwF2hq 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Certificates/test_certificate-2048.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mastercard/client-encryption-csharp/7c9250c5a0c5f0d0d8520eb2c69e29ae09787a32/Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Certificates/test_certificate-2048.der -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Certificates/test_certificate-2048.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDITCCAgmgAwIBAgIJANLIazc8xI4iMA0GCSqGSIb3DQEBBQUAMCcxJTAjBgNV 3 | BAMMHHd3dy5qZWFuLWFsZXhpcy1hdWZhdXZyZS5jb20wHhcNMTkwMjIxMDg1MTM1 4 | WhcNMjkwMjE4MDg1MTM1WjAnMSUwIwYDVQQDDBx3d3cuamVhbi1hbGV4aXMtYXVm 5 | YXV2cmUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9Mp6gEFp 6 | 9E+/1SS5XrUyYKMbE7eU0dyJCfmJPz8YOkOYV7ohqwXQvjlaP/YazZ6bbmYfa2WC 7 | raOpW0o2BYijHgQ7z2a2Az87rKdAtCpZSKFW82Ijnsw++lx7EABI3tFF282ZV7LT 8 | 13n9m4th5Kldukk9euy+TuJqCvPu4xzE/NE+l4LFMr8rfD47EPQkrun5w/TXwkmJ 9 | rdnG9ejl3BLQO06Ns6Bs516geiYZ7RYxtI8Xnu0ZC0fpqDqjCPZBTORkiFeLocEP 10 | RbTgo1H+0xQFNdsMH1/0F1BI+hvdxlbc3+kHZFZFoeBMkR3jC8jDXOXNCMNWb13T 11 | in6HqPReO0KW8wIDAQABo1AwTjAdBgNVHQ4EFgQUDtqNZacrC6wR53kCpw/BfG2C 12 | t3AwHwYDVR0jBBgwFoAUDtqNZacrC6wR53kCpw/BfG2Ct3AwDAYDVR0TBAUwAwEB 13 | /zANBgkqhkiG9w0BAQUFAAOCAQEAJ09tz2BDzSgNOArYtF4lgRtjViKpV7gHVqtc 14 | 3xQT9ujbaxEgaZFPbf7/zYfWZfJggX9T54NTGqo5AXM0l/fz9AZ0bOm03rnF2I/F 15 | /ewhSlHYzvKiPM+YaswaRo1M1UPPgKpLlRDMO0u5LYiU5ICgCNm13TWgjBlzLpP6 16 | U4z2iBNq/RWBgYxypi/8NMYZ1RcCrAVSt3QnW6Gp+vW/HrE7KIlAp1gFdme3Xcx1 17 | vDRpA+MeeEyrnc4UNIqT/4bHGkKlIMKdcjZgrFfEJVFav3eJ4CZ7ZSV6Bx+9yRCL 18 | DPGlRJLISxgwsOTuUmLOxjotRxO8TdR5e1V+skEtfEctMuSVYA== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Certificates/test_certificate-4096.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Certificates/test_certificate-512.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB4TCCAYugAwIBAgIUcINgyQ60WIgYlx6fqrRJlOplzb8wDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xOTAzMjgxOTUyMjRaFw0yOTAz 5 | MjUxOTUyMjRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwXDANBgkqhkiG9w0BAQEF 7 | AANLADBIAkEA2CRwNbQsHDbCGImV33MyrZ+zno11lw/FoziRs3k0kpxYy//XbSY5 8 | StGWqzF03SyWyq+fLEb4P7B4d0RBUVxSfwIDAQABo1MwUTAdBgNVHQ4EFgQUuz7A 9 | dJlXdmbv8xITrsbtg/xU4UQwHwYDVR0jBBgwFoAUuz7AdJlXdmbv8xITrsbtg/xU 10 | 4UQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAANBABvhfdhBC/8Suhhu 11 | 2kB4kj0FU/8J6oG0MDxT8mT/yFKw1TdQcFGRAwu+Rfc74GsSPQ3JdgjLh8FqSzud 12 | p8kMX50= 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs1/test_key_pkcs1-1024.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDHul8A7MM3ynx1XRxmLCRZ1Lr66QppWP5ZaEotsQPpFtyq3w/x 3 | dLWZd5XXQcIb/wUQWcbxBWYJPMk/GVQ3pvkIKEqC+h08VdXtfbBlM+RE8OYaQW5O 4 | Fg7YjHgL/WvCka6VJ990RuX9Zdj9TUu8GyqERll/XUDACs85atbSW6PBQQIDAQAB 5 | AoGBAL9acs0LCZn5OLalB6FoJ0edhasA/MWjysRUI8WU898s1SwsXDUEkTxAk2HR 6 | kayK7woUSYL/nhu5jkIS/Vn4clvH7XRkch22cjAAs4oGZXUUlVrcXFDiR0o2OqAI 7 | Xh24ESM/tpDWmn/N30afn31TBAKxgY5Ej2SrmK5gjSeMGI9xAkEA+gPM/P+jLiwy 8 | wuiS4QRYxeDFtluaafl7UHR/I6vL9VaqOptwV1e/JlytKNMCwoMqadd739/BAX9d 9 | qNgpniHC9QJBAMyCZBAc8PIjfM1h4seEDeb2mxl1Y2DmcDUVWIt+E4Zs3m1I59we 10 | VgY/BwX4UIElhgsVjNo6qXYPbWiql4/tzZ0CQQCLJ6RnyO2NXIJgY8yku6Ohd6r0 11 | BeZbR8XwEPdW5l8eTb9v4WZU5vz4oCqtB02I8DKiOJK1F7g4WijKOo5neoklAkBt 12 | G9/w7M/sD9zk4qWQVrboE4faRFPZ/fe9in7sJT6biHf/DFePi6vPt06y87FXxcJH 13 | JZ85SvTgZQi1P9aO1ovNAkAVxgJq94CKCZjOiks5xTro6V0pYdsx0tuuIlx/vGVG 14 | H3bcmpSm/S54nSovzbEDN7gKr4CSSsAQqC6oPwJSQN6m 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs1/test_key_pkcs1-2048.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEAzCT8ABttdAfW851VVNMSkpgcWZ1yFTgxNu+coPpL8Ug1OZgR 3 | YOgg+fcj/A8MTYs/yXgIJhI1goXVF/9NZPfryrBPxeFIOJe+CGePGMgXv3F7o9uV 4 | 010zcyImgKntv3NrLCVw9trzIIaKNgvwgmPDhegHsWpVMr7Lk2DL5QmPE28J8ekJ 5 | nYfHJzGUSlJYJpCoCVtO7bPPDCcWgrlOzoagTujtpyBRWAPRJoA1J2b7uvzrqmuU 6 | Gf8Yqg8MIeN7Uuxs4jSk3rsDXMCE5cCVKa+bfoTV1XRQJ2PABdNuvwH/wqosRg3Y 7 | TgFdWM46AY4DgIzzqY/xSeLibd1Hzg2wHQTM6wIDAQABAoIBAQDLmjtnk/NXHRaK 8 | RCm9/wHwCRuFWV1VwoR7KQGLH/er/ntvJLZ4cyuogo92Lj/z+uS0eC2QYurRcc81 9 | LuCuygF2VuBJGEXig5z5Xue+LJpaysEojLHia3sL4kyKWHCRWHjUP8dpvLdtgiHI 10 | g6HtObjhDajWjpnIkbgSFiFlHmJ/WqA7IjEOehGiqTjrfyXpL8rbcGt+chJb2z0s 11 | RdlABjl1MT2s9cCHZLwz6x1eDQDDYyw2pRRmEddMZ5VWtAd37I8RWl2NHrMsggca 12 | JzIA5LnddsRqmMVw7+1qFIIK0ZHOTknvvgQ8+U7P+r8v7+3mufvX4JakPingj543 13 | slbOGapBAoGBAO1F4REeKfCpGMo7kWZsAASAkEb+5Fcu4jrEzZkf2jk5WC4zkWlm 14 | SAqay1WLIEGP25LCo0o8vTEfx0tONukJMmEJewVi551Nxz+clcrbiJRcX6P8RTCe 15 | cJtQjUOqwHDvKNNpcAE8zz0YOLotJxhCH5ST3aE07sc159K9EGMzedrhAoGBANxB 16 | vbghMxN7lTuSvxMOwNQOWgKfWzV+fTXLQ7SgFhICqEV67nYHm0r/j9lN2Vtxuh6L 17 | hqZ2r1khzEOhyHz7YBINAYUqjjoFAVUfsHZ7auM7sdQBZx1VwS2XRPglgpso9wEh 18 | TEz75C7LH4/2nu1BIVAduE2cc95wKPEUexps/E1LAoGBAIppxFSvCvpIOpzmyPg9 19 | snjt4rx3vw6Y3AI6glF8Qlo1eJpjHMWmlAoTqOA7K9LzL7zabFVHP3qjtifY9bFV 20 | 2xy+YhSPUNvz3nLeToerL26UwHoyFM667qe8AtxhhKec7Gz/ygX+ykoykg0RgAfn 21 | svKCm7yJ2208pgLKpf+orMIhAoGBALrpMxWRXuW2pzKR2oJSr8KEl0/Iab9gouLG 22 | pqMegvwvsxqbMseItvkTHMB8tupJ/Xa0UsTqzOznqI7wONIPBDztOpAGSAHmg3X4 23 | WWiCXXeODd9qfVXAkxmcWBP4yPfg8JPN7REbZU1sZFFoKQAPmDSDtAZwsUdfiO7k 24 | wX7wY783AoGAQ646bcqPKXmNCc2oo4O4VgcC2afzNuxoUfLgFehVREj/tbhtOpN4 25 | NwhAsQhNz4uh1UmlulKTTGZ67VWikAiQ8ip5HSBMRVT/A4ZCUh5ondU1yVxH6Q0+ 26 | eyQ+FF+jTgnAdMp0smLw7yem6HcekksgdNwhDKifFTI13mKWUh8gBew= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs1/test_key_pkcs1-2048_uneven_length.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAvyWF3MO6BkKPopRSKWHTTutHMd19BrTEhHoR1UDu6T5eJj7M 3 | +/iAFr7+Or7WxykNi7tqZ5HIcLYNbgvxhpaTNATg+gYA975LhixGafjFIR4Npxux 4 | ub4FRiHMQLB9I+xtjPkT5YCK4W0FGq6WAlQdqmbkGA+tPgkwEPTUUzYddsfXExIh 5 | FYAtOUHGnvbpcUUzsAo8VEd2n5b0+BPybC/2CZkNe6DEztVWQmw+/S/7mzE1XMEZ 6 | ECo9blPkuPeZseWtvZQiV1SqPLww0tPzJDT31rZKXcXfF84SMs5VMqBBMgxi3Lqs 7 | SXoGLjVZIq1sZzulw/MfzMf4CnkvptOuHpQUOQIDAQABAoIBAQCNUyno8tGsZS7u 8 | vakNraV1OSGXbcJN3gNaS4K54FuEGje0IUSfm7jgmHARcR4NfH3mbTgbwT6ojwHn 9 | hNTrhJLrqBvK3/uBMLJYTo31NhhSBw1jG2fJBASUONLylUrA78KDs3kRe3uzdk6K 10 | zrBZmM2iOpa0b4rQTiy6rLn7mAwq0Ad59gHMyY63m9wCgKrD12ZFP5x8K56RHxQr 11 | NlqzafqVvboNSQ9XSLat/GgT1aEGcFqXyd1R/3T+wScAX5QkUb4YI2Y5xVlbwxvb 12 | er4s0hMpUAhDte6oRK6AIwCmg+G9WBJEiwZ8u4/5o3QZiBxwgENCXyvEjzBYV1Vi 13 | q9vwcnqtAoGBAPXArBqnJUXtCP1fsEVNJbgCT4HiZxqeZVh345MJLTkm38eBqAKf 14 | 7Lr5RzTDnNlzY2qFqwinDYtD1kC8AcvLPt/MEzApPKC9/8oVi62v0sSdrglzICLg 15 | 7WREwQ9W4dffqiXF12Bev1RDDMAUTXiZnslQlInp4BPuJrpcUplMGjtXAoGBAMcd 16 | 8vpwbfUg3/4VQ+XTOZZKz8tF7KMcpqoWoHZpkv3kawIiVygILp162AKBPCMz/Nmg 17 | k5KNvfef9k3gcSu6D8EMLnec7EUa/FSoMDqjBzI3h+DIrNAVaA2siBVNc9I0KMT7 18 | YxJk5iFR9+oBl/BrLT+Xw3aS7Z0C17lwjuYrYALvAoGAAP5jc8QjQXIg7C1b4Bvc 19 | wC66NZM9yXzEeiQZ9nk+BpIiq4LZtBEDewoRAKSd2L8/sqmRtHpAcsS2K4jp1PIg 20 | cfU4SqSQCzJZe2603uHsH0Fn97/QbHOuYg5pHW/DgAT2giqXoVsguO2AerJaUdeF 21 | Ldk2kIwyjMIUxAIlj1sgbAcCgYBDEbB1cQdoqSEp2YncURC1sg9aHfyAyeNE5ug3 22 | 2onboWP1/RE0d8UT4HwAI/3Ysbs6pt7uX3YcgbIaOz6DhxGlafAA9EeVY2iZL2nH 23 | ZoCXBqNrMM0c7gj1iibwXf1kUZNl/nM5j+kAUjbaiJzgK4m7QOhi+Uy2NW3qlaJc 24 | n0Gg2wKBgQCqWjP97Dwv0nyr3vnM+49B7BL7PYldjIy2RCwHYCN/JwfaihLt+EZ1 25 | ZhCDk9S0aANGjKrZB8bUdGq9DY6cPO4QJjmtfVmdHWT8XIP5mrYbUob4fTvcP1UH 26 | nN/hiGpa1AvhNoU4MQ2OGiK87mBPtfD5nwQh6aNnvglmFUjpL6FaWA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs1/test_key_pkcs1-4096.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs1/test_key_pkcs1-512.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIBOQIBAAJBAOiY+DyTnjyIs41Z9SfqIEV+0BUuDqC4iceBIY98yTTGtlIkb27M 3 | DJjBAW2fhrxyG8Cb74SspmcD4u/mxrm6460CAwEAAQJAbbwViUa/paF8zFg/gAhG 4 | F2Nfuk5TWmIVpoj2k2J07q9W9JeY5sJfLvFwwsMc/vY6FPHGSO0P4sQ3MQHo1zyA 5 | RQIhAPir1/laHualWd5ssAJrvzoK7EtVSnjbuU1tVKBTLwnvAiEA73Paq1U0wO12 6 | DyfRcE+UQ1NwvROLVn0XQA/MAGMj+CMCIDjwMA2aQwUQy1kQjeSgAzMpGR3Os7Sk 7 | qvM9m2jyYwzlAiBrBFJUhI5BM1+yQk9+bHKM7HvUZSm/C8UaYnUAL07iFQIgCLIh 8 | fJ5xPiUD6tO1jn0Kauf1DOlwhDQC10zpOitM8xo= 9 | -----END RSA PRIVATE KEY----- 10 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs12/test_key.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mastercard/client-encryption-csharp/7c9250c5a0c5f0d0d8520eb2c69e29ae09787a32/Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs12/test_key.p12 -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_invalid_key.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mastercard/client-encryption-csharp/7c9250c5a0c5f0d0d8520eb2c69e29ae09787a32/Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_invalid_key.der -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-1024.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mastercard/client-encryption-csharp/7c9250c5a0c5f0d0d8520eb2c69e29ae09787a32/Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-1024.der -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-1024.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALCcqlioDdtVqSIW 3 | /Y0PUM0YX5Us76B6VSS5e9LwFeK9g/jsrfGs72WdRxEX1uHTzZXHGimMmmODqv4M 4 | bZ5Qbbs7dCZwSKlqNlSKLx40zwy4xZh1ie/UJ2CgUhu62wXDnUMg4HE0l9oY5g/P 5 | BXvSbuGM87gMnNOk0E+pfWHjQbvlAgMBAAECgYBggfuD3rFTvYdinXWH82qP6FWy 6 | yo9W/gIwwzqqlY8gC7dl+s9CVOGsgTkoWgKN/JNG2Tmuoqpq3rQ9hsUP0Ztj32ty 7 | VfEWqKipDtC0x2xi2D7I25gxVLu521tqLhoaWN1oWdPraflXEkEY9p8CD8tzxc6Q 8 | wi6ShZ8TSacuLvivgQJBAOd/X4iNmmPZ0AY+ZVri4Ic+YXOOapagkXuR2Y5pcRfw 9 | 3jueEwZMIo142L+dFZMDORsqPhFwwCzOcbn2NEID4ckCQQDDTh607HbkvXAwYHDZ 10 | 6gpczrhSAkpjQ0fffOFuF8sI5A64KjnTGte+fm8ic5Mess1oP6LdaS0HvJs4n+m7 11 | nPc9AkEApTwbOmKoQoEjpHFA8wBhducls8+BcQYnEWZnPOkyGf6JAVCxD5ukRgpt 12 | 20cKMSbpyeP67YPnB5RLRIrhfgU7UQJAX3d5NRD9UPR0uYD6yNpJNHJr0NKD0B+c 13 | K1dczjbdLTxlIYqqd1GAsgIViu6ZtIDMPTAWCUqXE1gTO8uXMfkZNQJASgdEiTiI 14 | EnZLto+1hRr/fYNzIJHDelJ2oGtzbRmUtXQ8d489obsZusK2kSPWgX1lzXjvrv07 15 | jznkRk0QtDG8Vw== 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-2048.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mastercard/client-encryption-csharp/7c9250c5a0c5f0d0d8520eb2c69e29ae09787a32/Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-2048.der -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-2048.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD0ynqAQWn0T7/V 3 | JLletTJgoxsTt5TR3IkJ+Yk/Pxg6Q5hXuiGrBdC+OVo/9hrNnptuZh9rZYKto6lb 4 | SjYFiKMeBDvPZrYDPzusp0C0KllIoVbzYiOezD76XHsQAEje0UXbzZlXstPXef2b 5 | i2HkqV26ST167L5O4moK8+7jHMT80T6XgsUyvyt8PjsQ9CSu6fnD9NfCSYmt2cb1 6 | 6OXcEtA7To2zoGznXqB6JhntFjG0jxee7RkLR+moOqMI9kFM5GSIV4uhwQ9FtOCj 7 | Uf7TFAU12wwfX/QXUEj6G93GVtzf6QdkVkWh4EyRHeMLyMNc5c0Iw1ZvXdOKfoeo 8 | 9F47QpbzAgMBAAECggEAK3dMmzuCSdxjTsCPnc6E3H35z914Mm97ceb6RN26OpZI 9 | FcO6OLj2oOBkMxlLFxnDta2yhIpo0tZNuyUJRKBHfov35tLxHNB8kyK7rYIbincD 10 | joHtm0PfJuuG+odiaRY11lrCkLzzOr6xlo4AWu7r8qkQnqQtAqrXc4xu7artG4rf 11 | MIunGnjjWQGzovtey1JgZctO97MU4Wvw18vgYBI6JM4eHJkZxgEhVQblBTKZs4Of 12 | iWk6MRHchgvqnWugwl213FgCzwy9cnyxTP13i9QKaFzL29TYmmN6bRWBH95z41M8 13 | IAa0CGahrSJjudZCFwsFh413YWv/pdqdkKHg1sqseQKBgQD641RYQkMn4G9vOiwB 14 | /is5M0OAhhUdWH1QtB8vvhY5ISTjFMqgIIVQvGmqDDk8QqFMOfFFqLtnArGn8HrK 15 | mBXMpRigS4ae/QgHEz34/RFjNDQ9zxIf/yoCRH5PmnPPU6x8j3bj/vJMRQA6/yng 16 | oca+9qvi3R32AtC5DUELnwyzNwKBgQD5x1iEV+albyCNNyLoT/f6LSH1NVcO+0IO 17 | vIaAVMtfy+hEEXz7izv3/AgcogVZzRARSK0qsQ+4WQN6Q2WG5cQYSyB92PR+Vgwh 18 | nagVvA+QHNDL988xoMhB5r2D2IVSRuTB2EOg7LiWHUHIExaxVkbADODDj7YV2aQC 19 | JVv0gbDQJQKBgQCaABix5Fqci6NbPvXsczvM7K6uoZ8sWDjz5NyPzbqObs3ZpdWK 20 | 3Ot4V270tnQbjTq9M4PqIlyGKp0qXO7ClQAskdq/6hxEU0UuMp2DzLNzlYPLvON/ 21 | SH1czvZJnqEfzli+TMHJyaCpOGGf1Si7fhIk/f0cUGYnsCq2rHAU1hhRmQKBgE/B 22 | JTRs1MqyJxSwLEc9cZLCYntnYrr342nNLK1BZgbalvlVFDFFjgpqwTRTT54S6jR6 23 | nkBpdPmKAqBBcOOX7ftL0b4dTkQguZLqQkdeWyHK8aiPIetYyVixkoXM1xUkadqz 24 | cTSrIW1dPiniXnaVc9XSxtnqw1tKuSGuSCRUXN65AoGBAN/AmT1S4PAQpSWufC8N 25 | UJey8S0bURUNNjd52MQ7pWzGq2QC00+dBLkTPj3KOGYpXw9ScZPbxOthBFzHOxER 26 | Wo16AFw3OeRtn4VB1QJ9XvoA/oz4lEhJKbwUfuFGGvSpYvg3vZcOHF2zlvcUu7C0 27 | ub/WhOjV9jZvU5B2Ev8x1neb 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-4096.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mastercard/client-encryption-csharp/7c9250c5a0c5f0d0d8520eb2c69e29ae09787a32/Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-4096.der -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-4096.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCydHTLH/qEJ2gr 3 | 4EgO/3jE43lJlTbNag1CKSCOVXjh4V0keItReYgk+B7/IOn/UOTj75MRVsXpxyXi 4 | UJW+kwaIX26TLuW3fZVc466ipH1ftrMG0bbB686Tk+TmzAycBQGMLCx7sCzemO+t 5 | VFI+BKgjfOj/bl91HRTEX0ESC6BRUK13gfyQJelOu1OZkV5efHMi4AmDW8jUwOKH 6 | KZ3ZlScm9D0Ghe6CT8i/MY4CgYRvgnEqQUUmd/QnpJJkR7e5m0uWlCDGIf3ZLjbI 7 | gZsVInrSeQ8I/gloZYbZuUhQUdObR56DghuTKr5VWc+huqtVcwcBfEfCP7FIFK9R 8 | 7mqkQmGrW4pbTsBigs9vAzlDmjWQFhXJ0zV2hlABVixqwvOxMEt19gJADfYmFsgv 9 | OWytmjmZCuQgbmjOCdGSqp9UeWGl+U4jbRJTuRsOb5CS1XtUx9drsZJ+YLvFSsvB 10 | 1wXYm7Pt9Rx2v6UfJtLVMtW8q286BPeQ2FFmiPewgPnk1RyeQb1Luw1NuRdvBLHq 11 | 81Ll5HNf6Fd6/ZC9kgwEhcE9LVjTX8HXQmSc8XIjV9uI/wjPyyb5t0hGlrqkXya9 12 | /U86q9v6PXFJ47K0ZW1/+FSzbKCFDNiJI1RWKn9M9CSRPEOKZdNH/lQjSKzCbN9Q 13 | Db8i2T8MLA+6lnt4fwax6KsPMPl4gwIDAQABAoICAA2S8CNXPl35BWJ4/+IsKoqH 14 | Zv1i3TVIwNNsf250L+q2krgodyhuXx24xdrQLRxfkdmIqp4iBJHgtQ/+4zUIx/Ft 15 | mOKXKaAIbGkRZII4ktTJ99on74fWoPL2x+2KTdc8Rj7cSVHrN66C4ZBEnrDa99Mj 16 | ODHdumMVIDRDrpZpzfsBcQBrIcPxLkrv0s8WkKANRVC8y9xzCdatCU4Qq1IWl1DO 17 | OSoa2+aLnRB1+4BTS7iTqnn1VwYzD55IVV5NWjtDBb/hapDHmyB+9GnR+fLkmYUS 18 | 8kLT1/FZ76T2A/sgDkF8dCE6r1BFaw7g8vsxUMECK+FAC9FJuPlroV5RDUmLRxR6 19 | Lgo/glwt3BQi5LvYMA87CEn+hRG1Rh0SgsN2d2EFebPGZF5LJQfgpN/BHB41Sgrs 20 | FCyPoVRR11q72JBVOCUrhS8inuHmR1XFzmhkfj55Yl+RdBSIVYfNQ3JD1p4nRO5E 21 | Rl4POp4jgoYOhPRFr8oZmwnUP8cZNOxNE8ll1yqVXoIM30iscSmCJUI8cngJ1dJ3 22 | ouVVgytcaYETHm7dwAQbgxfb4grxv/S8nloC9rbDPoCujJOeXQHICQwJcTmR/73+ 23 | pLzTxMAVV6gJA4stGvCwE2IeTtJsoU3fV5HEhXAuQhIks4B8/SGTBZBoLhShTUqS 24 | Yb0fSHOTubjAirPV8krBAoIBAQDbbwk+zDtIUwqBqT4pVstb/h4+rYQDQL0cNgUq 25 | bioyrr87QtRbtt8XLjCC//+B21OghcYCRMwLulZ706znCfTOmcMPZVuJsbWTk/mC 26 | qt5hM/sFD6z5IMHOdIj7x7Rlhx3akA0HBFy+GZtNmBoQyo6j9h550SdKDIZTfeAY 27 | J7cKcz41uEfNpZgeQgdu9jL+wbh3IaZ9S2IDGQO6pNjfIDiLvqrUTLWrHAcM9s4a 28 | ozFCSBVlIAWXjExhJ3N3/zIA4Mvk2qyU4j6J/g4QvHsAor8sz6vdnWMsMXq0n6aG 29 | 9ntgp94HNbosU3Ko3BHsEJopzcxXn+okM5sWDoMMVgfzDrZhAoIBAQDQMUcyyrn8 30 | sZh6VA2cN8uAPbjkBhzPYYSXpAa7p+jT4CD55+dhwjqjiVX2xxklwj8k4a8dhfFL 31 | MKA10NUH+FviiLEawqpgImysu1FUhxRIS8f8E5X3fxHtQWu70fBZjb3p71rmjhlY 32 | Tr12XeVvJKecG2jal7KwxPuMVLVQAXaRqu+3SE+GLtMv1X+zKSpvEr/CsMXyZ7hT 33 | SGFBdLi447lOLGRN82ZItCqKGxTmT6depN2XRDu13d1OHk4kSpb1YI3lajhsKL7g 34 | vPhd1VJszpbVgN5rADGfQvWzWe2HLKtorkt5Au0rDkEfNdsVLX2tCGQNHZ8yXeco 35 | gTj2wZJGfJFjAoIBAQDKrrcFcDNZzIop1Z97I5ZW9FQPZMpJDuUeR69hz7vecJZm 36 | MIZh6HoLuThJ6BejZGjMHoQU2GL4ejcjzRMpnIKoylHnyKFSf/jNxaJz1Uvu0MqN 37 | lDsbKeyZu/5DQeUY2kLy/Jdr4dWgKZrPgyygUdiLDex8bHoz5Xm1aNEyvoxNdMED 38 | caGxC8GEQU0IaxQTR/AQ6d4UYSq43cQaA+Xlwqc4PPchfXFYCV1h1h3tcMsxA8/v 39 | RjKkFoz+OChpsCgJs5nhWzKJmqhVYXqwbsfWgHzA8Vk4LAXMbi9+4vA4PTccwjFM 40 | y42ZH8MKwas0NumOr26NiUIGCjy3lNPq8xQIp5BBAoIBAQCHdIhuc5gu4R3j+Wwh 41 | h+vPtFjng1KbW0d5oi7/SXAi6mCKOGhDIqwkWuajeUbTWl7bEDtvagZkdW7HlOgG 42 | F4ExEt6oGp/fjIZInFd+N6TqpOOpDtU0AmkXhMkjmqRWn/JAkosCFtJGsnRy3wS4 43 | G5Ex8GN4VdrdSEyiMTsGou0SObVd+p5DH5QoOzCq0M3bFsfNVFZ2MSWsihs9C0Rk 44 | h0W5pwhb71FNXGuRD625a3nqCjpigPKYkZG7kdwloKI3ZGruKP4s4RXQAyNVacYH 45 | JSLeJsqPs8CVbmuOFaSFnnqn0T8prM3ChbO5KsEwNjjeq+bs4akCjDYqFGmLosYZ 46 | NlMhAoIBADqds6s0/8elWPiLZAPrJKI383YzFroXGEXSNVOUQLDK0TNuW+RSQLus 47 | tA6YD7IdqIgYxHL1jk2kZDB1SlGi2+dtTvXrKXcvtbA6p7SprjrIqsGFpOAOBMjB 48 | rt1qcAkLV7QY4B0JnFDYN/MzhsXqrMksUERj0zitdrd8UQkM50BuMqsHOJa57o8o 49 | XJBWTGoeqblS6LvAKA6Hvf59B1iMWFDXZlhMnI0+jXYZcM9E9T6t9w5OrmN3b0kH 50 | zwJVWGAq9g13GpQjl/mLJHL2WG4/3dzVafyb2vy9pIMMp6dCRwLS9n3w5wzxqTrp 51 | FRc4BkVecBOq/WYXqKWZ/tFJ/Ib/uG0= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-512.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mastercard/client-encryption-csharp/7c9250c5a0c5f0d0d8520eb2c69e29ae09787a32/Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-512.der -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.Tests/Tests/_Resources/Keys/Pkcs8/test_key_pkcs8-512.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEArnaGWuy6JFTryzOL 3 | 5Sj6DxTV9dVwPcPWV3p9ihr33ZGcTR2juFzWGxTQFKMRJg63rr7+ISphxDAepFRf 4 | LDbOKwIDAQABAkADPrsjB9IyiM3V+sB4Y7m6/BU6vFyZGYJsICAjqHrByUKsSXfj 5 | YTf0O8gs/9BNmvx/pl91fWfQS/NqAj1XdVdJAiEA6GJHQUlSvwgGfQrf0rVLWWOA 6 | aBkR+GFwop19oD+6JTUCIQDAMV7aYV3QNOw3krCzHfWJ146TilQzRCLhDJTwjJRx 7 | 3wIgRY/1yINMc8bROmkg6xA+B/oTHBY1HOb+Mo92ZZvt+ukCIFoozu5zLqc1rHqF 8 | fg8Ixt7bGC9ufQFvvU0FsfkGebzRAiBhuLkH4fTAIyDjtTAm0DbJ+80eub5P/qm0 9 | fjESCk2mng== 10 | -----END PRIVATE KEY----- 11 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30406.217 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mastercard.Developer.ClientEncryption.Core", "Mastercard.Developer.ClientEncryption.Core\Mastercard.Developer.ClientEncryption.Core.csproj", "{8FFA4F79-9D2C-4807-BAD7-8E2F0A15E099}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mastercard.Developer.ClientEncryption.RestSharp", "Mastercard.Developer.ClientEncryption.RestSharp\Mastercard.Developer.ClientEncryption.RestSharp.csproj", "{4ABDAD88-8DC4-4463-91EC-74A711D6ACFB}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9FF81251-FD39-4A8E-9092-99D23D1EFFE3}" 11 | ProjectSection(SolutionItems) = preProject 12 | LICENSE = LICENSE 13 | README.md = README.md 14 | EndProjectSection 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mastercard.Developer.ClientEncryption.RestSharpV2", "Mastercard.Developer.ClientEncryption.RestSharpV2\Mastercard.Developer.ClientEncryption.RestSharpV2.csproj", "{BC6F2C72-16A3-44CB-AD68-3ACD8A465640}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mastercard.Developer.ClientEncryption.Tests", "Mastercard.Developer.ClientEncryption.Tests\Mastercard.Developer.ClientEncryption.Tests.csproj", "{5F44EDFF-8818-44F0-9A60-8FED8ABC36C0}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {8FFA4F79-9D2C-4807-BAD7-8E2F0A15E099}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {8FFA4F79-9D2C-4807-BAD7-8E2F0A15E099}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {8FFA4F79-9D2C-4807-BAD7-8E2F0A15E099}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {8FFA4F79-9D2C-4807-BAD7-8E2F0A15E099}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {4ABDAD88-8DC4-4463-91EC-74A711D6ACFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {4ABDAD88-8DC4-4463-91EC-74A711D6ACFB}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {4ABDAD88-8DC4-4463-91EC-74A711D6ACFB}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {4ABDAD88-8DC4-4463-91EC-74A711D6ACFB}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {DE07F478-E087-4FAA-80E2-220671C70418}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {DE07F478-E087-4FAA-80E2-220671C70418}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {DE07F478-E087-4FAA-80E2-220671C70418}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {DE07F478-E087-4FAA-80E2-220671C70418}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {BC6F2C72-16A3-44CB-AD68-3ACD8A465640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {BC6F2C72-16A3-44CB-AD68-3ACD8A465640}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {BC6F2C72-16A3-44CB-AD68-3ACD8A465640}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {BC6F2C72-16A3-44CB-AD68-3ACD8A465640}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {6BDFFEF6-994D-404E-AA54-9D2319CD6776}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {6BDFFEF6-994D-404E-AA54-9D2319CD6776}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {6BDFFEF6-994D-404E-AA54-9D2319CD6776}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {6BDFFEF6-994D-404E-AA54-9D2319CD6776}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {5F44EDFF-8818-44F0-9A60-8FED8ABC36C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {5F44EDFF-8818-44F0-9A60-8FED8ABC36C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {5F44EDFF-8818-44F0-9A60-8FED8ABC36C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {5F44EDFF-8818-44F0-9A60-8FED8ABC36C0}.Release|Any CPU.Build.0 = Release|Any CPU 50 | EndGlobalSection 51 | GlobalSection(SolutionProperties) = preSolution 52 | HideSolutionNode = FALSE 53 | EndGlobalSection 54 | GlobalSection(ExtensibilityGlobals) = postSolution 55 | SolutionGuid = {0BA6CB82-AD23-4F2E-BEC3-CA02F18DFF99} 56 | EndGlobalSection 57 | EndGlobal 58 | -------------------------------------------------------------------------------- /Mastercard.Developer.ClientEncryption.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True 7 | True 8 | True 9 | True 10 | True 11 | True 12 | True -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mastercard/client-encryption-csharp/7c9250c5a0c5f0d0d8520eb2c69e29ae09787a32/icon.png --------------------------------------------------------------------------------