├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── config.yml ├── dependabot.yml └── workflows │ ├── merge-dependabot.yml │ ├── on-push-do-doco.yml │ └── on-tag-do-release.yml ├── .gitignore ├── license.txt ├── readme.md ├── readme.source.md └── src ├── .editorconfig ├── .gitattributes ├── Directory.Build.props ├── NServiceBus.Newtonsoft.Encryption.Tests ├── NServiceBus.Newtonsoft.Encryption.Tests.csproj └── Snippets │ └── Snippets.cs ├── NServiceBus.Newtonsoft.Encryption ├── EncryptionConfigurationExtensions.cs ├── EncryptionFeature.cs ├── FodyWeavers.xml ├── NServiceBus.Newtonsoft.Encryption.csproj └── Pipeline │ ├── DecryptBehavior.cs │ ├── DecryptRegistration.cs │ ├── EncryptBehavior.cs │ └── EncryptRegistration.cs ├── NServiceBusSample ├── Handler.cs ├── MessageWithSecretData.cs ├── NServiceBusSample.csproj └── Program.cs ├── Newtonsoft.Json.Encryption.Tests ├── AsyncUsage.cs ├── CollectionUsage.ByteArrayList.verified.txt ├── CollectionUsage.GuidEnumerable.verified.txt ├── CollectionUsage.StringCollection.verified.txt ├── CollectionUsage.StringEnumerable.verified.txt ├── CollectionUsage.StringList.verified.txt ├── CollectionUsage.WithConstructorEnumerableParameter.verified.txt ├── CollectionUsage.cs ├── CryptoBuilder.cs ├── DictionaryUsage.ByteArrayDictionary.verified.txt ├── DictionaryUsage.IntGuidDictionary.verified.txt ├── DictionaryUsage.IntStringDictionary.verified.txt ├── DictionaryUsage.StringDictionary.verified.txt ├── DictionaryUsage.cs ├── EncryptionFactoryTest.ExampleUsage.verified.txt ├── EncryptionFactoryTest.RoundTrip.verified.txt ├── EncryptionFactoryTest.Simple.verified.txt ├── EncryptionFactoryTest.cs ├── Extensions.cs ├── FodyWeavers.xml ├── Newtonsoft.Json.Encryption.Tests.csproj ├── NodeProp.Node.verified.txt ├── NodeProp.NodeWithConverter.verified.txt ├── NodeProp.cs ├── ReverseConverter.cs ├── RoundTrip.cs ├── SimpleProp.ByteArray.verified.txt ├── SimpleProp.EmptyString.verified.txt ├── SimpleProp.Guid.verified.txt ├── SimpleProp.NullString.verified.txt ├── SimpleProp.String.verified.txt ├── SimpleProp.cs ├── Snippets │ └── Snippets.cs └── TestBase.cs ├── Newtonsoft.Json.Encryption.sln ├── Newtonsoft.Json.Encryption.sln.DotSettings ├── Newtonsoft.Json.Encryption ├── ContractResolver.cs ├── Converters │ ├── ByteArrayConverter.cs │ ├── GuidConverter.cs │ ├── NodeConverter.cs │ ├── StringConverter.cs │ └── WrappedNodeConverter.cs ├── CryptoState.cs ├── EncryptAttribute.cs ├── Encrypter.cs ├── EncryptionFactory.cs ├── Extensions.cs ├── FodyWeavers.xml ├── InternalsVisibleTo.cs ├── JsonPropertyHelper.cs ├── Newtonsoft.Json.Encryption.csproj └── NodeEncryptAttribute.cs ├── Rebus.Newtonsoft.Encryption.Tests ├── Rebus.Newtonsoft.Encryption.Tests.csproj └── Snippets │ └── Snippets.cs ├── Rebus.Newtonsoft.Encryption ├── DecryptStep.cs ├── EncryptStep.cs ├── EncryptionConfigurationExtensions.cs ├── FodyWeavers.xml └── Rebus.Newtonsoft.Encryption.csproj ├── RebusSample ├── Handler.cs ├── MessageWithSecretData.cs ├── Program.cs └── RebusSample.csproj ├── Sample ├── ClassToSerialize.cs ├── Program.cs └── Sample.csproj ├── Shared ├── AlgorithmBuilders.cs ├── HeaderExtractor.cs ├── KeyIdAndIvHeaderMismatchException.cs └── Shared.csproj ├── appveyor.yml ├── global.json ├── icon.png ├── key.snk └── mdsnippets.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space:error 8 | 9 | [*.cs] 10 | indent_size = 4:error 11 | charset = utf-8-bom:error 12 | 13 | # Sort using and Import directives with System.* appearing first 14 | dotnet_sort_system_directives_first = true:error 15 | 16 | # Avoid "this." and "Me." if not necessary 17 | dotnet_style_qualification_for_field = false:error 18 | dotnet_style_qualification_for_property = false:error 19 | dotnet_style_qualification_for_method = false:error 20 | dotnet_style_qualification_for_event = false:error 21 | 22 | # Use language keywords instead of framework type names for type references 23 | dotnet_style_predefined_type_for_locals_parameters_members = true:error 24 | dotnet_style_predefined_type_for_member_access = true:error 25 | 26 | # Suggest more modern language features when available 27 | dotnet_style_object_initializer = true:suggestion 28 | dotnet_style_collection_initializer = true:suggestion 29 | dotnet_style_coalesce_expression = false:suggestion 30 | dotnet_style_null_propagation = true:suggestion 31 | dotnet_style_explicit_tuple_names = true:suggestion 32 | 33 | # Prefer "var" everywhere 34 | csharp_style_var_for_built_in_types = true:error 35 | csharp_style_var_when_type_is_apparent = true:error 36 | csharp_style_var_elsewhere = true:error 37 | 38 | # Prefer method-like constructs to have a block body 39 | csharp_style_expression_bodied_methods = false:none 40 | csharp_style_expression_bodied_constructors = false:none 41 | csharp_style_expression_bodied_operators = false:none 42 | 43 | # Prefer property-like constructs to have an expression-body 44 | csharp_style_expression_bodied_properties = true:suggestion 45 | csharp_style_expression_bodied_indexers = true:suggestion 46 | csharp_style_expression_bodied_accessors = true:none 47 | 48 | # Suggest more modern language features when available 49 | csharp_style_pattern_matching_over_is_with_cast_check = true:error 50 | csharp_style_pattern_matching_over_as_with_null_check = true:error 51 | csharp_style_inlined_variable_declaration = true:suggestion 52 | csharp_style_throw_expression = true:suggestion 53 | csharp_style_conditional_delegate_call = true:suggestion 54 | 55 | # Newline settings 56 | #csharp_new_line_before_open_brace = all:error 57 | csharp_new_line_before_else = true:error 58 | csharp_new_line_before_catch = true:error 59 | csharp_new_line_before_finally = true:error 60 | csharp_new_line_before_members_in_object_initializers = true:error 61 | csharp_new_line_before_members_in_anonymous_types = true:error 62 | 63 | #braces 64 | #csharp_prefer_braces = true:error 65 | 66 | # msbuild 67 | [*.{csproj,targets,props}] 68 | indent_size = 2 69 | 70 | # Xml files 71 | [*.{xml,config,nuspec,resx,vsixmanifest}] 72 | indent_size = 2 73 | resharper_xml_wrap_tags_and_pi = true:error 74 | 75 | # JSON files 76 | [*.json] 77 | indent_size = 2 78 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: nservicebusextensions -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: StackOverflow 4 | url: https://stackoverflow.com/ 5 | about: Please ask and answer questions here. -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/src" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/merge-dependabot.yml: -------------------------------------------------------------------------------- 1 | name: merge-dependabot 2 | on: 3 | pull_request: 4 | jobs: 5 | automerge: 6 | runs-on: ubuntu-latest 7 | if: github.actor == 'dependabot[bot]' 8 | steps: 9 | - name: Dependabot Auto Merge 10 | uses: ahmadnassri/action-dependabot-auto-merge@v2.3.1 11 | with: 12 | target: minor 13 | github-token: ${{ secrets.dependabot }} 14 | command: squash and merge -------------------------------------------------------------------------------- /.github/workflows/on-push-do-doco.yml: -------------------------------------------------------------------------------- 1 | name: on-push-do-doco 2 | on: 3 | push: 4 | jobs: 5 | release: 6 | runs-on: windows-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - name: Run MarkdownSnippets 10 | run: | 11 | dotnet tool install --global MarkdownSnippets.Tool 12 | mdsnippets ${GITHUB_WORKSPACE} 13 | shell: bash 14 | - name: Push changes 15 | run: | 16 | git config --local user.email "action@github.com" 17 | git config --local user.name "GitHub Action" 18 | git commit -m "Doco changes" -a || echo "nothing to commit" 19 | remote="https://${GITHUB_ACTOR}:${{secrets.GITHUB_TOKEN}}@github.com/${GITHUB_REPOSITORY}.git" 20 | branch="${GITHUB_REF:11}" 21 | git push "${remote}" ${branch} || echo "nothing to push" 22 | shell: bash -------------------------------------------------------------------------------- /.github/workflows/on-tag-do-release.yml: -------------------------------------------------------------------------------- 1 | name: on-tag-do-release 2 | on: 3 | push: 4 | tags: 5 | - '[0-9]+\.[0-9]+\.[0-9]+' 6 | jobs: 7 | release: 8 | runs-on: windows-latest 9 | steps: 10 | - name: Run 11 | run: | 12 | dotnet tool install --global GitReleaseManager.Tool 13 | tag="${GITHUB_REF:10}" 14 | owner="${GITHUB_REPOSITORY%/*}" 15 | repo="${GITHUB_REPOSITORY#*/}" 16 | dotnet-gitreleasemanager create -m ${tag} --token ${{secrets.GITHUB_TOKEN}} -o ${owner} -r ${repo} 17 | dotnet-gitreleasemanager close -m ${tag} --token ${{secrets.GITHUB_TOKEN}} -o ${owner} -r ${repo} 18 | dotnet-gitreleasemanager publish -t ${tag} --token ${{secrets.GITHUB_TOKEN}} -o ${owner} -r ${repo} 19 | shell: bash -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Visual Studio 3 | ################# 4 | 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | 8 | # User-specific files 9 | *.suo 10 | *.user 11 | *.sln.docstates 12 | *.pidb 13 | *.ncrunchsolution 14 | *.ncrunchproject 15 | 16 | .learningtransport 17 | .idea/ 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Rr]elease/ 22 | *_i.c 23 | *_p.c 24 | *.ilk 25 | *.meta 26 | *.obj 27 | *.pch 28 | *.pgc 29 | *.pgd 30 | *.rsp 31 | *.sbr 32 | *.tlb 33 | *.tli 34 | *.tlh 35 | *.tmp 36 | *.vspscc 37 | .builds 38 | *.dotCover 39 | *test-results* 40 | 41 | .vs/ 42 | 43 | nugets/ 44 | 45 | # Visual Studio profiler 46 | *.psess 47 | *.vsp 48 | 49 | # ReSharper is a .NET coding add-in 50 | _ReSharper* 51 | 52 | # Others 53 | [Bb]in 54 | [Oo]bj 55 | sql 56 | TestResults 57 | *.Cache 58 | ClientBin 59 | stylecop.* 60 | ~$* 61 | *.dbmdl 62 | Generated_Code #added for RIA/Silverlight projects 63 | 64 | # Backup & report files from converting an old project file to a newer 65 | # Visual Studio version. Backup files are not needed, because we have git ;-) 66 | _UpgradeReport_Files/ 67 | Backup*/ 68 | UpgradeLog*.XML 69 | 70 | 71 | ############ 72 | ## Windows 73 | ############ 74 | 75 | # Windows image file caches 76 | Thumbs.db 77 | 78 | # Folder config file 79 | Desktop.ini 80 | 81 | 82 | Fody/.DS_Store 83 | 84 | .DS_Store 85 | 86 | *.userprefs 87 | *.received.txt 88 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Simon Cropp 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. -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Newtonsoft.Json.Encryption 9 | 10 | [![Build status](https://ci.appveyor.com/api/projects/status/qciwf7sdysdeu693/branch/main?svg=true)](https://ci.appveyor.com/project/SimonCropp/newtonsoft-json-encryption) 11 | [![NuGet Status](https://img.shields.io/nuget/v/Newtonsoft.Json.Encryption.svg?label=Newtonsoft.Json.Encryption)](https://www.nuget.org/packages/Newtonsoft.Json.Encryption/) 12 | [![NuGet Status](https://img.shields.io/nuget/v/Rebus.Newtonsoft.Encryption.svg?label=Rebus.Newtonsoft.Encryption)](https://www.nuget.org/packages/Rebus.Newtonsoft.Encryption/) 13 | [![NuGet Status](https://img.shields.io/nuget/v/NServiceBus.Newtonsoft.Encryption.svg?label=NServiceBus.Newtonsoft.Encryption)](https://www.nuget.org/packages/NServiceBus.Newtonsoft.Encryption/) 14 | 15 | 16 | Leverages the Newtonsoft extension API to encrypt/decrypt specific nodes at serialization time. So only the nodes that require encryption are touched, the remaining content is still human readable. This approach provides an compromise between readability/debugabaility and security. 17 | 18 | 19 | 20 | [Already a Patron? skip past this section](#endofbacking) 21 | 22 | 23 | ## Community backed 24 | 25 | **It is expected that all developers either [become a Patron](https://opencollective.com/nservicebusextensions/contribute/patron-6976) to use NServiceBusExtensions. [Go to licensing FAQ](https://github.com/NServiceBusExtensions/Home/#licensingpatron-faq)** 26 | 27 | 28 | ### Sponsors 29 | 30 | Support this project by [becoming a Sponsor](https://opencollective.com/nservicebusextensions/contribute/sponsor-6972). The company avatar will show up here with a website link. The avatar will also be added to all GitHub repositories under the [NServiceBusExtensions organization](https://github.com/NServiceBusExtensions). 31 | 32 | 33 | ### Patrons 34 | 35 | Thanks to all the backing developers. Support this project by [becoming a patron](https://opencollective.com/nservicebusextensions/contribute/patron-6976). 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ## Contents 46 | 47 | * [Encryption Algorithms](#encryption-algorithms) 48 | * [Decorating properties](#decorating-properties) 49 | * [Serialized](#serialized) 50 | * [Supported property types](#supported-property-types) 51 | * [Usage](#usage) 52 | * [Breakdown](#breakdown) 53 | * [Key](#key) 54 | * [EncryptionFactory and JsonSerializer](#encryptionfactory-and-jsonserializer) 55 | * [Serialization](#serialization) 56 | * [Deserialization](#deserialization) 57 | * [Rebus](#rebus) 58 | * [NServiceBus](#nservicebus) 59 | 60 | 61 | ## NuGet packages 62 | 63 | * https://nuget.org/packages/Newtonsoft.Json.Encryption/ 64 | * https://nuget.org/packages/Rebus.Newtonsoft.Encryption/ 65 | * https://nuget.org/packages/NServiceBus.Newtonsoft.Encryption/ 66 | 67 | 68 | ## Encryption Algorithms 69 | 70 | Any implementation of [SymmetricAlgorithm](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.aspx) is supported. 71 | 72 | 73 | ## Decorating properties 74 | 75 | ```C# 76 | public class ClassToSerialize 77 | { 78 | [Encrypt] 79 | public string Property { get; set; } 80 | } 81 | ``` 82 | 83 | 84 | ## Serialized 85 | 86 | ```C# 87 | { 88 | "Property":"wSayABpFI3g7a/D6gGTq5g==" 89 | } 90 | ``` 91 | 92 | 93 | ## Supported property types 94 | 95 | * `string` 96 | * `byte[]` 97 | * `Guid` 98 | * `IDictionary` 99 | * `IDictionary` 100 | * `IDictionary` 101 | * `IEnumerable` 102 | * `IEnumerable` 103 | * `IEnumerable` 104 | 105 | Note that only the values in a `IDictionary` are encrypted. 106 | 107 | 108 | ## Usage 109 | 110 | The full serialize and deserialization workflow: 111 | 112 | 113 | ```cs 114 | // per system (periodically rotated) 115 | var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6"); 116 | 117 | // per app domain 118 | using var factory = new EncryptionFactory(); 119 | var serializer = new JsonSerializer 120 | { 121 | ContractResolver = factory.GetContractResolver() 122 | }; 123 | 124 | // transferred as meta data with the serialized payload 125 | byte[] initVector; 126 | 127 | string serialized; 128 | 129 | // per serialize session 130 | using (var algorithm = new RijndaelManaged 131 | { 132 | Key = key 133 | }) 134 | { 135 | //TODO: store initVector for use in deserialization 136 | initVector = algorithm.IV; 137 | using (factory.GetEncryptSession(algorithm)) 138 | { 139 | var instance = new ClassToSerialize 140 | { 141 | Property = "PropertyValue", 142 | }; 143 | var builder = new StringBuilder(); 144 | using (var writer = new StringWriter(builder)) 145 | { 146 | serializer.Serialize(writer, instance); 147 | } 148 | 149 | serialized = builder.ToString(); 150 | } 151 | } 152 | 153 | // per deserialize session 154 | using (var algorithm = new RijndaelManaged 155 | { 156 | IV = initVector, 157 | Key = key 158 | }) 159 | { 160 | using (factory.GetDecryptSession(algorithm)) 161 | { 162 | using var stringReader = new StringReader(serialized); 163 | using var jsonReader = new JsonTextReader(stringReader); 164 | var deserialized = serializer.Deserialize(jsonReader); 165 | Console.WriteLine(deserialized!.Property); 166 | } 167 | } 168 | ``` 169 | snippet source | anchor 170 | 171 | 172 | 173 | ## Breakdown 174 | 175 | 176 | ### Key 177 | 178 | See [SymmetricAlgorithm.Key](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.key.aspx). 179 | 180 | Example Key used for [RijndaelManaged algorithm](https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged.aspx) in the below sample code: 181 | 182 | ```C# 183 | var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6"); 184 | ``` 185 | 186 | A new valid key can be generated by instanitiating a [SymmetricAlgorithm](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.aspx) and accessing [SymmetricAlgorithm.Key](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.key.aspx). 187 | 188 | 189 | ### EncryptionFactory and JsonSerializer 190 | 191 | Generally a single instance of `EncryptionFactory` will exist per AppDomain. 192 | 193 | A single instance of `EncryptionFactory` is safe to be used for multiple instances of `JsonSerializer`. 194 | 195 | ```C# 196 | var factory = new EncryptionFactory(); 197 | 198 | var serializer = new JsonSerializer 199 | { 200 | ContractResolver = factory.GetContractResolver() 201 | }; 202 | ``` 203 | 204 | 205 | ### Serialization 206 | 207 | A single encrypt session is used per serialization instance. 208 | 209 | On instantiation the `SymmetricAlgorithm` will generate a valid [IV](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.iv.aspx). This is generally a good value to use for serialization and then stored for deserialization. 210 | 211 | 212 | 213 | ```cs 214 | // per serialize session 215 | using (var algorithm = new RijndaelManaged 216 | { 217 | Key = key 218 | }) 219 | { 220 | //TODO: store initVector for use in deserialization 221 | initVector = algorithm.IV; 222 | using (factory.GetEncryptSession(algorithm)) 223 | { 224 | var instance = new ClassToSerialize 225 | { 226 | Property = "PropertyValue", 227 | }; 228 | var builder = new StringBuilder(); 229 | using (var writer = new StringWriter(builder)) 230 | { 231 | serializer.Serialize(writer, instance); 232 | } 233 | 234 | serialized = builder.ToString(); 235 | } 236 | } 237 | ``` 238 | snippet source | anchor 239 | 240 | 241 | 242 | ### Deserialization 243 | 244 | A single decrypt session is used per serialization instance. 245 | 246 | * `key` must be the same as the one use for serialization. 247 | * `initVector` must be the same as the one use for serialization. It is safe to be transferred with the serialized text. 248 | 249 | 250 | 251 | 252 | ```cs 253 | // per deserialize session 254 | using (var algorithm = new RijndaelManaged 255 | { 256 | IV = initVector, 257 | Key = key 258 | }) 259 | { 260 | using (factory.GetDecryptSession(algorithm)) 261 | { 262 | using var stringReader = new StringReader(serialized); 263 | using var jsonReader = new JsonTextReader(stringReader); 264 | var deserialized = serializer.Deserialize(jsonReader); 265 | Console.WriteLine(deserialized!.Property); 266 | } 267 | } 268 | ``` 269 | snippet source | anchor 270 | 271 | 272 | 273 | ## Rebus 274 | 275 | 276 | 277 | ```cs 278 | var activator = new BuiltinHandlerActivator(); 279 | 280 | activator.Register(() => new Handler()); 281 | var configurer = Configure.With(activator); 282 | 283 | var encryptionFactory = new EncryptionFactory(); 284 | var settings = new JsonSerializerSettings 285 | { 286 | TypeNameHandling = TypeNameHandling.All, 287 | ContractResolver = encryptionFactory.GetContractResolver() 288 | }; 289 | configurer.Serialization(s => { s.UseNewtonsoftJson(settings); }); 290 | configurer.EnableJsonEncryption( 291 | encryptionFactory: encryptionFactory, 292 | encryptStateBuilder: () => 293 | ( 294 | algorithm: new RijndaelManaged 295 | { 296 | Key = key 297 | }, 298 | keyId: "1" 299 | ), 300 | decryptStateBuilder: (keyId, initVector) => 301 | new RijndaelManaged 302 | { 303 | Key = key, 304 | IV = initVector 305 | }); 306 | ``` 307 | snippet source | anchor 308 | 309 | 310 | 311 | ## NServiceBus 312 | 313 | 314 | 315 | ```cs 316 | var configuration = new EndpointConfiguration("NServiceBusSample"); 317 | var serialization = configuration.UseSerialization(); 318 | var encryptionFactory = new EncryptionFactory(); 319 | serialization.Settings( 320 | new() 321 | { 322 | ContractResolver = encryptionFactory.GetContractResolver() 323 | }); 324 | 325 | configuration.EnableJsonEncryption( 326 | encryptionFactory: encryptionFactory, 327 | encryptStateBuilder: () => 328 | ( 329 | algorithm: new RijndaelManaged 330 | { 331 | Key = key 332 | }, 333 | keyId: "1" 334 | ), 335 | decryptStateBuilder: (keyId, initVector) => 336 | new RijndaelManaged 337 | { 338 | Key = key, 339 | IV = initVector 340 | }); 341 | ``` 342 | snippet source | anchor 343 | 344 | 345 | 346 | ## Icon 347 | 348 | [Lock](https://thenounproject.com/term/lock/59296/) designed by [Mourad Mokrane](https://thenounproject.com/molumen/) from [The Noun Project](https://thenounproject.com). 349 | -------------------------------------------------------------------------------- /readme.source.md: -------------------------------------------------------------------------------- 1 | # Newtonsoft.Json.Encryption 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/qciwf7sdysdeu693/branch/main?svg=true)](https://ci.appveyor.com/project/SimonCropp/newtonsoft-json-encryption) 4 | [![NuGet Status](https://img.shields.io/nuget/v/Newtonsoft.Json.Encryption.svg?label=Newtonsoft.Json.Encryption)](https://www.nuget.org/packages/Newtonsoft.Json.Encryption/) 5 | [![NuGet Status](https://img.shields.io/nuget/v/Rebus.Newtonsoft.Encryption.svg?label=Rebus.Newtonsoft.Encryption)](https://www.nuget.org/packages/Rebus.Newtonsoft.Encryption/) 6 | [![NuGet Status](https://img.shields.io/nuget/v/NServiceBus.Newtonsoft.Encryption.svg?label=NServiceBus.Newtonsoft.Encryption)](https://www.nuget.org/packages/NServiceBus.Newtonsoft.Encryption/) 7 | 8 | 9 | Leverages the Newtonsoft extension API to encrypt/decrypt specific nodes at serialization time. So only the nodes that require encryption are touched, the remaining content is still human readable. This approach provides an compromise between readability/debugabaility and security. 10 | 11 | 12 | 13 | [Already a Patron? skip past this section](#endofbacking) 14 | 15 | 16 | ## Community backed 17 | 18 | **It is expected that all developers either [become a Patron](https://opencollective.com/nservicebusextensions/contribute/patron-6976) to use NServiceBusExtensions. [Go to licensing FAQ](https://github.com/NServiceBusExtensions/Home/#licensingpatron-faq)** 19 | 20 | 21 | ### Sponsors 22 | 23 | Support this project by [becoming a Sponsor](https://opencollective.com/nservicebusextensions/contribute/sponsor-6972). The company avatar will show up here with a website link. The avatar will also be added to all GitHub repositories under the [NServiceBusExtensions organization](https://github.com/NServiceBusExtensions). 24 | 25 | 26 | ### Patrons 27 | 28 | Thanks to all the backing developers. Support this project by [becoming a patron](https://opencollective.com/nservicebusextensions/contribute/patron-6976). 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | toc 38 | 39 | 40 | ## NuGet packages 41 | 42 | * https://nuget.org/packages/Newtonsoft.Json.Encryption/ 43 | * https://nuget.org/packages/Rebus.Newtonsoft.Encryption/ 44 | * https://nuget.org/packages/NServiceBus.Newtonsoft.Encryption/ 45 | 46 | 47 | ## Encryption Algorithms 48 | 49 | Any implementation of [SymmetricAlgorithm](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.aspx) is supported. 50 | 51 | 52 | ## Decorating properties 53 | 54 | ```C# 55 | public class ClassToSerialize 56 | { 57 | [Encrypt] 58 | public string Property { get; set; } 59 | } 60 | ``` 61 | 62 | 63 | ## Serialized 64 | 65 | ```C# 66 | { 67 | "Property":"wSayABpFI3g7a/D6gGTq5g==" 68 | } 69 | ``` 70 | 71 | 72 | ## Supported property types 73 | 74 | * `string` 75 | * `byte[]` 76 | * `Guid` 77 | * `IDictionary` 78 | * `IDictionary` 79 | * `IDictionary` 80 | * `IEnumerable` 81 | * `IEnumerable` 82 | * `IEnumerable` 83 | 84 | Note that only the values in a `IDictionary` are encrypted. 85 | 86 | 87 | ## Usage 88 | 89 | The full serialize and deserialization workflow: 90 | snippet: Workflow 91 | 92 | 93 | ## Breakdown 94 | 95 | 96 | ### Key 97 | 98 | See [SymmetricAlgorithm.Key](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.key.aspx). 99 | 100 | Example Key used for [RijndaelManaged algorithm](https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged.aspx) in the below sample code: 101 | 102 | ```C# 103 | var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6"); 104 | ``` 105 | 106 | A new valid key can be generated by instanitiating a [SymmetricAlgorithm](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.aspx) and accessing [SymmetricAlgorithm.Key](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.key.aspx). 107 | 108 | 109 | ### EncryptionFactory and JsonSerializer 110 | 111 | Generally a single instance of `EncryptionFactory` will exist per AppDomain. 112 | 113 | A single instance of `EncryptionFactory` is safe to be used for multiple instances of `JsonSerializer`. 114 | 115 | ```C# 116 | var factory = new EncryptionFactory(); 117 | 118 | var serializer = new JsonSerializer 119 | { 120 | ContractResolver = factory.GetContractResolver() 121 | }; 122 | ``` 123 | 124 | 125 | ### Serialization 126 | 127 | A single encrypt session is used per serialization instance. 128 | 129 | On instantiation the `SymmetricAlgorithm` will generate a valid [IV](https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.iv.aspx). This is generally a good value to use for serialization and then stored for deserialization. 130 | 131 | snippet: serialize 132 | 133 | 134 | ### Deserialization 135 | 136 | A single decrypt session is used per serialization instance. 137 | 138 | * `key` must be the same as the one use for serialization. 139 | * `initVector` must be the same as the one use for serialization. It is safe to be transferred with the serialized text. 140 | 141 | 142 | snippet: deserialize 143 | 144 | 145 | ## Rebus 146 | 147 | snippet: RebugsUsage 148 | 149 | 150 | ## NServiceBus 151 | 152 | snippet: NsbUsage 153 | 154 | 155 | ## Icon 156 | 157 | [Lock](https://thenounproject.com/term/lock/59296/) designed by [Mourad Mokrane](https://thenounproject.com/molumen/) from [The Noun Project](https://thenounproject.com). -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | # EditorConfig: http://EditorConfig.org 3 | 4 | # top-most EditorConfig file 5 | 6 | [*] 7 | indent_style = space 8 | 9 | 10 | [*.cs] 11 | indent_size = 4 12 | charset = utf-8 13 | 14 | 15 | # Microsoft .NET properties 16 | csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:suggestion 17 | csharp_style_namespace_declarations = file_scoped:error 18 | dotnet_naming_rule.private_constants_rule.import_to_resharper = as_predefined 19 | dotnet_naming_rule.private_constants_rule.severity = warning 20 | dotnet_naming_rule.private_constants_rule.style = upper_camel_case_style 21 | dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols 22 | dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = as_predefined 23 | dotnet_naming_rule.private_instance_fields_rule.severity = warning 24 | dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style 25 | dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols 26 | dotnet_naming_rule.private_static_fields_rule.import_to_resharper = as_predefined 27 | dotnet_naming_rule.private_static_fields_rule.severity = warning 28 | dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style 29 | dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols 30 | dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = as_predefined 31 | dotnet_naming_rule.private_static_readonly_rule.severity = warning 32 | dotnet_naming_rule.private_static_readonly_rule.style = upper_camel_case_style 33 | dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols 34 | dotnet_naming_style.lower_camel_case_style.capitalization = camel_case 35 | dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case 36 | dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private 37 | dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field 38 | dotnet_naming_symbols.private_constants_symbols.required_modifiers = const 39 | dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private 40 | dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field 41 | dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private 42 | dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field 43 | dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static 44 | dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private 45 | dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field 46 | dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static, readonly 47 | dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none 48 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none 49 | dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none 50 | 51 | # ReSharper properties 52 | resharper_apply_on_completion = true 53 | resharper_csharp_wrap_lines = false 54 | resharper_object_creation_when_type_not_evident = target_typed 55 | 56 | # ReSharper inspection severities 57 | resharper_arrange_object_creation_when_type_evident_highlighting = error 58 | resharper_arrange_object_creation_when_type_not_evident_highlighting = error 59 | resharper_arrange_redundant_parentheses_highlighting = error 60 | resharper_arrange_static_member_qualifier_highlighting = error 61 | resharper_arrange_this_qualifier_highlighting = hint 62 | resharper_arrange_type_member_modifiers_highlighting = none 63 | resharper_built_in_type_reference_style_for_member_access_highlighting = hint 64 | resharper_built_in_type_reference_style_highlighting = hint 65 | resharper_check_namespace_highlighting = none 66 | resharper_convert_to_using_declaration_highlighting = error 67 | resharper_css_not_resolved_highlighting = warning 68 | resharper_field_can_be_made_read_only_local_highlighting = none 69 | resharper_merge_into_logical_pattern_highlighting = warning 70 | resharper_merge_into_pattern_highlighting = error 71 | resharper_method_has_async_overload_highlighting = warning 72 | resharper_partial_type_with_single_part_highlighting = error 73 | resharper_redundant_base_qualifier_highlighting = warning 74 | resharper_redundant_cast_highlighting = error 75 | resharper_redundant_empty_object_creation_argument_list_highlighting = error 76 | resharper_redundant_empty_object_or_collection_initializer_highlighting = error 77 | resharper_redundant_name_qualifier_highlighting = error 78 | resharper_redundant_suppress_nullable_warning_expression_highlighting = error 79 | resharper_redundant_using_directive_highlighting = error 80 | resharper_redundant_verbatim_string_prefix_highlighting = error 81 | resharper_replace_substring_with_range_indexer_highlighting = warning 82 | resharper_suggest_var_or_type_built_in_types_highlighting = hint 83 | resharper_suggest_var_or_type_elsewhere_highlighting = hint 84 | resharper_suggest_var_or_type_simple_types_highlighting = hint 85 | resharper_unnecessary_whitespace_highlighting = error 86 | resharper_use_await_using_highlighting = warning 87 | resharper_use_deconstruction_highlighting = warning 88 | 89 | # Sort using and Import directives with System.* appearing first 90 | dotnet_sort_system_directives_first = true 91 | 92 | # Avoid "this." and "Me." if not necessary 93 | dotnet_style_qualification_for_field = false:error 94 | dotnet_style_qualification_for_property = false:error 95 | dotnet_style_qualification_for_method = false:error 96 | dotnet_style_qualification_for_event = false:error 97 | 98 | # Use language keywords instead of framework type names for type references 99 | dotnet_style_predefined_type_for_locals_parameters_members = true:error 100 | dotnet_style_predefined_type_for_member_access = true:error 101 | 102 | # Suggest more modern language features when available 103 | dotnet_style_object_initializer = true:suggestion 104 | dotnet_style_collection_initializer = true:suggestion 105 | dotnet_style_coalesce_expression = false:suggestion 106 | dotnet_style_null_propagation = true:suggestion 107 | dotnet_style_explicit_tuple_names = true:suggestion 108 | 109 | # Prefer "var" everywhere 110 | csharp_style_var_for_built_in_types = true:error 111 | csharp_style_var_when_type_is_apparent = true:error 112 | csharp_style_var_elsewhere = true:error 113 | 114 | # Prefer method-like constructs to have a block body 115 | csharp_style_expression_bodied_methods = true:error 116 | csharp_style_expression_bodied_constructors = true:error 117 | csharp_style_expression_bodied_operators = true:error 118 | csharp_place_expr_method_on_single_line = false 119 | 120 | # Prefer property-like constructs to have an expression-body 121 | csharp_style_expression_bodied_properties = true:error 122 | csharp_style_expression_bodied_indexers = true:error 123 | csharp_style_expression_bodied_accessors = true:error 124 | 125 | # Suggest more modern language features when available 126 | csharp_style_pattern_matching_over_is_with_cast_check = true:error 127 | csharp_style_pattern_matching_over_as_with_null_check = true:error 128 | csharp_style_inlined_variable_declaration = true:suggestion 129 | csharp_style_throw_expression = true:suggestion 130 | csharp_style_conditional_delegate_call = true:suggestion 131 | csharp_style_prefer_switch_expression = true:suggestion 132 | 133 | # Newline settings 134 | #csharp_new_line_before_open_brace = all:error 135 | max_array_initializer_elements_on_line = 1 136 | csharp_new_line_before_else = true 137 | csharp_new_line_before_catch = true 138 | csharp_new_line_before_finally = true 139 | csharp_new_line_before_members_in_object_initializers = true 140 | csharp_new_line_before_members_in_anonymous_types = true 141 | csharp_place_type_constraints_on_same_line = false:error 142 | csharp_wrap_before_first_type_parameter_constraint = true:error 143 | csharp_wrap_extends_list_style = true:error 144 | csharp_wrap_after_dot_in_method_calls false 145 | csharp_wrap_before_binary_pattern_op false 146 | resharper_csharp_wrap_object_and_collection_initializer_style = chop_always 147 | resharper_csharp_place_simple_initializer_on_single_line = false 148 | 149 | dotnet_style_require_accessibility_modifiers = never:error 150 | resharper_place_type_constraints_on_same_line = false 151 | 152 | #braces https://www.jetbrains.com/help/resharper/EditorConfig_CSHARP_CSharpCodeStylePageImplSchema.html#Braces 153 | braces_for_ifelse = required 154 | braces_for_foreach = required 155 | braces_for_while = required 156 | braces_for_dowhile = required 157 | braces_for_lock = required 158 | braces_for_fixed = required 159 | braces_for_for = required 160 | 161 | # Xml files 162 | [*.{xml,config,nuspec,resx,vsixmanifest,csproj,targets,props}] 163 | indent_size = 2 164 | # https://www.jetbrains.com/help/resharper/EditorConfig_XML_XmlCodeStylePageSchema.html#resharper_xml_blank_line_after_pi 165 | blank_line_after_pi = false 166 | space_before_self_closing = true 167 | 168 | [*.json] 169 | indent_size = 2 -------------------------------------------------------------------------------- /src/.gitattributes: -------------------------------------------------------------------------------- 1 | * text 2 | *.png binary 3 | *.snk binary 4 | 5 | *.verified.txt text eol=lf 6 | *.verified.xml text eol=lf 7 | *.verified.json text eol=lf -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CS1591;NU1608 5 | 2.3.0 6 | 1.0.0 7 | Newtonsoft, Encryption 8 | 9 | -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption.Tests/NServiceBus.Newtonsoft.Encryption.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption.Tests/Snippets/Snippets.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Newtonsoft.Json.Encryption; 3 | using NServiceBus.Newtonsoft.Encryption; 4 | // ReSharper disable UnusedParameter.Local 5 | #pragma warning disable SYSLIB0022 6 | 7 | class Snippets 8 | { 9 | void Usage(byte[] key) 10 | { 11 | #region NsbUsage 12 | var configuration = new EndpointConfiguration("NServiceBusSample"); 13 | var serialization = configuration.UseSerialization(); 14 | var encryptionFactory = new EncryptionFactory(); 15 | serialization.Settings( 16 | new() 17 | { 18 | ContractResolver = encryptionFactory.GetContractResolver() 19 | }); 20 | 21 | configuration.EnableJsonEncryption( 22 | encryptionFactory: encryptionFactory, 23 | encryptStateBuilder: () => 24 | ( 25 | algorithm: new RijndaelManaged 26 | { 27 | Key = key 28 | }, 29 | keyId: "1" 30 | ), 31 | decryptStateBuilder: (keyId, initVector) => 32 | new RijndaelManaged 33 | { 34 | Key = key, 35 | IV = initVector 36 | }); 37 | #endregion 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption/EncryptionConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | using NServiceBus.Configuration.AdvancedExtensibility; 3 | using NServiceBus.Settings; 4 | 5 | namespace NServiceBus.Newtonsoft.Encryption; 6 | 7 | /// 8 | /// Provides configuration extensions to configure message property encryption. 9 | /// 10 | public static class EncryptionConfigurationExtensions 11 | { 12 | /// 13 | /// Enable message property encryption. 14 | /// 15 | public static void EnableJsonEncryption(this EndpointConfiguration configuration, EncryptionFactory encryptionFactory, EncryptStateBuilder encryptStateBuilder, DecryptStateBuilder decryptStateBuilder) 16 | { 17 | var recoverability = configuration.Recoverability(); 18 | recoverability.AddUnrecoverableException(); 19 | var settings = configuration.GetSettings(); 20 | settings.Set(encryptStateBuilder); 21 | settings.Set(decryptStateBuilder); 22 | settings.Set(encryptionFactory); 23 | configuration.EnableFeature(); 24 | } 25 | 26 | internal static EncryptionFactory GetEncryptionFactory(this IReadOnlySettings settings) => 27 | settings.Get(); 28 | 29 | internal static EncryptStateBuilder GetEncryptStateBuilder(this IReadOnlySettings settings) => 30 | settings.Get(); 31 | 32 | internal static DecryptStateBuilder GetDecryptStateBuilder(this IReadOnlySettings settings) => 33 | settings.Get(); 34 | } -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption/EncryptionFeature.cs: -------------------------------------------------------------------------------- 1 | using NServiceBus.Features; 2 | using NServiceBus.Newtonsoft.Encryption; 3 | 4 | class EncryptionFeature : 5 | Feature 6 | { 7 | protected override void Setup(FeatureConfigurationContext context) 8 | { 9 | var settings = context.Settings; 10 | var pipeline = context.Pipeline; 11 | var encryptionFactory = settings.GetEncryptionFactory(); 12 | pipeline.Register(new EncryptRegistration(encryptionFactory, settings.GetEncryptStateBuilder())); 13 | pipeline.Register(new DecryptRegistration(encryptionFactory, settings.GetDecryptStateBuilder())); 14 | } 15 | } -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption/NServiceBus.Newtonsoft.Encryption.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption/Pipeline/DecryptBehavior.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | using NServiceBus.Pipeline; 3 | 4 | class DecryptBehavior : 5 | IBehavior 6 | { 7 | EncryptionFactory factory; 8 | DecryptStateBuilder stateBuilder; 9 | 10 | public DecryptBehavior(EncryptionFactory factory, DecryptStateBuilder stateBuilder) 11 | { 12 | this.factory = factory; 13 | this.stateBuilder = stateBuilder; 14 | } 15 | 16 | public async Task Invoke(IIncomingPhysicalMessageContext context, Func next) 17 | { 18 | if (!context.MessageHeaders.ReadKeyAndIv(out var keyId, out var iv)) 19 | { 20 | await next(context); 21 | return; 22 | } 23 | using (factory.GetDecryptSession(stateBuilder(keyId!, iv))) 24 | { 25 | await next(context); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption/Pipeline/DecryptRegistration.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | using NServiceBus.Pipeline; 3 | 4 | class DecryptRegistration : 5 | RegisterStep 6 | { 7 | public DecryptRegistration(EncryptionFactory encryptionFactory, DecryptStateBuilder stateBuilder) : 8 | base( 9 | stepId: "NServiceBusJsonEncryptionDecrypt", 10 | behavior: typeof(DecryptBehavior), 11 | description: "Invokes the decryption logic", 12 | factoryMethod: _ => new DecryptBehavior(encryptionFactory, stateBuilder)) => 13 | InsertBefore("MutateIncomingTransportMessage"); 14 | } -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption/Pipeline/EncryptBehavior.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | using NServiceBus.Pipeline; 3 | 4 | class EncryptBehavior : 5 | IBehavior 6 | { 7 | EncryptionFactory factory; 8 | EncryptStateBuilder stateBuilder; 9 | 10 | public EncryptBehavior(EncryptionFactory factory, EncryptStateBuilder stateBuilder) 11 | { 12 | this.factory = factory; 13 | this.stateBuilder = stateBuilder; 14 | } 15 | 16 | public async Task Invoke(IOutgoingLogicalMessageContext context, Func next) 17 | { 18 | var state = stateBuilder(); 19 | context.Headers.WriteKeyAndIv(state.keyId, state.algorithm.IV); 20 | using (factory.GetEncryptSession(state.algorithm)) 21 | { 22 | await next(context); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/NServiceBus.Newtonsoft.Encryption/Pipeline/EncryptRegistration.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | using NServiceBus.Pipeline; 3 | 4 | class EncryptRegistration : 5 | RegisterStep 6 | { 7 | public EncryptRegistration(EncryptionFactory encryptionFactory, EncryptStateBuilder stateBuilder) : 8 | base( 9 | stepId: "NServiceBusJsonEncryptionEncrypt", 10 | behavior: typeof(EncryptBehavior), 11 | description: "Invokes the encrypt logic", 12 | factoryMethod: _ => new EncryptBehavior(encryptionFactory, stateBuilder)) => 13 | InsertAfter("MutateOutgoingMessages"); 14 | } -------------------------------------------------------------------------------- /src/NServiceBusSample/Handler.cs: -------------------------------------------------------------------------------- 1 | using NServiceBus.Logging; 2 | 3 | public class Handler : 4 | IHandleMessages 5 | { 6 | static ILog log = LogManager.GetLogger(); 7 | 8 | public Task Handle(MessageWithSecretData message, IMessageHandlerContext context) 9 | { 10 | log.Info($"Secret: '{message.Secret}'"); 11 | log.Info($"SubSecret: {message.SubProperty?.Secret}"); 12 | if (message.CreditCards != null) 13 | { 14 | foreach (var creditCard in message.CreditCards) 15 | { 16 | log.Info($"CreditCard: {creditCard.Number} is valid to {creditCard.ValidTo}"); 17 | } 18 | } 19 | 20 | return Task.CompletedTask; 21 | } 22 | } -------------------------------------------------------------------------------- /src/NServiceBusSample/MessageWithSecretData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | 3 | public class MessageWithSecretData : 4 | IMessage 5 | { 6 | [Encrypt] 7 | public string? Secret { get; set; } 8 | public MySecretSubProperty? SubProperty { get; set; } 9 | public List? CreditCards { get; set; } 10 | } 11 | 12 | public class MySecretSubProperty 13 | { 14 | [Encrypt] 15 | public string? Secret { get; set; } 16 | } 17 | 18 | public class CreditCardDetails 19 | { 20 | public DateTime ValidTo { get; set; } 21 | [Encrypt] 22 | public string? Number { get; set; } 23 | } -------------------------------------------------------------------------------- /src/NServiceBusSample/NServiceBusSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/NServiceBusSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Newtonsoft.Json.Encryption; 3 | using NServiceBus.Newtonsoft.Encryption; 4 | // ReSharper disable UnusedParameter.Local 5 | #pragma warning disable SYSLIB0022 6 | 7 | class Program 8 | { 9 | static async Task Main() 10 | { 11 | var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6"); 12 | Console.Title = "NServiceBusSample"; 13 | 14 | var configuration = new EndpointConfiguration("NServiceBusSample"); 15 | var serialization = configuration.UseSerialization(); 16 | var encryptionFactory = new EncryptionFactory(); 17 | serialization.Settings( 18 | new() 19 | { 20 | ContractResolver = encryptionFactory.GetContractResolver() 21 | }); 22 | 23 | configuration.EnableJsonEncryption( 24 | encryptionFactory: encryptionFactory, 25 | encryptStateBuilder: () => 26 | ( 27 | algorithm: new RijndaelManaged 28 | { 29 | Key = key 30 | }, 31 | keyId: "1" 32 | ), 33 | decryptStateBuilder: (keyId, initVector) => 34 | new RijndaelManaged 35 | { 36 | Key = key, 37 | IV = initVector 38 | }); 39 | 40 | configuration.UsePersistence(); 41 | configuration.UseTransport(); 42 | var endpointInstance = await Endpoint.Start(configuration) 43 | ; 44 | Console.WriteLine("Press any key to exit"); 45 | 46 | await SendMessage(endpointInstance) 47 | ; 48 | 49 | Console.ReadKey(); 50 | await endpointInstance.Stop() 51 | ; 52 | encryptionFactory.Dispose(); 53 | } 54 | 55 | static Task SendMessage(IEndpointInstance endpointInstance) 56 | { 57 | var message = new MessageWithSecretData 58 | { 59 | Secret = "betcha can't guess my secret", 60 | SubProperty = new() 61 | { 62 | Secret = "My sub secret" 63 | }, 64 | CreditCards = new() 65 | { 66 | new() 67 | { 68 | ValidTo = DateTime.UtcNow.AddYears(1), 69 | Number = "312312312312312" 70 | }, 71 | new() 72 | { 73 | ValidTo = DateTime.UtcNow.AddYears(2), 74 | Number = "543645546546456" 75 | } 76 | } 77 | }; 78 | return endpointInstance.SendLocal(message); 79 | } 80 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/AsyncUsage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Encryption; 3 | 4 | public class AsyncUsage 5 | { 6 | [Fact] 7 | public async Task Works() 8 | { 9 | var target = new ClassWithString 10 | { 11 | Property = "Foo" 12 | }; 13 | 14 | using var factory = new EncryptionFactory(); 15 | using var algorithm = CryptoBuilder.Build(); 16 | var serializer = new JsonSerializer 17 | { 18 | ContractResolver = factory.GetContractResolver() 19 | }; 20 | 21 | string serialized; 22 | using (factory.GetEncryptSession(algorithm)) 23 | { 24 | await Task.Delay(1); 25 | serialized = serializer.Serialize(target); 26 | } 27 | 28 | using (factory.GetDecryptSession(algorithm)) 29 | { 30 | await Task.Delay(1); 31 | var result = serializer.Deserialize(serialized); 32 | Assert.Equal("Foo", result.Property); 33 | } 34 | } 35 | 36 | public class ClassWithString 37 | { 38 | [Encrypt] 39 | public string? Property { get; set; } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/CollectionUsage.ByteArrayList.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":["+4hZVXd5q/qJLcdnjgBFqA==","IUY+HDFpyXyLu4XvdG37Sg=="]} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/CollectionUsage.GuidEnumerable.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":["mdOE0iozY6GFE6qDwzndYSxgX3MLcKArCk0Tt04dswE=","h3HvFQqLQwGn72+NdaM8JLP0hl3hrhmu8ynfu594/4Y="]} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/CollectionUsage.StringCollection.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":["f/X3CiL32w/JZnMKwhreYA==","Gv3HLzJ5SyhH5ixVkIe1nQ=="]} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/CollectionUsage.StringEnumerable.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":["f/X3CiL32w/JZnMKwhreYA==","Gv3HLzJ5SyhH5ixVkIe1nQ=="]} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/CollectionUsage.StringList.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":["f/X3CiL32w/JZnMKwhreYA==","Gv3HLzJ5SyhH5ixVkIe1nQ=="]} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/CollectionUsage.WithConstructorEnumerableParameter.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":["mdOE0iozY6GFE6qDwzndYSxgX3MLcKArCk0Tt04dswE=","h3HvFQqLQwGn72+NdaM8JLP0hl3hrhmu8ynfu594/4Y="]} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/CollectionUsage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | 3 | [UsesVerify] 4 | public class CollectionUsage 5 | { 6 | [Fact] 7 | public async Task ByteArrayList() 8 | { 9 | var target = new ClassWithByteArrayList 10 | { 11 | Property = new() 12 | { 13 | new byte[] {2, 3}, 14 | new byte[] {5, 6} 15 | } 16 | }; 17 | var result = await RoundTrip.Run(target); 18 | Assert.Equal(new byte[] {5, 6}, result.Property?[1]); 19 | } 20 | 21 | public class ClassWithByteArrayList 22 | { 23 | [Encrypt] 24 | public List? Property { get; set; } 25 | } 26 | 27 | [Fact] 28 | public async Task StringList() 29 | { 30 | var target = new ClassWithStringList 31 | { 32 | Property = new() 33 | { 34 | "Value1", 35 | "Value2" 36 | } 37 | }; 38 | var result = await RoundTrip.Run(target); 39 | Assert.Equal("Value2", result.Property?[1]); 40 | } 41 | 42 | public class ClassWithStringList 43 | { 44 | [Encrypt] 45 | public List? Property { get; set; } 46 | } 47 | 48 | [Fact] 49 | public async Task StringCollection() 50 | { 51 | var target = new ClassWithStringCollection 52 | { 53 | Property = new List 54 | { 55 | "Value1", 56 | "Value2" 57 | } 58 | }; 59 | var result = await RoundTrip.Run(target); 60 | Assert.Equal("Value2", result.Property.Last()); 61 | } 62 | 63 | public class ClassWithStringCollection 64 | { 65 | [Encrypt] 66 | public ICollection? Property { get; set; } 67 | } 68 | 69 | [Fact] 70 | public async Task StringEnumerable() 71 | { 72 | var target = new ClassWithStringEnumerable 73 | { 74 | Property = new List 75 | { 76 | "Value1", 77 | "Value2" 78 | } 79 | }; 80 | var result = await RoundTrip.Run(target); 81 | Assert.Equal("Value2", result.Property.Last()); 82 | } 83 | 84 | public class ClassWithStringEnumerable 85 | { 86 | [Encrypt] 87 | public IEnumerable? Property { get; set; } 88 | } 89 | 90 | [Fact] 91 | public async Task GuidEnumerable() 92 | { 93 | var target = new ClassWithGuidEnumerable 94 | { 95 | Property = new List 96 | { 97 | new("45b14050-065c-4be7-8bb8-f3b46b8d94e6"), 98 | new("74b69ad1-f9e8-4549-8524-cce4a8b4c38b") 99 | } 100 | }; 101 | var result = await RoundTrip.Run(target); 102 | Assert.Equal("74b69ad1-f9e8-4549-8524-cce4a8b4c38b", result.Property.Last().ToString()); 103 | } 104 | 105 | public class ClassWithGuidEnumerable 106 | { 107 | [Encrypt] 108 | public IEnumerable? Property { get; set; } 109 | } 110 | 111 | [Fact] 112 | public async Task WithConstructorEnumerableParameter() 113 | { 114 | var target = new ClassWithConstructorEnumerableParameter( 115 | new List 116 | { 117 | new("45b14050-065c-4be7-8bb8-f3b46b8d94e6"), 118 | new("74b69ad1-f9e8-4549-8524-cce4a8b4c38b") 119 | }); 120 | 121 | var result = await RoundTrip.Run(target); 122 | Assert.Equal("74b69ad1-f9e8-4549-8524-cce4a8b4c38b", result.Property.Last().ToString()); 123 | } 124 | 125 | public class ClassWithConstructorEnumerableParameter 126 | { 127 | [Encrypt] 128 | public IEnumerable? Property { get; set; } 129 | 130 | public ClassWithConstructorEnumerableParameter(IEnumerable property) => 131 | Property = property; 132 | } 133 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/CryptoBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | public static class CryptoBuilder 4 | { 5 | public static SymmetricAlgorithm Build() => 6 | new RijndaelManaged 7 | { 8 | IV = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 9 | Key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6") 10 | }; 11 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/DictionaryUsage.ByteArrayDictionary.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":{"Key1":"+4hZVXd5q/qJLcdnjgBFqA==","Key2":"IUY+HDFpyXyLu4XvdG37Sg=="}} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/DictionaryUsage.IntGuidDictionary.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":{"1":"mdOE0iozY6GFE6qDwzndYSxgX3MLcKArCk0Tt04dswE=","2":"h3HvFQqLQwGn72+NdaM8JLP0hl3hrhmu8ynfu594/4Y="}} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/DictionaryUsage.IntStringDictionary.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":{"1":"f/X3CiL32w/JZnMKwhreYA==","2":"Gv3HLzJ5SyhH5ixVkIe1nQ=="}} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/DictionaryUsage.StringDictionary.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":{"Key1":"f/X3CiL32w/JZnMKwhreYA==","Key2":"Gv3HLzJ5SyhH5ixVkIe1nQ=="}} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/DictionaryUsage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | 3 | [UsesVerify] 4 | public class DictionaryUsage 5 | { 6 | [Fact] 7 | public async Task ByteArrayDictionary() 8 | { 9 | var target = new ClassWithByteArrayDictionary 10 | { 11 | Property = new() 12 | { 13 | { 14 | "Key1", new byte[] {2, 3} 15 | }, 16 | { 17 | "Key2", new byte[] {5, 6} 18 | } 19 | } 20 | }; 21 | var result = await RoundTrip.Run(target); 22 | Assert.Equal(new byte[] {5, 6}, result.Property?["Key2"]); 23 | } 24 | 25 | public class ClassWithByteArrayDictionary 26 | { 27 | [Encrypt] 28 | public Dictionary? Property { get; set; } 29 | } 30 | 31 | [Fact] 32 | public async Task StringDictionary() 33 | { 34 | var target = new ClassWithStringDictionary 35 | { 36 | Property = new() 37 | { 38 | { 39 | "Key1", "Value1" 40 | }, 41 | { 42 | "Key2", "Value2" 43 | } 44 | } 45 | }; 46 | var result = await RoundTrip.Run(target); 47 | Assert.Equal("Value2", result.Property?["Key2"]); 48 | } 49 | 50 | public class ClassWithStringDictionary 51 | { 52 | [Encrypt] 53 | public Dictionary? Property { get; set; } 54 | } 55 | 56 | [Fact] 57 | public async Task IntStringDictionary() 58 | { 59 | var target = new ClassWithIntStringDictionary 60 | { 61 | Property = new() 62 | { 63 | { 64 | 1, "Value1" 65 | }, 66 | { 67 | 2, "Value2" 68 | } 69 | } 70 | }; 71 | var result = await RoundTrip.Run(target); 72 | Assert.Equal("Value2", result.Property?[2]); 73 | } 74 | 75 | public class ClassWithIntStringDictionary 76 | { 77 | [Encrypt] 78 | public Dictionary? Property { get; set; } 79 | } 80 | 81 | [Fact] 82 | public async Task IntGuidDictionary() 83 | { 84 | var target = new ClassWithIntGuidDictionary 85 | { 86 | Property = new() 87 | { 88 | { 89 | 1, new("45b14050-065c-4be7-8bb8-f3b46b8d94e6") 90 | }, 91 | { 92 | 2, new("74b69ad1-f9e8-4549-8524-cce4a8b4c38b") 93 | } 94 | } 95 | }; 96 | var result = await RoundTrip.Run(target); 97 | Assert.Equal("74b69ad1-f9e8-4549-8524-cce4a8b4c38b", result.Property?[2].ToString()); 98 | } 99 | 100 | public class ClassWithIntGuidDictionary 101 | { 102 | [Encrypt] 103 | public Dictionary? Property { get; set; } 104 | } 105 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/EncryptionFactoryTest.ExampleUsage.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Property1: Property1Value, 3 | Property2: Property2Value 4 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/EncryptionFactoryTest.RoundTrip.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Property1: Property1Value, 3 | Property2: Property2Value 4 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/EncryptionFactoryTest.Simple.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property1":"d3uvofNWnG5Md5UnOBCoyA==","Property2":"VTIddiommXSZf9mK5EL1oQ=="} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/EncryptionFactoryTest.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Encryption; 4 | 5 | [UsesVerify] 6 | public class EncryptionFactoryTest 7 | { 8 | [Fact] 9 | public async Task ExampleUsage() 10 | { 11 | // per system (periodically rotated) 12 | var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6"); 13 | 14 | // per app domain 15 | using var factory = new EncryptionFactory(); 16 | var serializer = new JsonSerializer 17 | { 18 | ContractResolver = factory.GetContractResolver() 19 | }; 20 | 21 | // transferred as meta data with the serialized payload 22 | byte[] initVector; 23 | 24 | string serialized; 25 | 26 | // per serialize session 27 | using (var algorithm = new RijndaelManaged 28 | { 29 | Key = key 30 | }) 31 | { 32 | initVector = algorithm.IV; 33 | using (factory.GetEncryptSession(algorithm)) 34 | { 35 | var instance = new ClassToSerialize 36 | { 37 | Property1 = "Property1Value", 38 | Property2 = "Property2Value" 39 | }; 40 | serialized = serializer.Serialize(instance); 41 | } 42 | } 43 | 44 | // per deserialize session 45 | using (var algorithm = new RijndaelManaged 46 | { 47 | IV = initVector, 48 | Key = key 49 | }) 50 | { 51 | using (factory.GetDecryptSession(algorithm)) 52 | { 53 | var deserialized = serializer.Deserialize(serialized); 54 | await Verify(deserialized); 55 | } 56 | } 57 | } 58 | 59 | [Fact] 60 | public async Task Simple() 61 | { 62 | var factory = new EncryptionFactory(); 63 | var serializer = new JsonSerializer 64 | { 65 | ContractResolver = factory.GetContractResolver() 66 | }; 67 | using var algorithm = CryptoBuilder.Build(); 68 | using (factory.GetEncryptSession(algorithm)) 69 | { 70 | var instance = new ClassToSerialize 71 | { 72 | Property1 = "Property1Value", 73 | Property2 = "Property2Value" 74 | }; 75 | var result = serializer.Serialize(instance); 76 | await Verify(result); 77 | } 78 | } 79 | 80 | [Fact] 81 | public async Task RoundTrip() 82 | { 83 | var factory = new EncryptionFactory(); 84 | var serializer = new JsonSerializer 85 | { 86 | ContractResolver = factory.GetContractResolver() 87 | }; 88 | using var algorithm = CryptoBuilder.Build(); 89 | string serialized; 90 | using (factory.GetEncryptSession(algorithm)) 91 | { 92 | var instance = new ClassToSerialize 93 | { 94 | Property1 = "Property1Value", 95 | Property2 = "Property2Value" 96 | }; 97 | serialized = serializer.Serialize(instance); 98 | } 99 | using (factory.GetDecryptSession(algorithm)) 100 | { 101 | var result = serializer.Deserialize(serialized); 102 | await Verify(result); 103 | } 104 | } 105 | 106 | public class ClassToSerialize 107 | { 108 | [Encrypt] 109 | public string? Property1 { get; set; } 110 | 111 | [Encrypt] 112 | public string? Property2 { get; set; } 113 | } 114 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/Extensions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | public static class Extensions 4 | { 5 | public static string ReverseToString(this string value) => 6 | new(value.Reverse().ToArray()); 7 | 8 | public static T Deserialize(this JsonSerializer serializer, string value) 9 | { 10 | using var reader = new StringReader(value); 11 | using var jsonReader = new JsonTextReader(reader); 12 | return serializer.Deserialize(jsonReader)!; 13 | } 14 | 15 | public static string Serialize(this JsonSerializer serializer, T target) 16 | { 17 | var builder = new StringBuilder(); 18 | using (var writer = new StringWriter(builder)) 19 | { 20 | serializer.Serialize(writer, target); 21 | } 22 | return builder.ToString(); 23 | } 24 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/Newtonsoft.Json.Encryption.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/NodeProp.Node.verified.txt: -------------------------------------------------------------------------------- 1 | {"SubProperty":"fkGChGSMmB8fG1nlxVB7pa3un5RzD9vljM82o2FulzkZacVjeyrwEL69O4kmcj6bHT3VJ2Z7W2vKUA8HfT+e+Q=="} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/NodeProp.NodeWithConverter.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":"bl15Dqv2x2yEMNxVB+ISpw=="} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/NodeProp.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Encryption; 3 | 4 | [UsesVerify] 5 | public class NodeProp 6 | { 7 | [Fact] 8 | public async Task Node() 9 | { 10 | var target = new TargetClass 11 | { 12 | SubProperty = new() 13 | { 14 | Property1 = "PropertyValue1", 15 | Property2 = "PropertyValue2" 16 | } 17 | }; 18 | var result = await RoundTrip.Run(target); 19 | Assert.Equal("PropertyValue1", result.SubProperty?.Property1); 20 | } 21 | 22 | public class TargetClass 23 | { 24 | [NodeEncrypt] 25 | public SubClass? SubProperty { get; set; } 26 | } 27 | 28 | public class SubClass 29 | { 30 | public string? Property1 { get; set; } 31 | public string? Property2 { get; set; } 32 | } 33 | 34 | [Fact] 35 | public async Task NodeWithConverter() 36 | { 37 | var target = new WithConverterTargetClass 38 | { 39 | Property = "AbCd" 40 | }; 41 | var result = await RoundTrip.Run(target); 42 | Assert.Equal("AbCd", result.Property); 43 | ReverseConverter.AssertReadWriteCalled(); 44 | } 45 | 46 | public class WithConverterTargetClass 47 | { 48 | [NodeEncrypt] 49 | [JsonConverter(typeof(ReverseConverter))] 50 | public string? Property { get; set; } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/ReverseConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | public class ReverseConverter : 4 | JsonConverter 5 | { 6 | public static bool WriteCalled; 7 | public static bool ReadCalled; 8 | 9 | public static void AssertReadWriteCalled() 10 | { 11 | Assert.True(WriteCalled); 12 | Assert.True(ReadCalled); 13 | } 14 | 15 | public ReverseConverter() 16 | { 17 | WriteCalled = false; 18 | ReadCalled = false; 19 | } 20 | 21 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 22 | { 23 | WriteCalled = true; 24 | if (value is string stringValue) 25 | { 26 | writer.WriteValue(stringValue.ReverseToString()); 27 | return; 28 | } 29 | if (value is Guid guidValue) 30 | { 31 | writer.WriteValue(guidValue.ToString().ReverseToString()); 32 | return; 33 | } 34 | if (value is byte[] byteValue) 35 | { 36 | writer.WriteValue(byteValue.Reverse().ToArray()); 37 | return; 38 | } 39 | } 40 | 41 | public override object? ReadJson(JsonReader reader, Type type, object? existingValue, JsonSerializer serializer) 42 | { 43 | ReadCalled = true; 44 | if (type == typeof(string)) 45 | { 46 | var stringValue = reader.ReadAsString(); 47 | return stringValue!.ReverseToString(); 48 | } 49 | if (type == typeof(Guid)) 50 | { 51 | var stringValue = reader.ReadAsString(); 52 | return new Guid(stringValue!.ReverseToString()); 53 | } 54 | if (type == typeof(byte[])) 55 | { 56 | var bytes = reader.ReadAsBytes(); 57 | return bytes.Reverse().ToArray(); 58 | } 59 | return null; 60 | } 61 | 62 | public override bool CanConvert(Type objectType) => 63 | objectType == typeof(string) || 64 | objectType == typeof(byte[]) || 65 | objectType == typeof(Guid); 66 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/RoundTrip.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Encryption; 3 | 4 | public static class RoundTrip 5 | { 6 | public static async Task Run(T instance, [CallerFilePath] string sourceFile = "") 7 | { 8 | using var factory = new EncryptionFactory(); 9 | using var algorithm = CryptoBuilder.Build(); 10 | var serializer = new JsonSerializer 11 | { 12 | ContractResolver = factory.GetContractResolver() 13 | }; 14 | 15 | string result; 16 | using (factory.GetEncryptSession(algorithm)) 17 | { 18 | result = serializer.Serialize(instance); 19 | } 20 | 21 | await Verify(result, null, sourceFile); 22 | using (factory.GetDecryptSession(algorithm)) 23 | { 24 | return serializer.Deserialize(result); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/SimpleProp.ByteArray.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":"+4hZVXd5q/qJLcdnjgBFqA=="} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/SimpleProp.EmptyString.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":"RuDCF9bDLRsgUFxg/d5kng=="} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/SimpleProp.Guid.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":"mdOE0iozY6GFE6qDwzndYSxgX3MLcKArCk0Tt04dswE="} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/SimpleProp.NullString.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":null} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/SimpleProp.String.verified.txt: -------------------------------------------------------------------------------- 1 | {"Property":"wSayABpFI3g7a/D6gGTq5g=="} -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/SimpleProp.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | 3 | [UsesVerify] 4 | public class SimpleProp 5 | { 6 | [Fact] 7 | public async Task ByteArray() 8 | { 9 | var target = new ClassWithByteArray 10 | { 11 | Property = new byte[]{2,3} 12 | }; 13 | var result = await RoundTrip.Run(target); 14 | Assert.Equal(new byte[] { 2, 3 }, result.Property); 15 | } 16 | 17 | public class ClassWithByteArray 18 | { 19 | [Encrypt] 20 | public byte[]? Property { get; set; } 21 | } 22 | 23 | [Fact] 24 | public async Task NullString() 25 | { 26 | var target = new ClassWithString(); 27 | var result = await RoundTrip.Run(target); 28 | Assert.Null(result.Property); 29 | } 30 | 31 | [Fact] 32 | public async Task String() 33 | { 34 | var target = new ClassWithString 35 | { 36 | Property = "Foo" 37 | }; 38 | var result = await RoundTrip.Run(target); 39 | Assert.Equal("Foo", result.Property); 40 | } 41 | 42 | [Fact] 43 | public async Task EmptyString() 44 | { 45 | var target = new ClassWithString 46 | { 47 | Property = string.Empty 48 | }; 49 | var result = await RoundTrip.Run(target); 50 | Assert.Empty(result.Property!); 51 | } 52 | 53 | public class ClassWithString 54 | { 55 | [Encrypt] 56 | public string? Property { get; set; } 57 | } 58 | 59 | [Fact] 60 | public async Task Guid() 61 | { 62 | var target = new ClassWithGuid 63 | { 64 | Property = new("45b14050-065c-4be7-8bb8-f3b46b8d94e6") 65 | }; 66 | var result = await RoundTrip.Run(target); 67 | Assert.Equal("45b14050-065c-4be7-8bb8-f3b46b8d94e6", result.Property.ToString()); 68 | } 69 | 70 | public class ClassWithGuid 71 | { 72 | [Encrypt] 73 | public Guid Property { get; set; } 74 | } 75 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/Snippets/Snippets.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Encryption; 4 | 5 | class Snippets 6 | { 7 | void Workflow() 8 | { 9 | #region Workflow 10 | 11 | // per system (periodically rotated) 12 | var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6"); 13 | 14 | // per app domain 15 | using var factory = new EncryptionFactory(); 16 | var serializer = new JsonSerializer 17 | { 18 | ContractResolver = factory.GetContractResolver() 19 | }; 20 | 21 | // transferred as meta data with the serialized payload 22 | byte[] initVector; 23 | 24 | string serialized; 25 | 26 | #region serialize 27 | // per serialize session 28 | using (var algorithm = new RijndaelManaged 29 | { 30 | Key = key 31 | }) 32 | { 33 | //TODO: store initVector for use in deserialization 34 | initVector = algorithm.IV; 35 | using (factory.GetEncryptSession(algorithm)) 36 | { 37 | var instance = new ClassToSerialize 38 | { 39 | Property = "PropertyValue", 40 | }; 41 | var builder = new StringBuilder(); 42 | using (var writer = new StringWriter(builder)) 43 | { 44 | serializer.Serialize(writer, instance); 45 | } 46 | 47 | serialized = builder.ToString(); 48 | } 49 | } 50 | #endregion 51 | 52 | #region deserialize 53 | // per deserialize session 54 | using (var algorithm = new RijndaelManaged 55 | { 56 | IV = initVector, 57 | Key = key 58 | }) 59 | { 60 | using (factory.GetDecryptSession(algorithm)) 61 | { 62 | using var stringReader = new StringReader(serialized); 63 | using var jsonReader = new JsonTextReader(stringReader); 64 | var deserialized = serializer.Deserialize(jsonReader); 65 | Console.WriteLine(deserialized!.Property); 66 | } 67 | } 68 | 69 | #endregion 70 | #endregion 71 | } 72 | 73 | class ClassToSerialize 74 | { 75 | public string Property { get; set; } = null!; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.Tests/TestBase.cs: -------------------------------------------------------------------------------- 1 | public static class ModuleInitializer 2 | { 3 | public static void Initialize() => 4 | FixCurrentDirectory(); 5 | 6 | static void FixCurrentDirectory([CallerFilePath] string callerFilePath = "") => 7 | Environment.CurrentDirectory = Directory.GetParent(callerFilePath).FullName; 8 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 16 3 | VisualStudioVersion = 16.0.29201.188 4 | MinimumVisualStudioVersion = 16.0.29201.188 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Newtonsoft.Json.Encryption", "Newtonsoft.Json.Encryption\Newtonsoft.Json.Encryption.csproj", "{72A1D8DC-FF19-40E0-866C-9CFD7EE4541D}" 6 | EndProject 7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Newtonsoft.Json.Encryption.Tests", "Newtonsoft.Json.Encryption.Tests\Newtonsoft.Json.Encryption.Tests.csproj", "{D59EEA3C-ADFD-4595-B7A3-D6412AC9867E}" 8 | EndProject 9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "Sample\Sample.csproj", "{C87059BB-F570-4E83-ACAD-6B8094CAEDE9}" 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NServiceBus.Newtonsoft.Encryption", "NServiceBus.Newtonsoft.Encryption\NServiceBus.Newtonsoft.Encryption.csproj", "{43478CA6-9B03-41B6-8A05-783C65634909}" 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rebus.Newtonsoft.Encryption", "Rebus.Newtonsoft.Encryption\Rebus.Newtonsoft.Encryption.csproj", "{F06AF050-ED5D-4665-B7C8-984FBEAE28A7}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RebusSample", "RebusSample\RebusSample.csproj", "{A07A5756-BA9B-479F-8369-BB2B73C00398}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NServiceBusSample", "NServiceBusSample\NServiceBusSample.csproj", "{679404A8-33EE-4283-8D7E-3734D8D85BBE}" 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NServiceBus.Newtonsoft.Encryption.Tests", "NServiceBus.Newtonsoft.Encryption.Tests\NServiceBus.Newtonsoft.Encryption.Tests.csproj", "{C21456CE-1F0C-47F1-B5C7-1F905B30A412}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rebus.Newtonsoft.Encryption.Tests", "Rebus.Newtonsoft.Encryption.Tests\Rebus.Newtonsoft.Encryption.Tests.csproj", "{D425FEF0-F2C2-45F0-87E3-7CC88E466D1D}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{D0FD6F65-7127-4D2C-B15E-B4F550D2CB81}" 24 | EndProject 25 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2745D9FA-8583-434A-87D8-9A8BC11F5B0B}" 26 | ProjectSection(SolutionItems) = preProject 27 | Directory.Build.props = Directory.Build.props 28 | EndProjectSection 29 | EndProject 30 | Global 31 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 32 | Debug|Any CPU = Debug|Any CPU 33 | Release|Any CPU = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 36 | {72A1D8DC-FF19-40E0-866C-9CFD7EE4541D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {72A1D8DC-FF19-40E0-866C-9CFD7EE4541D}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {72A1D8DC-FF19-40E0-866C-9CFD7EE4541D}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {72A1D8DC-FF19-40E0-866C-9CFD7EE4541D}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {D59EEA3C-ADFD-4595-B7A3-D6412AC9867E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {D59EEA3C-ADFD-4595-B7A3-D6412AC9867E}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {D59EEA3C-ADFD-4595-B7A3-D6412AC9867E}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {D59EEA3C-ADFD-4595-B7A3-D6412AC9867E}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {C87059BB-F570-4E83-ACAD-6B8094CAEDE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {C87059BB-F570-4E83-ACAD-6B8094CAEDE9}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {C87059BB-F570-4E83-ACAD-6B8094CAEDE9}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {C87059BB-F570-4E83-ACAD-6B8094CAEDE9}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {43478CA6-9B03-41B6-8A05-783C65634909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {43478CA6-9B03-41B6-8A05-783C65634909}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {43478CA6-9B03-41B6-8A05-783C65634909}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {43478CA6-9B03-41B6-8A05-783C65634909}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {F06AF050-ED5D-4665-B7C8-984FBEAE28A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {F06AF050-ED5D-4665-B7C8-984FBEAE28A7}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {F06AF050-ED5D-4665-B7C8-984FBEAE28A7}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {F06AF050-ED5D-4665-B7C8-984FBEAE28A7}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {A07A5756-BA9B-479F-8369-BB2B73C00398}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {A07A5756-BA9B-479F-8369-BB2B73C00398}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {A07A5756-BA9B-479F-8369-BB2B73C00398}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {A07A5756-BA9B-479F-8369-BB2B73C00398}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {679404A8-33EE-4283-8D7E-3734D8D85BBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {679404A8-33EE-4283-8D7E-3734D8D85BBE}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {679404A8-33EE-4283-8D7E-3734D8D85BBE}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {679404A8-33EE-4283-8D7E-3734D8D85BBE}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {C21456CE-1F0C-47F1-B5C7-1F905B30A412}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {C21456CE-1F0C-47F1-B5C7-1F905B30A412}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {C21456CE-1F0C-47F1-B5C7-1F905B30A412}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {C21456CE-1F0C-47F1-B5C7-1F905B30A412}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {D425FEF0-F2C2-45F0-87E3-7CC88E466D1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {D425FEF0-F2C2-45F0-87E3-7CC88E466D1D}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {D425FEF0-F2C2-45F0-87E3-7CC88E466D1D}.Release|Any CPU.ActiveCfg = Release|Any CPU 71 | {D425FEF0-F2C2-45F0-87E3-7CC88E466D1D}.Release|Any CPU.Build.0 = Release|Any CPU 72 | {D0FD6F65-7127-4D2C-B15E-B4F550D2CB81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 73 | {D0FD6F65-7127-4D2C-B15E-B4F550D2CB81}.Debug|Any CPU.Build.0 = Debug|Any CPU 74 | {D0FD6F65-7127-4D2C-B15E-B4F550D2CB81}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {D0FD6F65-7127-4D2C-B15E-B4F550D2CB81}.Release|Any CPU.Build.0 = Release|Any CPU 76 | EndGlobalSection 77 | GlobalSection(SolutionProperties) = preSolution 78 | HideSolutionNode = FALSE 79 | EndGlobalSection 80 | GlobalSection(ExtensibilityGlobals) = postSolution 81 | SolutionGuid = {A9DBE1FD-2601-4F67-B85B-CE26A668FA05} 82 | EndGlobalSection 83 | EndGlobal 84 | -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | DO_NOT_SHOW 4 | *.received.* 5 | *.verified.* 6 | WARNING 7 | <?xml version="1.0" encoding="utf-16"?><Profile name="c# Cleanup"><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><CSCodeStyleAttributes ArrangeVarStyle="True" ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeCodeBodyStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" ArrangeNamespaces="True" /><CssAlphabetizeProperties>True</CssAlphabetizeProperties><JSStringLiteralQuotesDescriptor>True</JSStringLiteralQuotesDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><JsInsertSemicolon>True</JsInsertSemicolon><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs><OptimizeReferenceCommentsTs>True</OptimizeReferenceCommentsTs><PublicModifierStyleTs>True</PublicModifierStyleTs><ExplicitAnyTs>True</ExplicitAnyTs><TypeAnnotationStyleTs>True</TypeAnnotationStyleTs><RelativePathStyleTs>True</RelativePathStyleTs><AsInsteadOfCastTs>True</AsInsteadOfCastTs><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CssReformatCode>True</CssReformatCode><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><HtmlReformatCode>True</HtmlReformatCode><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><IDEA_SETTINGS>&lt;profile version="1.0"&gt; 8 | &lt;option name="myName" value="c# Cleanup" /&gt; 9 | &lt;/profile&gt;</IDEA_SETTINGS><RIDER_SETTINGS>&lt;profile&gt; 10 | &lt;Language id="EditorConfig"&gt; 11 | &lt;Reformat&gt;false&lt;/Reformat&gt; 12 | &lt;/Language&gt; 13 | &lt;Language id="HTML"&gt; 14 | &lt;OptimizeImports&gt;false&lt;/OptimizeImports&gt; 15 | &lt;Reformat&gt;false&lt;/Reformat&gt; 16 | &lt;Rearrange&gt;false&lt;/Rearrange&gt; 17 | &lt;/Language&gt; 18 | &lt;Language id="JSON"&gt; 19 | &lt;Reformat&gt;false&lt;/Reformat&gt; 20 | &lt;/Language&gt; 21 | &lt;Language id="RELAX-NG"&gt; 22 | &lt;Reformat&gt;false&lt;/Reformat&gt; 23 | &lt;/Language&gt; 24 | &lt;Language id="XML"&gt; 25 | &lt;OptimizeImports&gt;false&lt;/OptimizeImports&gt; 26 | &lt;Reformat&gt;false&lt;/Reformat&gt; 27 | &lt;Rearrange&gt;false&lt;/Rearrange&gt; 28 | &lt;/Language&gt; 29 | &lt;/profile&gt;</RIDER_SETTINGS></Profile> 30 | NEVER 31 | NEVER 32 | 33 | 34 | False 35 | False 36 | False 37 | False 38 | RemoveIndent 39 | RemoveIndent 40 | False 41 | True 42 | True 43 | True 44 | True -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/ContractResolver.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Serialization; 2 | 3 | namespace Newtonsoft.Json.Encryption; 4 | 5 | public class ContractResolver : 6 | DefaultContractResolver 7 | { 8 | Encrypter encrypter; 9 | 10 | public ContractResolver(Encrypter encrypter) => 11 | this.encrypter = encrypter; 12 | 13 | protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 14 | { 15 | var property = base.CreateProperty(member, memberSerialization); 16 | JsonPropertyHelper.Manipulate(member, encrypter, property); 17 | return property; 18 | } 19 | 20 | protected override JsonProperty CreatePropertyFromConstructorParameter(JsonProperty? matchingMemberProperty, ParameterInfo parameterInfo) 21 | { 22 | var property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo); 23 | if (matchingMemberProperty != null) 24 | { 25 | property.ItemConverter = matchingMemberProperty.ItemConverter; 26 | } 27 | return property; 28 | } 29 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/Converters/ByteArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Encryption; 3 | 4 | class ByteArrayConverter : 5 | JsonConverter 6 | { 7 | Encrypter encrypter; 8 | 9 | public ByteArrayConverter(Encrypter encrypter) => 10 | this.encrypter = encrypter; 11 | 12 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 13 | { 14 | var bytes = (byte[]) value!; 15 | var base64String = encrypter.EncryptBytesToString(bytes); 16 | writer.WriteValue(base64String); 17 | } 18 | 19 | public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) 20 | { 21 | var value = (string) reader.Value!; 22 | return encrypter.DecryptBytesFromString(value); 23 | } 24 | 25 | public override bool CanConvert(Type objectType) => 26 | throw new NotImplementedException(); 27 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/Converters/GuidConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Encryption; 3 | 4 | class GuidConverter : 5 | JsonConverter 6 | { 7 | Encrypter encrypter; 8 | 9 | public GuidConverter(Encrypter encrypter) => 10 | this.encrypter = encrypter; 11 | 12 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 13 | { 14 | var guid = (Guid) value!; 15 | var base64String = encrypter.EncryptGuidToString(guid); 16 | writer.WriteValue(base64String); 17 | } 18 | 19 | 20 | public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) 21 | { 22 | var value = (string)reader.Value!; 23 | return encrypter.DecryptGuidFromString(value); 24 | } 25 | 26 | public override bool CanConvert(Type objectType) => 27 | throw new NotImplementedException(); 28 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/Converters/NodeConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Encryption; 3 | 4 | class NodeConverter : 5 | JsonConverter 6 | { 7 | Encrypter encrypter; 8 | 9 | public NodeConverter(Encrypter encrypter) => 10 | this.encrypter = encrypter; 11 | 12 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 13 | { 14 | var target = serializer.Serialize(value!); 15 | var encrypted = encrypter.Encrypt(target); 16 | writer.WriteValue(encrypted); 17 | } 18 | 19 | public override object? ReadJson(JsonReader reader, Type type, object? existingValue, JsonSerializer serializer) 20 | { 21 | var value = (string) reader.Value!; 22 | var decrypted = encrypter.Decrypt(value); 23 | return serializer.Deserialize(type, decrypted); 24 | } 25 | 26 | public override bool CanConvert(Type objectType) => 27 | throw new NotImplementedException(); 28 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/Converters/StringConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Encryption; 3 | 4 | class StringConverter : 5 | JsonConverter 6 | { 7 | Encrypter encrypter; 8 | 9 | public StringConverter(Encrypter encrypter) => 10 | this.encrypter = encrypter; 11 | 12 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 13 | { 14 | var encrypted = encrypter.Encrypt((string?) value); 15 | writer.WriteValue(encrypted); 16 | } 17 | 18 | public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) 19 | { 20 | var value = (string) reader.Value!; 21 | return encrypter.Decrypt(value); 22 | } 23 | 24 | public override bool CanConvert(Type objectType) => 25 | throw new NotImplementedException(); 26 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/Converters/WrappedNodeConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Encryption; 3 | 4 | class WrappedNodeConverter : 5 | JsonConverter 6 | { 7 | Encrypter encrypter; 8 | JsonConverter innerConverter; 9 | 10 | public WrappedNodeConverter(Encrypter encrypter, JsonConverter innerConverter) 11 | { 12 | this.encrypter = encrypter; 13 | this.innerConverter = innerConverter; 14 | } 15 | 16 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 17 | { 18 | var target = innerConverter.Serialize(value!, serializer); 19 | var encrypted = encrypter.Encrypt(target); 20 | writer.WriteValue(encrypted); 21 | } 22 | 23 | public override object? ReadJson(JsonReader reader, Type type, object? existingValue, JsonSerializer serializer) 24 | { 25 | var value = (string) reader.Value!; 26 | var decrypted = encrypter.Decrypt(value); 27 | if (decrypted == null) 28 | { 29 | return null; 30 | } 31 | return innerConverter.Deserialize(type, serializer, decrypted, existingValue); 32 | } 33 | 34 | public override bool CanConvert(Type objectType) => 35 | throw new NotImplementedException(); 36 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/CryptoState.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | class CryptoState : 4 | IDisposable 5 | { 6 | public CryptoState(Func encryptProvider) 7 | { 8 | Provider = () => 9 | { 10 | if (transform == null) 11 | { 12 | return transform = encryptProvider(); 13 | } 14 | return transform; 15 | }; 16 | Cleanup = transform => 17 | { 18 | if (!transform.CanReuseTransform) 19 | { 20 | this.transform?.Dispose(); 21 | this.transform = null; 22 | } 23 | }; 24 | } 25 | 26 | ICryptoTransform? transform; 27 | public readonly Func Provider; 28 | public readonly Action Cleanup; 29 | 30 | public void Dispose() => 31 | transform?.Dispose(); 32 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/EncryptAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Newtonsoft.Json.Encryption; 2 | 3 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 4 | public class EncryptAttribute : 5 | Attribute 6 | { 7 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/Encrypter.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace Newtonsoft.Json.Encryption; 4 | 5 | public class Encrypter 6 | { 7 | Func encryptProvider; 8 | Func decryptProvider; 9 | Action encryptCleanup; 10 | Action decryptCleanup; 11 | 12 | public Encrypter( 13 | Func encryptProvider, 14 | Func decryptProvider, 15 | Action encryptCleanup, 16 | Action decryptCleanup) 17 | { 18 | this.encryptProvider = encryptProvider; 19 | this.decryptProvider = decryptProvider; 20 | this.encryptCleanup = encryptCleanup; 21 | this.decryptCleanup = decryptCleanup; 22 | } 23 | 24 | public string? Encrypt(string? target) 25 | { 26 | if (target == null) 27 | { 28 | return null; 29 | } 30 | var encryptor = encryptProvider(); 31 | try 32 | { 33 | using var memoryStream = new MemoryStream(); 34 | using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) 35 | { 36 | using var writer = new StreamWriter(cryptoStream); 37 | writer.Write(target); 38 | } 39 | 40 | return Convert.ToBase64String(memoryStream.ToArray()); 41 | } 42 | finally 43 | { 44 | encryptCleanup(encryptor); 45 | } 46 | } 47 | 48 | public string? Decrypt(string? value) 49 | { 50 | if (value == null) 51 | { 52 | return null; 53 | } 54 | var decryptor = decryptProvider(); 55 | var encrypted = Convert.FromBase64String(value); 56 | try 57 | { 58 | using var memoryStream = new MemoryStream(encrypted); 59 | using var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read); 60 | using var reader = new StreamReader(cryptoStream); 61 | return reader.ReadToEnd(); 62 | } 63 | finally 64 | { 65 | decryptCleanup(decryptor); 66 | } 67 | } 68 | 69 | public byte[] EncryptBytes(byte[] target) 70 | { 71 | var encryptor = encryptProvider(); 72 | try 73 | { 74 | return PerformCryptography(encryptor, target); 75 | } 76 | finally 77 | { 78 | encryptCleanup(encryptor); 79 | } 80 | } 81 | 82 | public byte[] DecryptBytes(byte[] value) 83 | { 84 | var decryptor = decryptProvider(); 85 | 86 | try 87 | { 88 | return PerformCryptography(decryptor, value); 89 | } 90 | finally 91 | { 92 | decryptCleanup(decryptor); 93 | } 94 | } 95 | 96 | static byte[] PerformCryptography(ICryptoTransform cryptoTransform, byte[] data) 97 | { 98 | using var memoryStream = new MemoryStream(); 99 | using (var cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write)) 100 | { 101 | cryptoStream.Write(data, 0, data.Length); 102 | } 103 | return memoryStream.ToArray(); 104 | } 105 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/EncryptionFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace Newtonsoft.Json.Encryption; 4 | 5 | public class EncryptionFactory : 6 | IDisposable 7 | { 8 | AsyncLocal encrypt = new(); 9 | AsyncLocal decrypt = new(); 10 | 11 | public IDisposable GetEncryptSession(SymmetricAlgorithm algorithm) => 12 | encrypt.Value = new(algorithm.CreateEncryptor); 13 | 14 | public IDisposable GetDecryptSession(SymmetricAlgorithm algorithm) => 15 | decrypt.Value = new(algorithm.CreateDecryptor); 16 | 17 | public ContractResolver GetContractResolver() => 18 | new( 19 | encrypter: new( 20 | encryptProvider: () => encrypt.Value.Provider(), 21 | decryptProvider: () => decrypt.Value.Provider(), 22 | encryptCleanup: transform => 23 | { 24 | encrypt.Value.Cleanup(transform); 25 | }, 26 | decryptCleanup: transform => 27 | { 28 | decrypt.Value.Cleanup(transform); 29 | }) 30 | ); 31 | 32 | public void Dispose() 33 | { 34 | encrypt.Value?.Dispose(); 35 | decrypt.Value?.Dispose(); 36 | } 37 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/Extensions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Encryption; 3 | 4 | static class Extensions 5 | { 6 | public static Guid DecryptGuidFromString(this Encrypter encrypter, string value) 7 | { 8 | var fromBase64String = Convert.FromBase64String(value); 9 | var decryptBytes = encrypter.DecryptBytes(fromBase64String); 10 | return new(decryptBytes); 11 | } 12 | 13 | public static string EncryptGuidToString(this Encrypter encrypter, Guid guid) 14 | { 15 | var byteArray = guid.ToByteArray(); 16 | var encryptBytes = encrypter.EncryptBytes(byteArray); 17 | return Convert.ToBase64String(encryptBytes); 18 | } 19 | 20 | public static byte[] DecryptBytesFromString(this Encrypter encrypter, string value) 21 | { 22 | var fromBase64String = Convert.FromBase64String(value); 23 | return encrypter.DecryptBytes(fromBase64String); 24 | } 25 | 26 | public static string EncryptBytesToString(this Encrypter encrypter, byte[] bytes) 27 | { 28 | var encryptBytes = encrypter.EncryptBytes(bytes); 29 | return Convert.ToBase64String(encryptBytes); 30 | } 31 | 32 | public static string Serialize(this JsonConverter converter, object value, JsonSerializer serializer) 33 | { 34 | var builder = new StringBuilder(); 35 | using (var stringWriter = new StringWriter(builder)) 36 | { 37 | using var textWriter = new JsonTextWriter(stringWriter); 38 | converter.WriteJson(textWriter, value, serializer); 39 | } 40 | 41 | return builder.ToString(); 42 | } 43 | 44 | public static object? Deserialize(this JsonConverter converter, Type type, JsonSerializer serializer, string decrypted, object? existingValue) 45 | { 46 | using var stringReader = new StringReader(decrypted); 47 | using var textReader = new JsonTextReader(stringReader); 48 | return converter.ReadJson(textReader, type, existingValue, serializer); 49 | } 50 | 51 | public static string? Serialize(this JsonSerializer serializer, object value) 52 | { 53 | var builder = new StringBuilder(); 54 | using (var writer = new StringWriter(builder)) 55 | { 56 | serializer.Serialize(writer, value); 57 | } 58 | return builder.ToString(); 59 | } 60 | 61 | public static object? Deserialize(this JsonSerializer serializer, Type type, string? value) 62 | { 63 | using var reader = new StringReader(value); 64 | return serializer.Deserialize(reader, type); 65 | } 66 | 67 | public static Type GetUnderlyingType(this MemberInfo member) 68 | { 69 | if (member is FieldInfo field) 70 | { 71 | return field.FieldType; 72 | } 73 | if (member is PropertyInfo property) 74 | { 75 | return property.PropertyType; 76 | } 77 | throw new ArgumentException 78 | ( 79 | "Input MemberInfo must be if type FieldInfo or PropertyInfo" 80 | ); 81 | } 82 | 83 | public static object GetValue(this MemberInfo member, object instance) 84 | { 85 | if (member is FieldInfo fieldInfo) 86 | { 87 | return fieldInfo.GetValue(instance); 88 | } 89 | if (member is PropertyInfo propertyInfo) 90 | { 91 | return propertyInfo.GetValue(instance, null); 92 | } 93 | throw new ArgumentException 94 | ( 95 | "Input MemberInfo must be if type FieldInfo or PropertyInfo" 96 | ); 97 | } 98 | 99 | public static void SetValue(this MemberInfo member, object instance, object value) 100 | { 101 | if (member is FieldInfo fieldInfo) 102 | { 103 | fieldInfo.SetValue(instance, value); 104 | return; 105 | } 106 | if (member is PropertyInfo propertyInfo) 107 | { 108 | propertyInfo.SetValue(instance, value); 109 | return; 110 | } 111 | throw new ArgumentException 112 | ( 113 | "Input MemberInfo must be if type FieldInfo or PropertyInfo" 114 | ); 115 | } 116 | 117 | static TypeInfo stringEnumerable = typeof(IEnumerable).GetTypeInfo(); 118 | static TypeInfo guidEnumerable = typeof(IEnumerable).GetTypeInfo(); 119 | static TypeInfo byteArrayEnumerable = typeof(IEnumerable).GetTypeInfo(); 120 | 121 | public static bool IsStringDictionary(this Type type) => 122 | IsDictionary(type, typeof(string)); 123 | 124 | public static bool IsGuidDictionary(this Type type) => 125 | IsDictionary(type, typeof(Guid)); 126 | 127 | public static bool IsByteArrayDictionary(this Type type) => 128 | IsDictionary(type, typeof(byte[])); 129 | 130 | static bool IsDictionary(Type type, Type valueTypeToCheck) 131 | { 132 | var typeInfo = type.GetTypeInfo(); 133 | return typeInfo.ImplementedInterfaces 134 | .Any(x => IsDictionaryInterface(x, valueTypeToCheck)); 135 | } 136 | 137 | static bool IsDictionaryInterface(Type type, Type valueTypeToCheck) 138 | { 139 | var typeInfo = type.GetTypeInfo(); 140 | var isDict = typeInfo.IsGenericType && 141 | typeInfo.GetGenericTypeDefinition() == typeof(IDictionary<,>); 142 | if (!isDict) 143 | { 144 | return false; 145 | } 146 | var valueType = typeInfo.GenericTypeArguments[1]; 147 | return valueType == valueTypeToCheck; 148 | } 149 | 150 | public static bool IsStringEnumerable(this Type type) => 151 | stringEnumerable.IsAssignableFrom(type.GetTypeInfo()); 152 | 153 | public static bool IsGuidEnumerable(this Type type) => 154 | guidEnumerable.IsAssignableFrom(type.GetTypeInfo()); 155 | 156 | public static bool IsByteArrayEnumerable(this Type type) => 157 | byteArrayEnumerable.IsAssignableFrom(type.GetTypeInfo()); 158 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/InternalsVisibleTo.cs: -------------------------------------------------------------------------------- 1 | [assembly:InternalsVisibleTo("NServiceBus.Newtonsoft.Encryption, PublicKey=0024000004800000940000000602000000240000525341310004000001000100399FF95B7E58B6C6EC041E079D3A48C2F063EC6EF0818D7B34820C3879DD6FF542D6F2999616CEF4D4B67FB22AF6093E65BAD45411D1F407FAC97C78932A7AA801270C73EEA7BA1F3405B5589EA7E3183815FD41A8B569564E5D63D6FC25B39EBF30C2204E28F9E2D9E7654B1CD42384AB581DA9BD8D040308C692C4AA6290BD")] 2 | -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/JsonPropertyHelper.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | using Newtonsoft.Json.Serialization; 3 | 4 | static class JsonPropertyHelper 5 | { 6 | public static void Manipulate( 7 | MemberInfo member, 8 | Encrypter encrypter, 9 | JsonProperty property) 10 | { 11 | var containsEncryptAttribute = member.ContainsEncryptAttribute(); 12 | var containsNodeEncryptAttribute = member.ContainsNodeEncryptAttribute(); 13 | if (!containsEncryptAttribute && !containsNodeEncryptAttribute) 14 | { 15 | return; 16 | } 17 | if (containsEncryptAttribute && containsNodeEncryptAttribute) 18 | { 19 | throw new($"Cannot contain both {nameof(EncryptAttribute)} and {nameof(NodeEncryptAttribute)}."); 20 | } 21 | if (containsNodeEncryptAttribute) 22 | { 23 | if (property.Converter == null) 24 | { 25 | property.Converter = new NodeConverter(encrypter); 26 | return; 27 | } 28 | property.Converter = new WrappedNodeConverter(encrypter, property.Converter); 29 | return; 30 | } 31 | 32 | var memberType = member.GetUnderlyingType(); 33 | 34 | if (memberType == typeof(string)) 35 | { 36 | VerifyConverterIsNull(property, member); 37 | property.Converter = new StringConverter(encrypter); 38 | return; 39 | } 40 | if (memberType.IsStringDictionary()) 41 | { 42 | VerifyItemConverterIsNull(property, member); 43 | property.ItemConverter = new StringConverter(encrypter); 44 | return; 45 | } 46 | if (memberType.IsStringEnumerable()) 47 | { 48 | VerifyItemConverterIsNull(property, member); 49 | property.ItemConverter = new StringConverter(encrypter); 50 | return; 51 | } 52 | 53 | if (memberType == typeof(Guid)) 54 | { 55 | VerifyConverterIsNull(property, member); 56 | property.Converter = new GuidConverter(encrypter); 57 | return; 58 | } 59 | if (memberType.IsGuidDictionary()) 60 | { 61 | VerifyItemConverterIsNull(property, member); 62 | property.ItemConverter = new GuidConverter(encrypter); 63 | return; 64 | } 65 | if (memberType.IsGuidEnumerable()) 66 | { 67 | VerifyItemConverterIsNull(property, member); 68 | property.ItemConverter = new GuidConverter(encrypter); 69 | return; 70 | } 71 | 72 | if (memberType == typeof(byte[])) 73 | { 74 | VerifyConverterIsNull(property, member); 75 | property.Converter = new ByteArrayConverter(encrypter); 76 | return; 77 | } 78 | if (memberType.IsByteArrayDictionary()) 79 | { 80 | VerifyItemConverterIsNull(property, member); 81 | property.ItemConverter = new ByteArrayConverter(encrypter); 82 | return; 83 | } 84 | if (memberType.IsByteArrayEnumerable()) 85 | { 86 | VerifyItemConverterIsNull(property, member); 87 | property.ItemConverter = new ByteArrayConverter(encrypter); 88 | return; 89 | } 90 | throw new("Expected a string, a IDictionary, a IEnumerable, a Guid, a IDictionary, a IEnumerable, a byte[], a IDictionary, or a IEnumerable."); 91 | } 92 | 93 | static void VerifyItemConverterIsNull(JsonProperty property, MemberInfo member) 94 | { 95 | if (property.ItemConverter != null) 96 | { 97 | throw new($"Expected JsonProperty.ItemConverter to be null. Property: {member.FriendlyName()}"); 98 | } 99 | } 100 | 101 | static void VerifyConverterIsNull(JsonProperty property, MemberInfo member) 102 | { 103 | if (property.Converter != null) 104 | { 105 | throw new($"Expected JsonProperty.Converter to be null. Property: {member.FriendlyName()}"); 106 | } 107 | } 108 | 109 | static bool ContainsEncryptAttribute(this MemberInfo member) => 110 | member.GetCustomAttribute() != null; 111 | 112 | static bool ContainsNodeEncryptAttribute(this MemberInfo member) => 113 | member.GetCustomAttribute() != null; 114 | 115 | static string FriendlyName(this MemberInfo member) => 116 | $"{member.DeclaringType.FullName}.{member.Name}"; 117 | } -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/Newtonsoft.Json.Encryption.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Newtonsoft.Json.Encryption/NodeEncryptAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Newtonsoft.Json.Encryption; 2 | 3 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 4 | public class NodeEncryptAttribute : 5 | Attribute 6 | { 7 | } -------------------------------------------------------------------------------- /src/Rebus.Newtonsoft.Encryption.Tests/Rebus.Newtonsoft.Encryption.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Rebus.Newtonsoft.Encryption.Tests/Snippets/Snippets.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable SYSLIB0022 2 | using System.Security.Cryptography; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Encryption; 5 | using Rebus.Activation; 6 | using Rebus.Config; 7 | using Rebus.Handlers; 8 | using Rebus.Newtonsoft.Encryption; 9 | using Rebus.Serialization.Json; 10 | // ReSharper disable UnusedParameter.Local 11 | 12 | class Snippets 13 | { 14 | void Usage(byte[] key) 15 | { 16 | #region RebugsUsage 17 | var activator = new BuiltinHandlerActivator(); 18 | 19 | activator.Register(() => new Handler()); 20 | var configurer = Configure.With(activator); 21 | 22 | var encryptionFactory = new EncryptionFactory(); 23 | var settings = new JsonSerializerSettings 24 | { 25 | TypeNameHandling = TypeNameHandling.All, 26 | ContractResolver = encryptionFactory.GetContractResolver() 27 | }; 28 | configurer.Serialization(s => { s.UseNewtonsoftJson(settings); }); 29 | configurer.EnableJsonEncryption( 30 | encryptionFactory: encryptionFactory, 31 | encryptStateBuilder: () => 32 | ( 33 | algorithm: new RijndaelManaged 34 | { 35 | Key = key 36 | }, 37 | keyId: "1" 38 | ), 39 | decryptStateBuilder: (keyId, initVector) => 40 | new RijndaelManaged 41 | { 42 | Key = key, 43 | IV = initVector 44 | }); 45 | #endregion 46 | } 47 | 48 | } 49 | 50 | class Handler: IHandleMessages 51 | { 52 | } 53 | -------------------------------------------------------------------------------- /src/Rebus.Newtonsoft.Encryption/DecryptStep.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | using Rebus.Messages; 3 | using Rebus.Pipeline; 4 | 5 | [StepDocumentation("Decrypts nested properties of the incoming message")] 6 | class DecryptStep : 7 | IIncomingStep 8 | { 9 | EncryptionFactory factory; 10 | DecryptStateBuilder stateBuilder; 11 | 12 | public DecryptStep(EncryptionFactory factory, DecryptStateBuilder stateBuilder) 13 | { 14 | this.factory = factory; 15 | this.stateBuilder = stateBuilder; 16 | } 17 | 18 | public async Task Process(IncomingStepContext context, Func next) 19 | { 20 | var transportMessage = context.Load(); 21 | 22 | if (!transportMessage.Headers.ReadKeyAndIv(out var keyId, out var iv)) 23 | { 24 | await next(); 25 | return; 26 | } 27 | using (factory.GetDecryptSession(stateBuilder(keyId!, iv))) 28 | { 29 | await next(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Rebus.Newtonsoft.Encryption/EncryptStep.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | using Rebus.Messages; 3 | using Rebus.Pipeline; 4 | 5 | [StepDocumentation("Encrypts nested properties of the outgoing message")] 6 | class EncryptStep : 7 | IOutgoingStep 8 | { 9 | EncryptionFactory factory; 10 | EncryptStateBuilder stateBuilder; 11 | 12 | public EncryptStep(EncryptionFactory factory, EncryptStateBuilder stateBuilder) 13 | { 14 | this.factory = factory; 15 | this.stateBuilder = stateBuilder; 16 | } 17 | 18 | public async Task Process(OutgoingStepContext context, Func next) 19 | { 20 | var message = context.Load(); 21 | var state = stateBuilder(); 22 | message.Headers.WriteKeyAndIv(state.keyId, state.algorithm.IV); 23 | using (factory.GetEncryptSession(state.algorithm)) 24 | { 25 | await next(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Rebus.Newtonsoft.Encryption/EncryptionConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | using Rebus.Config; 3 | using Rebus.Pipeline; 4 | using Rebus.Pipeline.Receive; 5 | using Rebus.Pipeline.Send; 6 | 7 | namespace Rebus.Newtonsoft.Encryption; 8 | 9 | /// 10 | /// Configuration extensions for enabling encrypted message bodies 11 | /// 12 | public static class ConfigurationExtensions 13 | { 14 | /// 15 | /// Configures Rebus to encrypt messages. 16 | /// 17 | public static void EnableJsonEncryption(this RebusConfigurer configurer, EncryptionFactory encryptionFactory, EncryptStateBuilder encryptStateBuilder, DecryptStateBuilder decryptStateBuilder) => 18 | configurer.Options(options => 19 | { 20 | options.Decorate( 21 | resolutionContext => 22 | { 23 | var pipeline = resolutionContext.Get(); 24 | var injector = new PipelineStepInjector(pipeline); 25 | 26 | var decryptStep = new DecryptStep(encryptionFactory, decryptStateBuilder); 27 | injector.OnReceive(decryptStep, PipelineRelativePosition.Before, typeof(DeserializeIncomingMessageStep)); 28 | 29 | var encryptStep = new EncryptStep(encryptionFactory, encryptStateBuilder); 30 | injector.OnSend(encryptStep, PipelineRelativePosition.Before, typeof(SerializeOutgoingMessageStep)); 31 | 32 | return injector; 33 | }); 34 | }); 35 | } -------------------------------------------------------------------------------- /src/Rebus.Newtonsoft.Encryption/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /src/Rebus.Newtonsoft.Encryption/Rebus.Newtonsoft.Encryption.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/RebusSample/Handler.cs: -------------------------------------------------------------------------------- 1 | using Rebus.Handlers; 2 | 3 | public class Handler : 4 | IHandleMessages 5 | { 6 | 7 | public Task Handle(MessageWithSecretData message) 8 | { 9 | Console.WriteLine($"Secret: '{message.Secret}'"); 10 | Console.WriteLine($"SubSecret: {message.SubProperty?.Secret}"); 11 | if (message.CreditCards != null) 12 | { 13 | foreach (var creditCard in message.CreditCards) 14 | { 15 | Console.WriteLine($"CreditCard: {creditCard.Number} is valid to {creditCard.ValidTo}"); 16 | } 17 | } 18 | 19 | return Task.CompletedTask; 20 | } 21 | } -------------------------------------------------------------------------------- /src/RebusSample/MessageWithSecretData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | 3 | public class MessageWithSecretData 4 | { 5 | [Encrypt] 6 | public string? Secret { get; set; } 7 | public MySecretSubProperty? SubProperty { get; set; } 8 | public List? CreditCards { get; set; } 9 | } 10 | 11 | public class MySecretSubProperty 12 | { 13 | [Encrypt] 14 | public string? Secret { get; set; } 15 | } 16 | 17 | public class CreditCardDetails 18 | { 19 | public DateTime ValidTo { get; set; } 20 | [Encrypt] 21 | public string? Number { get; set; } 22 | } -------------------------------------------------------------------------------- /src/RebusSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Encryption; 4 | using Rebus.Activation; 5 | using Rebus.Bus; 6 | using Rebus.Config; 7 | using Rebus.Newtonsoft.Encryption; 8 | using Rebus.Serialization.Json; 9 | using Rebus.Transport.FileSystem; 10 | // ReSharper disable UnusedParameter.Local 11 | #pragma warning disable SYSLIB0022 12 | 13 | class Program 14 | { 15 | static async Task Main() 16 | { 17 | var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6"); 18 | Console.Title = "RebusSample"; 19 | var directory = Directory.GetParent(Assembly.GetEntryAssembly()?.Location!)!.FullName; 20 | 21 | var activator = new BuiltinHandlerActivator(); 22 | 23 | activator.Register(() => new Handler()); 24 | var configurer = Configure.With(activator); 25 | 26 | var encryptionFactory = new EncryptionFactory(); 27 | var settings = new JsonSerializerSettings 28 | { 29 | TypeNameHandling = TypeNameHandling.All, 30 | ContractResolver = encryptionFactory.GetContractResolver() 31 | }; 32 | configurer.Serialization(s => 33 | { 34 | s.UseNewtonsoftJson(settings); 35 | }); 36 | configurer.EnableJsonEncryption( 37 | encryptionFactory: encryptionFactory, 38 | encryptStateBuilder: () => 39 | ( 40 | algorithm: new RijndaelManaged 41 | { 42 | Key = key 43 | }, 44 | keyId: "1" 45 | ), 46 | decryptStateBuilder: (keyId, initVector) => 47 | new RijndaelManaged 48 | { 49 | Key = key, 50 | IV = initVector 51 | }); 52 | 53 | configurer.Transport(t => 54 | { 55 | t.UseFileSystem(directory, "RebusSample"); 56 | }); 57 | 58 | var bus = configurer.Start(); 59 | Console.WriteLine("Press any key to exit"); 60 | 61 | await SendMessage(bus) 62 | ; 63 | Console.ReadKey(); 64 | bus?.Dispose(); 65 | encryptionFactory.Dispose(); 66 | activator.Dispose(); 67 | } 68 | 69 | 70 | static Task SendMessage(IBus bus) 71 | { 72 | var message = new MessageWithSecretData 73 | { 74 | Secret = "betcha can't guess my secret", 75 | SubProperty = new() 76 | { 77 | Secret = "My sub secret" 78 | }, 79 | CreditCards = new() 80 | { 81 | new() 82 | { 83 | ValidTo = DateTime.UtcNow.AddYears(1), 84 | Number = "312312312312312" 85 | }, 86 | new() 87 | { 88 | ValidTo = DateTime.UtcNow.AddYears(2), 89 | Number = "543645546546456" 90 | } 91 | } 92 | }; 93 | return bus.SendLocal(message); 94 | } 95 | } -------------------------------------------------------------------------------- /src/RebusSample/RebusSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net7.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Sample/ClassToSerialize.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | 3 | public class ClassToSerialize 4 | { 5 | [Encrypt] 6 | public string? Property { get; set; } 7 | } -------------------------------------------------------------------------------- /src/Sample/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Encryption; 4 | #pragma warning disable SYSLIB0022 5 | 6 | class Program 7 | { 8 | static void Main() 9 | { 10 | // per system (periodically rotated) 11 | var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6"); 12 | 13 | // per app domain 14 | using (var factory = new EncryptionFactory()) 15 | { 16 | var serializer = new JsonSerializer 17 | { 18 | ContractResolver = factory.GetContractResolver() 19 | }; 20 | 21 | // transferred as meta data with the serialized payload 22 | byte[] initVector; 23 | 24 | string serialized; 25 | 26 | // per serialize session 27 | using (var algorithm = new RijndaelManaged 28 | { 29 | Key = key 30 | }) 31 | { 32 | initVector = algorithm.IV; 33 | using (factory.GetEncryptSession(algorithm)) 34 | { 35 | var instance = new ClassToSerialize 36 | { 37 | Property = "PropertyValue", 38 | }; 39 | var builder = new StringBuilder(); 40 | using (var writer = new StringWriter(builder)) 41 | { 42 | serializer.Serialize(writer, instance); 43 | } 44 | serialized = builder.ToString(); 45 | } 46 | } 47 | 48 | // per deserialize session 49 | using (var algorithm = new RijndaelManaged 50 | { 51 | IV = initVector, 52 | Key = key 53 | }) 54 | { 55 | using (factory.GetDecryptSession(algorithm)) 56 | { 57 | using var stringReader = new StringReader(serialized); 58 | using var jsonReader = new JsonTextReader(stringReader); 59 | var deserialized = serializer.Deserialize(jsonReader); 60 | Console.WriteLine(deserialized!.Property); 61 | } 62 | } 63 | } 64 | Console.ReadKey(); 65 | } 66 | } -------------------------------------------------------------------------------- /src/Sample/Sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Shared/AlgorithmBuilders.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace Newtonsoft.Json.Encryption; 4 | 5 | public delegate (SymmetricAlgorithm algorithm, string keyId) EncryptStateBuilder(); 6 | public delegate SymmetricAlgorithm DecryptStateBuilder(string keyId, byte[] initVector); -------------------------------------------------------------------------------- /src/Shared/HeaderExtractor.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Encryption; 2 | 3 | static class HeaderExtractor 4 | { 5 | public static bool ReadKeyAndIv(this IReadOnlyDictionary headers, out string? keyId, out byte[] iv) 6 | { 7 | headers.TryGetValue(KeyId, out keyId); 8 | headers.TryGetValue(Iv, out var ivString); 9 | var ivIsEmpty = string.IsNullOrWhiteSpace(ivString); 10 | var keyIdIsEmpty = string.IsNullOrWhiteSpace(keyId); 11 | if (!keyIdIsEmpty && !ivIsEmpty) 12 | { 13 | iv = Convert.FromBase64String(ivString!); 14 | return true; 15 | } 16 | 17 | if (keyIdIsEmpty && ivIsEmpty) 18 | { 19 | iv = Array.Empty(); 20 | return false; 21 | } 22 | 23 | throw new KeyIdAndIvHeaderMismatchException(); 24 | } 25 | 26 | public static void WriteKeyAndIv(this IDictionary headers, string keyId, byte[] iv) 27 | { 28 | headers[KeyId] = keyId; 29 | headers[Iv] = Convert.ToBase64String(iv); 30 | } 31 | 32 | internal const string KeyId = "NewtonsoftEncryptionKeyId"; 33 | internal const string Iv = "NewtonsoftEncryptionIv"; 34 | } -------------------------------------------------------------------------------- /src/Shared/KeyIdAndIvHeaderMismatchException.cs: -------------------------------------------------------------------------------- 1 | namespace Newtonsoft.Json.Encryption; 2 | 3 | class KeyIdAndIvHeaderMismatchException : 4 | Exception 5 | { 6 | public KeyIdAndIvHeaderMismatchException() : 7 | base($"Either the header values for {HeaderExtractor.KeyId} and {HeaderExtractor.Iv} must both be empty, or both be non empty.") 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /src/Shared/Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2022 2 | environment: 3 | DOTNET_CLI_TELEMETRY_OPTOUT: true 4 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 5 | build_script: 6 | - pwsh: | 7 | Invoke-WebRequest "https://dot.net/v1/dotnet-install.ps1" -OutFile "./dotnet-install.ps1" 8 | ./dotnet-install.ps1 -JSonFile src/global.json -Architecture x64 -InstallDir 'C:\Program Files\dotnet' 9 | - dotnet build src --configuration Release --nologo 10 | - dotnet test src --configuration Release --no-build --no-restore --nologo 11 | test: off 12 | artifacts: 13 | - path: nugets\**\*.nupkg -------------------------------------------------------------------------------- /src/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "7.0.101", 4 | "allowPrerelease": true, 5 | "rollForward": "latestFeature" 6 | } 7 | } -------------------------------------------------------------------------------- /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NServiceBusExtensions/Newtonsoft.Json.Encryption/8e0eaad09b666dd29a64477088559aefba310663/src/icon.png -------------------------------------------------------------------------------- /src/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NServiceBusExtensions/Newtonsoft.Json.Encryption/8e0eaad09b666dd29a64477088559aefba310663/src/key.snk -------------------------------------------------------------------------------- /src/mdsnippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/SimonCropp/MarkdownSnippets/master/schema.json", 3 | "TocExcludes": [ "NuGet Packages", "Release Notes", "Icon" ], 4 | "MaxWidth": 80, 5 | "ValidateContent": true 6 | } --------------------------------------------------------------------------------