├── .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 | [](https://ci.appveyor.com/project/SimonCropp/newtonsoft-json-encryption)
11 | [](https://www.nuget.org/packages/Newtonsoft.Json.Encryption/)
12 | [](https://www.nuget.org/packages/Rebus.Newtonsoft.Encryption/)
13 | [](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 | [](https://ci.appveyor.com/project/SimonCropp/newtonsoft-json-encryption)
4 | [](https://www.nuget.org/packages/Newtonsoft.Json.Encryption/)
5 | [](https://www.nuget.org/packages/Rebus.Newtonsoft.Encryption/)
6 | [](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><profile version="1.0">
8 | <option name="myName" value="c# Cleanup" />
9 | </profile></IDEA_SETTINGS><RIDER_SETTINGS><profile>
10 | <Language id="EditorConfig">
11 | <Reformat>false</Reformat>
12 | </Language>
13 | <Language id="HTML">
14 | <OptimizeImports>false</OptimizeImports>
15 | <Reformat>false</Reformat>
16 | <Rearrange>false</Rearrange>
17 | </Language>
18 | <Language id="JSON">
19 | <Reformat>false</Reformat>
20 | </Language>
21 | <Language id="RELAX-NG">
22 | <Reformat>false</Reformat>
23 | </Language>
24 | <Language id="XML">
25 | <OptimizeImports>false</OptimizeImports>
26 | <Reformat>false</Reformat>
27 | <Rearrange>false</Rearrange>
28 | </Language>
29 | </profile></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 | }
--------------------------------------------------------------------------------