37 changed files with 1742 additions and 0 deletions
@ -0,0 +1,109 @@ |
|||||||
|
# EditorConfig is awesome: http://EditorConfig.org |
||||||
|
|
||||||
|
# top-most EditorConfig file |
||||||
|
root = true |
||||||
|
|
||||||
|
# Don't use tabs for indentation. |
||||||
|
[*] |
||||||
|
indent_style = space |
||||||
|
# (Please don't specify an indent_size here; that has too many unintended consequences.) |
||||||
|
|
||||||
|
# Code files |
||||||
|
[*.{cs,csx,vb,vbx}] |
||||||
|
indent_size = 4 |
||||||
|
insert_final_newline = true |
||||||
|
charset = utf-8-bom |
||||||
|
|
||||||
|
# Xml project files |
||||||
|
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] |
||||||
|
indent_size = 2 |
||||||
|
|
||||||
|
# Xml config files |
||||||
|
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] |
||||||
|
indent_size = 2 |
||||||
|
|
||||||
|
# JSON files |
||||||
|
[*.json] |
||||||
|
indent_size = 2 |
||||||
|
|
||||||
|
# Dotnet code style settings: |
||||||
|
[*.{cs,vb}] |
||||||
|
# Sort using and Import directives with System.* appearing first |
||||||
|
dotnet_sort_system_directives_first = true |
||||||
|
# Avoid "this." and "Me." if not necessary |
||||||
|
dotnet_style_qualification_for_field = false:suggestion |
||||||
|
dotnet_style_qualification_for_property = false:suggestion |
||||||
|
dotnet_style_qualification_for_method = false:suggestion |
||||||
|
dotnet_style_qualification_for_event = false:suggestion |
||||||
|
|
||||||
|
# Use language keywords instead of framework type names for type references |
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion |
||||||
|
dotnet_style_predefined_type_for_member_access = true:suggestion |
||||||
|
|
||||||
|
# Suggest more modern language features when available |
||||||
|
dotnet_style_object_initializer = true:suggestion |
||||||
|
dotnet_style_collection_initializer = true:suggestion |
||||||
|
dotnet_style_coalesce_expression = true:suggestion |
||||||
|
dotnet_style_null_propagation = true:suggestion |
||||||
|
dotnet_style_explicit_tuple_names = true:suggestion |
||||||
|
|
||||||
|
# Prefix private members with underscore |
||||||
|
dotnet_naming_rule.private_members_with_underscore.symbols = private_fields |
||||||
|
dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore |
||||||
|
dotnet_naming_rule.private_members_with_underscore.severity = suggestion |
||||||
|
|
||||||
|
dotnet_naming_symbols.private_fields.applicable_kinds = field |
||||||
|
dotnet_naming_symbols.private_fields.applicable_accessibilities = private |
||||||
|
|
||||||
|
dotnet_naming_style.prefix_underscore.capitalization = camel_case |
||||||
|
dotnet_naming_style.prefix_underscore.required_prefix = _ |
||||||
|
|
||||||
|
# Async methods should have "Async" suffix |
||||||
|
dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods |
||||||
|
dotnet_naming_rule.async_methods_end_in_async.style = end_in_async |
||||||
|
dotnet_naming_rule.async_methods_end_in_async.severity = suggestion |
||||||
|
|
||||||
|
dotnet_naming_symbols.any_async_methods.applicable_kinds = method |
||||||
|
dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * |
||||||
|
dotnet_naming_symbols.any_async_methods.required_modifiers = async |
||||||
|
|
||||||
|
dotnet_naming_style.end_in_async.required_prefix = |
||||||
|
dotnet_naming_style.end_in_async.required_suffix = Async |
||||||
|
dotnet_naming_style.end_in_async.capitalization = pascal_case |
||||||
|
dotnet_naming_style.end_in_async.word_separator = |
||||||
|
|
||||||
|
# CSharp code style settings: |
||||||
|
[*.cs] |
||||||
|
# Prefer "var" everywhere |
||||||
|
csharp_style_var_for_built_in_types = true:suggestion |
||||||
|
csharp_style_var_when_type_is_apparent = true:suggestion |
||||||
|
csharp_style_var_elsewhere = true:suggestion |
||||||
|
|
||||||
|
# Prefer method-like constructs to have a expression-body |
||||||
|
csharp_style_expression_bodied_methods = true:none |
||||||
|
csharp_style_expression_bodied_constructors = true:none |
||||||
|
csharp_style_expression_bodied_operators = true:none |
||||||
|
|
||||||
|
# Prefer property-like constructs to have an expression-body |
||||||
|
csharp_style_expression_bodied_properties = true:none |
||||||
|
csharp_style_expression_bodied_indexers = true:none |
||||||
|
csharp_style_expression_bodied_accessors = true:none |
||||||
|
|
||||||
|
# Suggest more modern language features when available |
||||||
|
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion |
||||||
|
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion |
||||||
|
csharp_style_inlined_variable_declaration = true:suggestion |
||||||
|
csharp_style_throw_expression = true:suggestion |
||||||
|
csharp_style_conditional_delegate_call = true:suggestion |
||||||
|
|
||||||
|
# Newline settings |
||||||
|
csharp_new_line_before_open_brace = all |
||||||
|
csharp_new_line_before_else = true |
||||||
|
csharp_new_line_before_catch = true |
||||||
|
csharp_new_line_before_finally = true |
||||||
|
csharp_new_line_before_members_in_object_initializers = true |
||||||
|
csharp_new_line_before_members_in_anonymous_types = true |
||||||
|
|
||||||
|
# All files |
||||||
|
[*] |
||||||
|
guidelines = 120 |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
*.sh eol=lf |
||||||
|
.dockerignore eol=lf |
||||||
|
dockerfile eol=lf |
||||||
@ -0,0 +1,219 @@ |
|||||||
|
## Ignore Visual Studio temporary files, build results, and |
||||||
|
## files generated by popular Visual Studio add-ons. |
||||||
|
|
||||||
|
# User-specific files |
||||||
|
*.suo |
||||||
|
*.user |
||||||
|
*.userosscache |
||||||
|
*.sln.docstates |
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio) |
||||||
|
*.userprefs |
||||||
|
|
||||||
|
# Build results |
||||||
|
[Dd]ebug/ |
||||||
|
[Dd]ebugPublic/ |
||||||
|
[Rr]elease/ |
||||||
|
[Rr]eleases/ |
||||||
|
x64/ |
||||||
|
x86/ |
||||||
|
build/ |
||||||
|
bld/ |
||||||
|
[Bb]in/ |
||||||
|
[Oo]bj/ |
||||||
|
|
||||||
|
# Visual Studo 2015 cache/options directory |
||||||
|
.vs/ |
||||||
|
|
||||||
|
# MSTest test Results |
||||||
|
[Tt]est[Rr]esult*/ |
||||||
|
[Bb]uild[Ll]og.* |
||||||
|
|
||||||
|
# NUNIT |
||||||
|
*.VisualState.xml |
||||||
|
TestResult.xml |
||||||
|
|
||||||
|
# Build Results of an ATL Project |
||||||
|
[Dd]ebugPS/ |
||||||
|
[Rr]eleasePS/ |
||||||
|
dlldata.c |
||||||
|
|
||||||
|
*_i.c |
||||||
|
*_p.c |
||||||
|
*_i.h |
||||||
|
*.ilk |
||||||
|
*.meta |
||||||
|
*.obj |
||||||
|
*.pch |
||||||
|
*.pdb |
||||||
|
*.pgc |
||||||
|
*.pgd |
||||||
|
*.rsp |
||||||
|
*.sbr |
||||||
|
*.tlb |
||||||
|
*.tli |
||||||
|
*.tlh |
||||||
|
*.tmp |
||||||
|
*.tmp_proj |
||||||
|
*.log |
||||||
|
*.vspscc |
||||||
|
*.vssscc |
||||||
|
.builds |
||||||
|
*.pidb |
||||||
|
*.svclog |
||||||
|
*.scc |
||||||
|
|
||||||
|
# Chutzpah Test files |
||||||
|
_Chutzpah* |
||||||
|
|
||||||
|
# Visual C++ cache files |
||||||
|
ipch/ |
||||||
|
*.aps |
||||||
|
*.ncb |
||||||
|
*.opensdf |
||||||
|
*.sdf |
||||||
|
*.cachefile |
||||||
|
|
||||||
|
# Visual Studio profiler |
||||||
|
*.psess |
||||||
|
*.vsp |
||||||
|
*.vspx |
||||||
|
|
||||||
|
# TFS 2012 Local Workspace |
||||||
|
$tf/ |
||||||
|
|
||||||
|
# Guidance Automation Toolkit |
||||||
|
*.gpState |
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in |
||||||
|
_ReSharper*/ |
||||||
|
*.[Rr]e[Ss]harper |
||||||
|
*.DotSettings.user |
||||||
|
|
||||||
|
# JustCode is a .NET coding addin-in |
||||||
|
.JustCode |
||||||
|
|
||||||
|
# TeamCity is a build add-in |
||||||
|
_TeamCity* |
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool |
||||||
|
*.dotCover |
||||||
|
|
||||||
|
# NCrunch |
||||||
|
_NCrunch_* |
||||||
|
.*crunch*.local.xml |
||||||
|
|
||||||
|
# MightyMoose |
||||||
|
*.mm.* |
||||||
|
AutoTest.Net/ |
||||||
|
|
||||||
|
# Web workbench (sass) |
||||||
|
.sass-cache/ |
||||||
|
|
||||||
|
# Installshield output folder |
||||||
|
[Ee]xpress/ |
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in |
||||||
|
DocProject/buildhelp/ |
||||||
|
DocProject/Help/*.HxT |
||||||
|
DocProject/Help/*.HxC |
||||||
|
DocProject/Help/*.hhc |
||||||
|
DocProject/Help/*.hhk |
||||||
|
DocProject/Help/*.hhp |
||||||
|
DocProject/Help/Html2 |
||||||
|
DocProject/Help/html |
||||||
|
|
||||||
|
# Click-Once directory |
||||||
|
publish/ |
||||||
|
|
||||||
|
# Publish Web Output |
||||||
|
*.[Pp]ublish.xml |
||||||
|
*.azurePubxml |
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings |
||||||
|
# but database connection strings (with potential passwords) will be unencrypted |
||||||
|
*.pubxml |
||||||
|
*.publishproj |
||||||
|
PublishProfiles/ |
||||||
|
|
||||||
|
# NuGet Packages |
||||||
|
*.nupkg |
||||||
|
# The packages folder can be ignored because of Package Restore |
||||||
|
**/packages/* |
||||||
|
# except build/, which is used as an MSBuild target. |
||||||
|
!**/packages/build/ |
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed |
||||||
|
#!**/packages/repositories.config |
||||||
|
|
||||||
|
# Windows Azure Build Output |
||||||
|
csx/ |
||||||
|
*.build.csdef |
||||||
|
|
||||||
|
# Windows Store app package directory |
||||||
|
AppPackages/ |
||||||
|
|
||||||
|
# Others |
||||||
|
*.[Cc]ache |
||||||
|
ClientBin/ |
||||||
|
[Ss]tyle[Cc]op.* |
||||||
|
~$* |
||||||
|
*~ |
||||||
|
*.dbmdl |
||||||
|
*.dbproj.schemaview |
||||||
|
*.pfx |
||||||
|
*.publishsettings |
||||||
|
node_modules/ |
||||||
|
bower_components/ |
||||||
|
|
||||||
|
# RIA/Silverlight projects |
||||||
|
Generated_Code/ |
||||||
|
|
||||||
|
# Backup & report files from converting an old project file |
||||||
|
# to a newer Visual Studio version. Backup files are not needed, |
||||||
|
# because we have git ;-) |
||||||
|
_UpgradeReport_Files/ |
||||||
|
Backup*/ |
||||||
|
UpgradeLog*.XML |
||||||
|
UpgradeLog*.htm |
||||||
|
|
||||||
|
# SQL Server files |
||||||
|
*.mdf |
||||||
|
*.ldf |
||||||
|
|
||||||
|
# Business Intelligence projects |
||||||
|
*.rdl.data |
||||||
|
*.bim.layout |
||||||
|
*.bim_*.settings |
||||||
|
|
||||||
|
# Microsoft Fakes |
||||||
|
FakesAssemblies/ |
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio |
||||||
|
.ntvs_analysis.dat |
||||||
|
|
||||||
|
# Visual Studio 6 build log |
||||||
|
*.plg |
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file |
||||||
|
*.opt |
||||||
|
|
||||||
|
# Other |
||||||
|
project.lock.json |
||||||
|
*.jfm |
||||||
|
mail_dist/ |
||||||
|
*.refactorlog |
||||||
|
*.scmp |
||||||
|
src/Core/Properties/launchSettings.json |
||||||
|
*.override.env |
||||||
|
**/*.DS_Store |
||||||
|
src/Admin/wwwroot/lib |
||||||
|
src/Admin/wwwroot/css |
||||||
|
.vscode/* |
||||||
|
**/.vscode/* |
||||||
|
bitwarden_license/src/Portal/wwwroot/lib |
||||||
|
bitwarden_license/src/Portal/wwwroot/css |
||||||
|
bitwarden_license/src/Sso/wwwroot/lib |
||||||
|
bitwarden_license/src/Sso/wwwroot/css |
||||||
|
.github/test/build.secrets |
||||||
|
**/CoverageOutput/ |
||||||
|
.idea/* |
||||||
|
**/**.swp |
||||||
@ -0,0 +1,182 @@ |
|||||||
|
BITWARDEN LICENSE AGREEMENT |
||||||
|
Version 1, 4 September 2020 |
||||||
|
|
||||||
|
PLEASE CAREFULLY READ THIS BITWARDEN LICENSE AGREEMENT ("AGREEMENT"). THIS |
||||||
|
AGREEMENT CONSTITUTES A LEGALLY BINDING AGREEMENT BETWEEN YOU AND BITWARDEN, |
||||||
|
INC. ("BITWARDEN") AND GOVERNS YOUR USE OF THE COMMERCIAL MODULES (DEFINED |
||||||
|
BELOW). BY COPYING OR USING THE COMMERCIAL MODULES, YOU AGREE TO THIS AGREEMENT. |
||||||
|
IF YOU DO NOT AGREE WITH THIS AGREEMENT, YOU MAY NOT COPY OR USE THE COMMERCIAL |
||||||
|
MODULES. IF YOU ARE COPYING OR USING THE COMMERCIAL MODULES ON BEHALF OF A LEGAL |
||||||
|
ENTITY, YOU REPRESENT AND WARRANT THAT YOU HAVE AUTHORITY TO AGREE TO THIS |
||||||
|
AGREEMENT ON BEHALF OF SUCH ENTITY. IF YOU DO NOT HAVE SUCH AUTHORITY, DO NOT |
||||||
|
COPY OR USE THE COMMERCIAL MODULES IN ANY MANNER. |
||||||
|
|
||||||
|
This Agreement is entered into by and between Bitwarden and you, or the legal |
||||||
|
entity on behalf of whom you are acting (as applicable, "You" or "Your"). |
||||||
|
|
||||||
|
1. DEFINITIONS |
||||||
|
|
||||||
|
"Bitwarden Software" means the Bitwarden server software, libraries, and |
||||||
|
Commercial Modules. |
||||||
|
|
||||||
|
"Commercial Modules" means the modules designed to work with and enhance the |
||||||
|
Bitwarden Software to which this Agreement is linked, referenced, or appended. |
||||||
|
|
||||||
|
2. LICENSES, RESTRICTIONS AND THIRD PARTY CODE |
||||||
|
|
||||||
|
2.1 Commercial Module License. Subject to Your compliance with this Agreement, |
||||||
|
Bitwarden hereby grants to You a limited, non-exclusive, non-transferable, |
||||||
|
royalty-free license to use the Commercial Modules for the sole purposes of |
||||||
|
internal development and internal testing, and only in a non-production |
||||||
|
environment. |
||||||
|
|
||||||
|
2.2 Reservation of Rights. As between Bitwarden and You, Bitwarden owns all |
||||||
|
right, title and interest in and to the Bitwarden Software, and except as |
||||||
|
expressly set forth in Sections 2.1, no other license to the Bitwarden Software |
||||||
|
is granted to You under this Agreement, by implication, estoppel, or otherwise. |
||||||
|
|
||||||
|
2.3 Restrictions. You agree not to: (i) except as expressly permitted in |
||||||
|
Section 2.1, sell, rent, lease, distribute, sublicense, loan or otherwise |
||||||
|
transfer the Commercial Modules to any third party; (ii) alter or remove any |
||||||
|
trademarks, service mark, and logo included with the Commercial Modules, or |
||||||
|
(iii) use the Commercial Modules to create a competing product or service. |
||||||
|
Bitwarden is not obligated to provide maintenance and support services for the |
||||||
|
Bitwarden Software licensed under this Agreement. |
||||||
|
|
||||||
|
2.4 Third Party Software. The Commercial Modules may contain or be provided |
||||||
|
with third party open source libraries, components, utilities and other open |
||||||
|
source software (collectively, "Open Source Software"). Notwithstanding anything |
||||||
|
to the contrary herein, use of the Open Source Software will be subject to the |
||||||
|
license terms and conditions applicable to such Open Source Software. To the |
||||||
|
extent any condition of this Agreement conflicts with any license to the Open |
||||||
|
Source Software, the Open Source Software license will govern with respect to |
||||||
|
such Open Source Software only. |
||||||
|
|
||||||
|
2.5 This Agreement does not grant any rights in the trademarks, service marks, or |
||||||
|
logos of any Contributor (except as may be necessary to comply with the notice |
||||||
|
requirements in Section 2.3), and use of any Bitwarden trademarks must comply with |
||||||
|
Bitwarden Trademark Guidelines |
||||||
|
<https://github.com/bitwarden/server/blob/master/TRADEMARK_GUIDELINES.md>. |
||||||
|
|
||||||
|
3. TERMINATION |
||||||
|
|
||||||
|
3.1 Termination. This Agreement will automatically terminate upon notice from |
||||||
|
Bitwarden, which notice may be by email or posting in the location where the |
||||||
|
Commercial Modules are made available. |
||||||
|
|
||||||
|
3.2 Effect of Termination. Upon any termination of this Agreement, for any |
||||||
|
reason, You will promptly cease use of the Commercial Modules and destroy any |
||||||
|
copies thereof. For the avoidance of doubt, termination of this Agreement will |
||||||
|
not affect Your right to Bitwarden Software, other than the Commercial Modules, |
||||||
|
made available pursuant to an Open Source Software license. |
||||||
|
|
||||||
|
3.3 Survival. Sections 1, 2.2 -2.4, 3.2, 3.3, 4, and 5 will survive any |
||||||
|
termination of this Agreement. |
||||||
|
|
||||||
|
4. DISCLAIMER AND LIMITATION OF LIABILITY |
||||||
|
|
||||||
|
4.1 Disclaimer of Warranties. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE |
||||||
|
LAW, THE BITWARDEN SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, |
||||||
|
EXPRESS OR IMPLIED REGARDING OR RELATING TO THE BITWARDEN SOFTWARE, INCLUDING |
||||||
|
ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, |
||||||
|
TITLE, AND NON-INFRINGEMENT. FURTHER, BITWARDEN DOES NOT WARRANT RESULTS OF USE |
||||||
|
OR THAT THE BITWARDEN SOFTWARE WILL BE ERROR FREE OR THAT THE USE OF THE |
||||||
|
BITWARDEN SOFTWARE WILL BE UNINTERRUPTED. |
||||||
|
|
||||||
|
4.2 Limitation of Liability. IN NO EVENT WILL BITWARDEN OR ITS LICENSORS BE |
||||||
|
LIABLE TO YOU OR ANY THIRD PARTY UNDER THIS AGREEMENT FOR (I) ANY AMOUNTS IN |
||||||
|
EXCESS OF US $25 OR (II) FOR ANY SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF |
||||||
|
ANY KIND, INCLUDING FOR ANY LOSS OF PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, |
||||||
|
LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, WHETHER ALLEGED AS A BREACH |
||||||
|
OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF BITWARDEN HAS |
||||||
|
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. |
||||||
|
|
||||||
|
5. MISCELLANEOUS |
||||||
|
|
||||||
|
5.1 Assignment. You may not assign or otherwise transfer this Agreement or any |
||||||
|
rights or obligations hereunder, in whole or in part, whether by operation of |
||||||
|
law or otherwise, to any third party without Bitwarden's prior written consent. |
||||||
|
Any purported transfer, assignment or delegation without such prior written |
||||||
|
consent will be null and void and of no force or effect. Bitwarden may assign |
||||||
|
this Agreement to any successor to its business or assets to which this |
||||||
|
Agreement relates, whether by merger, sale of assets, sale of stock, |
||||||
|
reorganization or otherwise. Subject to this Section 5.1, this Agreement will be |
||||||
|
binding upon and inure to the benefit of the parties hereto, and their |
||||||
|
respective successors and permitted assigns. |
||||||
|
|
||||||
|
5.2 Entire Agreement; Modification; Waiver. This Agreement represents the |
||||||
|
entire agreement between the parties, and supersedes all prior agreements and |
||||||
|
understandings, written or oral, with respect to the matters covered by this |
||||||
|
Agreement, and is not intended to confer upon any third party any rights or |
||||||
|
remedies hereunder. You acknowledge that You have not entered in this Agreement |
||||||
|
based on any representations other than those contained herein. No modification |
||||||
|
of or amendment to this Agreement, nor any waiver of any rights under this |
||||||
|
Agreement, will be effective unless in writing and signed by both parties. The |
||||||
|
waiver of one breach or default or any delay in exercising any rights will not |
||||||
|
constitute a waiver of any subsequent breach or default. |
||||||
|
|
||||||
|
5.3 Governing Law. This Agreement will in all respects be governed by the laws |
||||||
|
of the State of California without reference to its principles of conflicts of |
||||||
|
laws. The parties hereby agree that all disputes arising out of this Agreement |
||||||
|
will be subject to the exclusive jurisdiction of and venue in the federal and |
||||||
|
state courts within Los Angeles County, California. You hereby consent to the |
||||||
|
personal and exclusive jurisdiction and venue of these courts. The parties |
||||||
|
hereby disclaim and exclude the application hereto of the United Nations |
||||||
|
Convention on Contracts for the International Sale of Goods. |
||||||
|
|
||||||
|
5.4 Severability. If any provision of this Agreement is held invalid or |
||||||
|
unenforceable under applicable law by a court of competent jurisdiction, it will |
||||||
|
be replaced with the valid provision that most closely reflects the intent of |
||||||
|
the parties and the remaining provisions of the Agreement will remain in full |
||||||
|
force and effect. |
||||||
|
|
||||||
|
5.5 Relationship of the Parties. Nothing in this Agreement is to be construed |
||||||
|
as creating an agency, partnership, or joint venture relationship between the |
||||||
|
parties hereto. Neither party will have any right or authority to assume or |
||||||
|
create any obligations or to make any representations or warranties on behalf of |
||||||
|
any other party, whether express or implied, or to bind the other party in any |
||||||
|
respect whatsoever. |
||||||
|
|
||||||
|
5.6 Notices. All notices permitted or required under this Agreement will be in |
||||||
|
writing and will be deemed to have been given when delivered in person |
||||||
|
(including by overnight courier), or three (3) business days after being mailed |
||||||
|
by first class, registered or certified mail, postage prepaid, to the address of |
||||||
|
the party specified in this Agreement or such other address as either party may |
||||||
|
specify in writing. |
||||||
|
|
||||||
|
5.7 U.S. Government Restricted Rights. If Commercial Modules is being licensed |
||||||
|
by the U.S. Government, the Commercial Modules is deemed to be "commercial |
||||||
|
computer software" and "commercial computer documentation" developed exclusively |
||||||
|
at private expense, and (a) if acquired by or on behalf of a civilian agency, |
||||||
|
will be subject solely to the terms of this computer software license as |
||||||
|
specified in 48 C.F.R. 12.212 of the Federal Acquisition Regulations and its |
||||||
|
successors; and (b) if acquired by or on behalf of units of the Department of |
||||||
|
Defense ("DOD") will be subject to the terms of this commercial computer |
||||||
|
software license as specified in 48 C.F.R. 227.7202-2, DOD FAR Supplement and |
||||||
|
its successors. |
||||||
|
|
||||||
|
5.8 Injunctive Relief. A breach or threatened breach by You of Section 2 may |
||||||
|
cause irreparable harm for which damages at law may not provide adequate relief, |
||||||
|
and therefore Bitwarden will be entitled to seek injunctive relief in any |
||||||
|
applicable jurisdiction without being required to post a bond. |
||||||
|
|
||||||
|
5.9 Export Law Assurances. You understand that the Commercial Modules is |
||||||
|
subject to export control laws and regulations. You may not download or |
||||||
|
otherwise export or re-export the Commercial Modules or any underlying |
||||||
|
information or technology except in full compliance with all applicable laws and |
||||||
|
regulations, in particular, but without limitation, United States export control |
||||||
|
laws. None of the Commercial Modules or any underlying information or technology |
||||||
|
may be downloaded or otherwise exported or re- exported: (a) into (or to a |
||||||
|
national or resident of) any country to which the United States has embargoed |
||||||
|
goods; or (b) to anyone on the U.S. Treasury Department's list of specially |
||||||
|
designated nationals or the U.S. Commerce Department's list of prohibited |
||||||
|
countries or debarred or denied persons or entities. You hereby agree to the |
||||||
|
foregoing and represents and warrants that You are not located in, under control |
||||||
|
of, or a national or resident of any such country or on any such list. |
||||||
|
|
||||||
|
5.10 Construction. The titles and section headings used in this Agreement are |
||||||
|
for ease of reference only and will not be used in the interpretation or |
||||||
|
construction of this Agreement. No rule of construction resolving any ambiguity |
||||||
|
in favor of the non-drafting party will be applied hereto. The word "including", |
||||||
|
when used herein, is illustrative rather than exclusive and means "including, |
||||||
|
without limitation." |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||||
|
# Visual Studio Version 16 |
||||||
|
VisualStudioVersion = 16.0.31205.134 |
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1 |
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CryptoAgent", "src\CryptoAgent\CryptoAgent.csproj", "{CAD440BF-93C9-4DEC-B083-99FD49B50429}" |
||||||
|
EndProject |
||||||
|
Global |
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||||
|
Debug|Any CPU = Debug|Any CPU |
||||||
|
Release|Any CPU = Release|Any CPU |
||||||
|
EndGlobalSection |
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||||
|
{CAD440BF-93C9-4DEC-B083-99FD49B50429}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||||
|
{CAD440BF-93C9-4DEC-B083-99FD49B50429}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||||
|
{CAD440BF-93C9-4DEC-B083-99FD49B50429}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||||
|
{CAD440BF-93C9-4DEC-B083-99FD49B50429}.Release|Any CPU.Build.0 = Release|Any CPU |
||||||
|
EndGlobalSection |
||||||
|
GlobalSection(SolutionProperties) = preSolution |
||||||
|
HideSolutionNode = FALSE |
||||||
|
EndGlobalSection |
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution |
||||||
|
SolutionGuid = {40AD45DE-FF2B-4E64-9451-F368AA7C01E9} |
||||||
|
EndGlobalSection |
||||||
|
EndGlobal |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
using Bit.CryptoAgent.Services; |
||||||
|
using Microsoft.AspNetCore.Authorization; |
||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Controllers |
||||||
|
{ |
||||||
|
public class MiscController : Controller |
||||||
|
{ |
||||||
|
private readonly IRsaKeyService _rsaKeyService; |
||||||
|
|
||||||
|
public MiscController( |
||||||
|
IRsaKeyService rsaKeyService) |
||||||
|
{ |
||||||
|
_rsaKeyService = rsaKeyService; |
||||||
|
} |
||||||
|
|
||||||
|
[HttpGet("~/alive")] |
||||||
|
[HttpGet("~/now")] |
||||||
|
[AllowAnonymous] |
||||||
|
public DateTime GetAlive() |
||||||
|
{ |
||||||
|
return DateTime.UtcNow; |
||||||
|
} |
||||||
|
|
||||||
|
[HttpGet("~/public-key")] |
||||||
|
[AllowAnonymous] |
||||||
|
public async Task<IActionResult> GetPublicKey() |
||||||
|
{ |
||||||
|
var key = await _rsaKeyService.GetPublicKeyAsync(); |
||||||
|
return new OkObjectResult(new { PublicKey = Convert.ToBase64String(key) }); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,84 @@ |
|||||||
|
using Bit.CryptoAgent.Models; |
||||||
|
using Bit.CryptoAgent.Repositories; |
||||||
|
using Bit.CryptoAgent.Services; |
||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Controllers |
||||||
|
{ |
||||||
|
[Route("user-keys")] |
||||||
|
public class UserKeysController : Controller |
||||||
|
{ |
||||||
|
private readonly ILogger<UserKeysController> _logger; |
||||||
|
private readonly ICryptoService _cryptoService; |
||||||
|
private readonly IUserKeyRepository _userKeyRepository; |
||||||
|
|
||||||
|
public UserKeysController( |
||||||
|
ILogger<UserKeysController> logger, |
||||||
|
IUserKeyRepository userKeyRepository, |
||||||
|
ICryptoService cryptoService) |
||||||
|
{ |
||||||
|
_logger = logger; |
||||||
|
_cryptoService = cryptoService; |
||||||
|
_userKeyRepository = userKeyRepository; |
||||||
|
} |
||||||
|
|
||||||
|
[HttpPost("{userId}/get")] |
||||||
|
public async Task<IActionResult> Get(Guid userId, [FromBody] UserKeyGetRequestModel model) |
||||||
|
{ |
||||||
|
var publicKey = Convert.FromBase64String(model.PublicKey); |
||||||
|
var user = await _userKeyRepository.ReadAsync(userId); |
||||||
|
if (user == null) |
||||||
|
{ |
||||||
|
return new NotFoundResult(); |
||||||
|
} |
||||||
|
user.LastAccessDate = DateTime.UtcNow; |
||||||
|
await _userKeyRepository.UpdateAsync(user); |
||||||
|
var key = await _cryptoService.AesDecryptAsync(user.Key); |
||||||
|
var encKey = await _cryptoService.RsaEncryptAsync(key, publicKey); |
||||||
|
var response = new UserKeyResponseModel |
||||||
|
{ |
||||||
|
Key = Convert.ToBase64String(encKey) |
||||||
|
}; |
||||||
|
return new JsonResult(response); |
||||||
|
} |
||||||
|
|
||||||
|
[HttpPost("{userId}")] |
||||||
|
public async Task<IActionResult> Post(Guid userId, [FromBody] UserKeyRequestModel model) |
||||||
|
{ |
||||||
|
var user = await _userKeyRepository.ReadAsync(userId); |
||||||
|
if (user != null) |
||||||
|
{ |
||||||
|
return new BadRequestResult(); |
||||||
|
} |
||||||
|
var key = await _cryptoService.RsaDecryptAsync(Convert.FromBase64String(model.Key)); |
||||||
|
user = new UserKeyModel |
||||||
|
{ |
||||||
|
Id = userId, |
||||||
|
Key = await _cryptoService.AesEncryptToB64Async(key) |
||||||
|
}; |
||||||
|
await _userKeyRepository.CreateAsync(user); |
||||||
|
return new OkResult(); |
||||||
|
} |
||||||
|
|
||||||
|
[HttpPut("{userId}")] |
||||||
|
public async Task<IActionResult> Put(Guid userId, [FromBody] UserKeyRequestModel model) |
||||||
|
{ |
||||||
|
var user = await _userKeyRepository.ReadAsync(userId); |
||||||
|
if (user != null) |
||||||
|
{ |
||||||
|
return new BadRequestResult(); |
||||||
|
} |
||||||
|
var key = await _cryptoService.RsaDecryptAsync(Convert.FromBase64String(model.Key)); |
||||||
|
user = new UserKeyModel |
||||||
|
{ |
||||||
|
Id = userId, |
||||||
|
Key = await _cryptoService.AesEncryptToB64Async(key) |
||||||
|
}; |
||||||
|
await _userKeyRepository.UpdateAsync(user); |
||||||
|
return new OkResult(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web"> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>net5.0</TargetFramework> |
||||||
|
<RootNamespace>Bit.CryptoAgent</RootNamespace> |
||||||
|
<UserSecretsId>116f49e5-0b50-4080-856f-7e812413e723</UserSecretsId> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="Azure.Identity" Version="1.4.1" /> |
||||||
|
<PackageReference Include="Azure.Security.KeyVault.Certificates" Version="4.2.0" /> |
||||||
|
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.2.0" /> |
||||||
|
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.2.0" /> |
||||||
|
<PackageReference Include="Azure.Storage.Blobs" Version="12.9.1" /> |
||||||
|
<PackageReference Include="JsonFlatFileDataStore" Version="2.2.3" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
</Project> |
||||||
@ -0,0 +1,53 @@ |
|||||||
|
namespace Bit.CryptoAgent |
||||||
|
{ |
||||||
|
public class CryptoAgentSettings |
||||||
|
{ |
||||||
|
public DatabaseSettings Database { get; set; } |
||||||
|
public CertificateSettings Certificate { get; set; } |
||||||
|
public RsaKeySettings RsaKey { get; set; } |
||||||
|
|
||||||
|
public class CertificateSettings |
||||||
|
{ |
||||||
|
// Filesystem |
||||||
|
public string FilesystemPath { get; set; } |
||||||
|
public string FilesystemPassword { get; set; } |
||||||
|
// Local store |
||||||
|
public string StoreThumbprint { get; set; } |
||||||
|
// Azure blob storage |
||||||
|
public string AzureStorageConnectionString { get; set; } |
||||||
|
public string AzureStorageContainer { get; set; } |
||||||
|
public string AzureStorageFileName { get; set; } |
||||||
|
public string AzureStorageFilePassword { get; set; } |
||||||
|
// Azure key vault |
||||||
|
public string AzureKeyvaultUri { get; set; } |
||||||
|
public string AzureKeyvaultCertificateName { get; set; } |
||||||
|
public string AzureKeyvaultAdTenantId { get; set; } |
||||||
|
public string AzureKeyvaultAdAppId { get; set; } |
||||||
|
public string AzureKeyvaultAdSecret { get; set; } |
||||||
|
} |
||||||
|
|
||||||
|
public class RsaKeySettings |
||||||
|
{ |
||||||
|
// Local certificate provider |
||||||
|
public string Provider { get; set; } |
||||||
|
// Azure key vault |
||||||
|
public string AzureKeyvaultUri { get; set; } |
||||||
|
public string AzureKeyvaultKeyName { get; set; } |
||||||
|
public string AzureKeyvaultAdTenantId { get; set; } |
||||||
|
public string AzureKeyvaultAdAppId { get; set; } |
||||||
|
public string AzureKeyvaultAdSecret { get; set; } |
||||||
|
// GCP... |
||||||
|
// AWS... |
||||||
|
// Hashicorp Vault... |
||||||
|
// Other HSMs... |
||||||
|
} |
||||||
|
|
||||||
|
public class DatabaseSettings |
||||||
|
{ |
||||||
|
public string JsonFilePath { get; set; } |
||||||
|
public string SqlServerConnectionString { get; set; } |
||||||
|
public string MySqlConnectionString { get; set; } |
||||||
|
public string PostgreSqlConnectionString { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Models |
||||||
|
{ |
||||||
|
public interface IStoredItem<TId> where TId : IEquatable<TId> |
||||||
|
{ |
||||||
|
public TId Id { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
using System; |
||||||
|
using System.ComponentModel.DataAnnotations; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Models |
||||||
|
{ |
||||||
|
public class UserKeyGetRequestModel |
||||||
|
{ |
||||||
|
[Required] |
||||||
|
public string PublicKey { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Models |
||||||
|
{ |
||||||
|
public class UserKeyModel : BaseUserKeyModel, IStoredItem<Guid> |
||||||
|
{ |
||||||
|
public Guid Id { get; set; } |
||||||
|
} |
||||||
|
|
||||||
|
public abstract class BaseUserKeyModel |
||||||
|
{ |
||||||
|
public string Key { get; set; } |
||||||
|
public DateTime CreationDate { get; set; } = DateTime.UtcNow; |
||||||
|
public DateTime? RevisionDate { get; set; } |
||||||
|
public DateTime? LastAccessDate { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
using Bit.CryptoAgent.Services; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Models |
||||||
|
{ |
||||||
|
public class UserKeyRequestModel |
||||||
|
{ |
||||||
|
public string Key { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
namespace Bit.CryptoAgent.Models |
||||||
|
{ |
||||||
|
public class UserKeyResponseModel |
||||||
|
{ |
||||||
|
public string Key { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
using Microsoft.AspNetCore.Hosting; |
||||||
|
using Microsoft.Extensions.Hosting; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent |
||||||
|
{ |
||||||
|
public class Program |
||||||
|
{ |
||||||
|
public static void Main(string[] args) |
||||||
|
{ |
||||||
|
Host |
||||||
|
.CreateDefaultBuilder(args) |
||||||
|
.ConfigureWebHostDefaults(webBuilder => |
||||||
|
{ |
||||||
|
webBuilder.UseStartup<Startup>(); |
||||||
|
}) |
||||||
|
.Build() |
||||||
|
.Run(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
{ |
||||||
|
"iisSettings": { |
||||||
|
"windowsAuthentication": false, |
||||||
|
"anonymousAuthentication": true, |
||||||
|
"iisExpress": { |
||||||
|
"applicationUrl": "http://localhost:10253", |
||||||
|
"sslPort": 0 |
||||||
|
} |
||||||
|
}, |
||||||
|
"profiles": { |
||||||
|
"IIS Express": { |
||||||
|
"commandName": "IISExpress", |
||||||
|
"launchBrowser": true, |
||||||
|
"environmentVariables": { |
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||||
|
} |
||||||
|
}, |
||||||
|
"SsoAgent": { |
||||||
|
"commandName": "Project", |
||||||
|
"dotnetRunMessages": "true", |
||||||
|
"launchBrowser": true, |
||||||
|
"applicationUrl": "http://localhost:5000", |
||||||
|
"environmentVariables": { |
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Repositories |
||||||
|
{ |
||||||
|
public interface IApplicationDataRepository |
||||||
|
{ |
||||||
|
Task<string> ReadSymmetricKeyAsync(); |
||||||
|
Task UpdateSymmetricKeyAsync(string key); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Repositories |
||||||
|
{ |
||||||
|
public interface IRepository<TItem, TId> |
||||||
|
{ |
||||||
|
Task CreateAsync(TItem item); |
||||||
|
Task<TItem> ReadAsync(TId id); |
||||||
|
Task UpdateAsync(TItem item); |
||||||
|
Task DeleteAsync(TId id); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
using Bit.CryptoAgent.Models; |
||||||
|
using System; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Repositories |
||||||
|
{ |
||||||
|
public interface IUserKeyRepository : IRepository<UserKeyModel, Guid> |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
using JsonFlatFileDataStore; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Repositories.JsonFile |
||||||
|
{ |
||||||
|
public class ApplicationDataRepository : IApplicationDataRepository |
||||||
|
{ |
||||||
|
public ApplicationDataRepository(IDataStore dataStore) |
||||||
|
{ |
||||||
|
DataStore = dataStore; |
||||||
|
} |
||||||
|
|
||||||
|
protected IDataStore DataStore { get; private set; } |
||||||
|
|
||||||
|
public Task<string> ReadSymmetricKeyAsync() |
||||||
|
{ |
||||||
|
var item = DataStore.GetItem("symmetricKey"); |
||||||
|
return Task.FromResult(item as string); |
||||||
|
} |
||||||
|
|
||||||
|
public async Task UpdateSymmetricKeyAsync(string key) |
||||||
|
{ |
||||||
|
await DataStore.ReplaceItemAsync("symmetricKey", key, true); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,49 @@ |
|||||||
|
using Bit.CryptoAgent.Models; |
||||||
|
using JsonFlatFileDataStore; |
||||||
|
using System; |
||||||
|
using System.Linq; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Repositories.JsonFile |
||||||
|
{ |
||||||
|
public class Repository<TItem, TId> : IRepository<TItem, TId> |
||||||
|
where TId : IEquatable<TId> |
||||||
|
where TItem : class, IStoredItem<TId> |
||||||
|
{ |
||||||
|
public Repository( |
||||||
|
IDataStore dataStore, |
||||||
|
string collectionName) |
||||||
|
{ |
||||||
|
DataStore = dataStore; |
||||||
|
CollectionName = collectionName; |
||||||
|
} |
||||||
|
|
||||||
|
protected IDataStore DataStore { get; private set; } |
||||||
|
protected string CollectionName { get; private set; } |
||||||
|
|
||||||
|
public virtual async Task CreateAsync(TItem item) |
||||||
|
{ |
||||||
|
var collection = DataStore.GetCollection<TItem>(CollectionName); |
||||||
|
await collection.InsertOneAsync(item); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Task<TItem> ReadAsync(TId id) |
||||||
|
{ |
||||||
|
var collection = DataStore.GetCollection<TItem>(CollectionName); |
||||||
|
var item = collection.AsQueryable().FirstOrDefault(i => i.Id.Equals(id)); |
||||||
|
return Task.FromResult(item); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task UpdateAsync(TItem item) |
||||||
|
{ |
||||||
|
var collection = DataStore.GetCollection<TItem>(CollectionName); |
||||||
|
await collection.ReplaceOneAsync(item.Id, item); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task DeleteAsync(TId id) |
||||||
|
{ |
||||||
|
var collection = DataStore.GetCollection<TItem>(CollectionName); |
||||||
|
await collection.DeleteOneAsync(id); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,37 @@ |
|||||||
|
using Bit.CryptoAgent.Models; |
||||||
|
using JsonFlatFileDataStore; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Repositories.JsonFile |
||||||
|
{ |
||||||
|
public class UserKeyRepository : Repository<UserKeyModel, Guid>, IUserKeyRepository |
||||||
|
{ |
||||||
|
public UserKeyRepository(IDataStore dataStore) |
||||||
|
: base(dataStore, "userKey") |
||||||
|
{ } |
||||||
|
|
||||||
|
public override async Task CreateAsync(UserKeyModel item) |
||||||
|
{ |
||||||
|
var collection = DataStore.GetCollection<JsonUserKeyModel>(CollectionName); |
||||||
|
await collection.InsertOneAsync(new JsonUserKeyModel(item)); |
||||||
|
} |
||||||
|
|
||||||
|
// New model is required since JsonFlatFileDataStore doesn't handle Guid id types |
||||||
|
public class JsonUserKeyModel : BaseUserKeyModel |
||||||
|
{ |
||||||
|
public JsonUserKeyModel() { } |
||||||
|
|
||||||
|
public JsonUserKeyModel(UserKeyModel model) |
||||||
|
{ |
||||||
|
Id = model.Id.ToString(); |
||||||
|
Key = model.Key; |
||||||
|
CreationDate = model.CreationDate; |
||||||
|
RevisionDate = model.RevisionDate; |
||||||
|
LastAccessDate = model.LastAccessDate; |
||||||
|
} |
||||||
|
|
||||||
|
public string Id { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
using Azure.Identity; |
||||||
|
using Azure.Security.KeyVault.Certificates; |
||||||
|
using Azure.Security.KeyVault.Secrets; |
||||||
|
using System; |
||||||
|
using System.Security.Cryptography.X509Certificates; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public class AzureKeyVaultCertificateProviderService : ICertificateProviderService |
||||||
|
{ |
||||||
|
private readonly CryptoAgentSettings _settings; |
||||||
|
|
||||||
|
public AzureKeyVaultCertificateProviderService(CryptoAgentSettings settings) |
||||||
|
{ |
||||||
|
_settings = settings; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<X509Certificate2> GetCertificateAsync() |
||||||
|
{ |
||||||
|
var credential = new ClientSecretCredential(_settings.Certificate.AzureKeyvaultAdTenantId, |
||||||
|
_settings.Certificate.AzureKeyvaultAdAppId, _settings.Certificate.AzureKeyvaultAdSecret); |
||||||
|
var keyVaultUri = new Uri(_settings.Certificate.AzureKeyvaultUri); |
||||||
|
|
||||||
|
var certificateClient = new CertificateClient(keyVaultUri, credential); |
||||||
|
var certificateResponse = await certificateClient.GetCertificateAsync( |
||||||
|
_settings.Certificate.AzureKeyvaultCertificateName); |
||||||
|
var certificate = certificateResponse.Value; |
||||||
|
if (certificate.Policy?.Exportable == true && certificate.Policy?.KeyType == CertificateKeyType.Rsa) |
||||||
|
{ |
||||||
|
var secretName = ParseSecretName(certificate.SecretId); |
||||||
|
var secretClient = new SecretClient(keyVaultUri, credential); |
||||||
|
var secretResponse = await secretClient.GetSecretAsync(secretName); |
||||||
|
var secret = secretResponse.Value; |
||||||
|
if (string.Equals(secret.Properties.ContentType, CertificateContentType.Pkcs12.ToString(), |
||||||
|
StringComparison.InvariantCultureIgnoreCase)) |
||||||
|
{ |
||||||
|
var pfxBytes = Convert.FromBase64String(secret.Value); |
||||||
|
return new X509Certificate2(pfxBytes); |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private string ParseSecretName(Uri secretId) |
||||||
|
{ |
||||||
|
if (secretId.Segments.Length < 3) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException($@"The secret ""{secretId}"" does not contain a valid name."); |
||||||
|
} |
||||||
|
return secretId.Segments[2].TrimEnd('/'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,90 @@ |
|||||||
|
using Azure.Identity; |
||||||
|
using Azure.Security.KeyVault.Keys; |
||||||
|
using Azure.Security.KeyVault.Keys.Cryptography; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public class AzureKeyVaultRsaKeyService : IRsaKeyService |
||||||
|
{ |
||||||
|
private readonly CryptoAgentSettings _settings; |
||||||
|
|
||||||
|
private KeyVaultKey _key; |
||||||
|
private CryptographyClient _cryptographyClient; |
||||||
|
private ClientSecretCredential _credential; |
||||||
|
|
||||||
|
public AzureKeyVaultRsaKeyService( |
||||||
|
CryptoAgentSettings settings) |
||||||
|
{ |
||||||
|
_settings = settings; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> EncryptAsync(byte[] data) |
||||||
|
{ |
||||||
|
var client = await GetCryptographyClientAsync(); |
||||||
|
var result = await client.EncryptAsync(EncryptionAlgorithm.RsaOaep, data); |
||||||
|
return result.Ciphertext; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> DecryptAsync(byte[] data) |
||||||
|
{ |
||||||
|
var client = await GetCryptographyClientAsync(); |
||||||
|
var result = await client.DecryptAsync(EncryptionAlgorithm.RsaOaep, data); |
||||||
|
return result.Plaintext; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> SignAsync(byte[] data) |
||||||
|
{ |
||||||
|
var client = await GetCryptographyClientAsync(); |
||||||
|
var result = await client.SignAsync(SignatureAlgorithm.RS256, data); |
||||||
|
return result.Signature; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<bool> VerifyAsync(byte[] data, byte[] signature) |
||||||
|
{ |
||||||
|
var client = await GetCryptographyClientAsync(); |
||||||
|
var result = await client.VerifyDataAsync(SignatureAlgorithm.RS256, data, signature); |
||||||
|
return result.IsValid; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> GetPublicKeyAsync() |
||||||
|
{ |
||||||
|
var key = await GetKeyAsync(); |
||||||
|
return key.Key.ToRSA().ExportRSAPublicKey(); |
||||||
|
} |
||||||
|
|
||||||
|
private async Task<CryptographyClient> GetCryptographyClientAsync() |
||||||
|
{ |
||||||
|
if (_cryptographyClient == null) |
||||||
|
{ |
||||||
|
var key = await GetKeyAsync(); |
||||||
|
var credential = GetCredential(); |
||||||
|
_cryptographyClient = new CryptographyClient(key.Id, credential); |
||||||
|
} |
||||||
|
return _cryptographyClient; |
||||||
|
} |
||||||
|
|
||||||
|
private async Task<KeyVaultKey> GetKeyAsync() |
||||||
|
{ |
||||||
|
if (_key == null) |
||||||
|
{ |
||||||
|
var credential = GetCredential(); |
||||||
|
var keyVaultUri = new Uri(_settings.RsaKey.AzureKeyvaultUri); |
||||||
|
var keyClient = new KeyClient(keyVaultUri, credential); |
||||||
|
_key = await keyClient.GetKeyAsync(_settings.RsaKey.AzureKeyvaultKeyName); |
||||||
|
} |
||||||
|
return _key; |
||||||
|
} |
||||||
|
|
||||||
|
private ClientSecretCredential GetCredential() |
||||||
|
{ |
||||||
|
if (_credential == null) |
||||||
|
{ |
||||||
|
_credential = new ClientSecretCredential(_settings.RsaKey.AzureKeyvaultAdTenantId, |
||||||
|
_settings.RsaKey.AzureKeyvaultAdAppId, _settings.RsaKey.AzureKeyvaultAdSecret); |
||||||
|
} |
||||||
|
return _credential; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
using Azure.Storage.Blobs; |
||||||
|
using System.IO; |
||||||
|
using System.Security.Cryptography.X509Certificates; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public class AzureStorageCertificateProviderService : ICertificateProviderService |
||||||
|
{ |
||||||
|
private readonly CryptoAgentSettings _settings; |
||||||
|
|
||||||
|
public AzureStorageCertificateProviderService(CryptoAgentSettings settings) |
||||||
|
{ |
||||||
|
_settings = settings; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<X509Certificate2> GetCertificateAsync() |
||||||
|
{ |
||||||
|
var container = new BlobContainerClient(_settings.Certificate.AzureStorageConnectionString, |
||||||
|
_settings.Certificate.AzureStorageContainer); |
||||||
|
await container.CreateIfNotExistsAsync(); |
||||||
|
var blobClient = container.GetBlobClient(_settings.Certificate.AzureStorageFileName); |
||||||
|
if (await blobClient.ExistsAsync()) |
||||||
|
{ |
||||||
|
using var stream = new MemoryStream(); |
||||||
|
await blobClient.DownloadToAsync(stream); |
||||||
|
return new X509Certificate2(stream.ToArray(), _settings.Certificate.AzureStorageFilePassword); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,99 @@ |
|||||||
|
using System; |
||||||
|
using System.Security.Cryptography; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public class CryptoFunctionService : ICryptoFunctionService |
||||||
|
{ |
||||||
|
public async Task<byte[]> AesGcmEncryptAsync(byte[] data, byte[] key) |
||||||
|
{ |
||||||
|
using var aes = new AesGcm(key); |
||||||
|
var iv = await GetRandomBytesAsync(AesGcm.NonceByteSizes.MaxSize); |
||||||
|
var tag = new byte[AesGcm.TagByteSizes.MaxSize]; |
||||||
|
var encData = new byte[data.Length]; |
||||||
|
|
||||||
|
aes.Encrypt(iv, data, encData, tag); |
||||||
|
|
||||||
|
var encResult = new byte[encData.Length + tag.Length + iv.Length]; |
||||||
|
encData.CopyTo(encResult, 0); |
||||||
|
tag.CopyTo(encResult, encData.Length); |
||||||
|
iv.CopyTo(encResult, encData.Length + tag.Length); |
||||||
|
|
||||||
|
return encResult; |
||||||
|
} |
||||||
|
|
||||||
|
public Task<byte[]> AesGcmDecryptAsync(byte[] data, byte[] key) |
||||||
|
{ |
||||||
|
using var aes = new AesGcm(key); |
||||||
|
var endDataLength = data.Length - AesGcm.TagByteSizes.MaxSize - AesGcm.NonceByteSizes.MaxSize; |
||||||
|
var encData = new ArraySegment<byte>(data, 0, endDataLength); |
||||||
|
var tag = new ArraySegment<byte>(data, endDataLength, AesGcm.TagByteSizes.MaxSize); |
||||||
|
var iv = new ArraySegment<byte>(data, endDataLength + AesGcm.TagByteSizes.MaxSize, AesGcm.NonceByteSizes.MaxSize); |
||||||
|
var plainData = new byte[endDataLength]; |
||||||
|
|
||||||
|
aes.Decrypt(iv, encData, tag, plainData); |
||||||
|
|
||||||
|
return Task.FromResult(plainData); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<byte[]> RsaEncryptAsync(byte[] data, byte[] publicKey) |
||||||
|
{ |
||||||
|
using var rsa = RSA.Create(); |
||||||
|
rsa.ImportSubjectPublicKeyInfo(publicKey, out var bytesRead); |
||||||
|
return RsaEncryptAsync(data, rsa); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<byte[]> RsaEncryptAsync(byte[] data, RSA publicKey) |
||||||
|
{ |
||||||
|
var encData = publicKey.Encrypt(data, RSAEncryptionPadding.OaepSHA1); |
||||||
|
return Task.FromResult(encData); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<byte[]> RsaDecryptAsync(byte[] data, byte[] privateKey) |
||||||
|
{ |
||||||
|
using var rsa = RSA.Create(); |
||||||
|
rsa.ImportPkcs8PrivateKey(privateKey, out var bytesRead); |
||||||
|
return RsaDecryptAsync(data, rsa); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<byte[]> RsaDecryptAsync(byte[] data, RSA privateKey) |
||||||
|
{ |
||||||
|
var encData = privateKey.Decrypt(data, RSAEncryptionPadding.OaepSHA1); |
||||||
|
return Task.FromResult(encData); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<bool> RsaVerifyAsync(byte[] data, byte[] signature, byte[] publicKey) |
||||||
|
{ |
||||||
|
using var rsa = RSA.Create(); |
||||||
|
rsa.ImportSubjectPublicKeyInfo(publicKey, out var bytesRead); |
||||||
|
return RsaVerifyAsync(data, signature, rsa); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<bool> RsaVerifyAsync(byte[] data, byte[] signature, RSA publicKey) |
||||||
|
{ |
||||||
|
var valid = publicKey.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); |
||||||
|
return Task.FromResult(valid); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<byte[]> RsaSignAsync(byte[] data, byte[] privateKey) |
||||||
|
{ |
||||||
|
using var rsa = RSA.Create(); |
||||||
|
rsa.ImportPkcs8PrivateKey(privateKey, out var bytesRead); |
||||||
|
return RsaSignAsync(data, rsa); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<byte[]> RsaSignAsync(byte[] data, RSA privateKey) |
||||||
|
{ |
||||||
|
var signature = privateKey.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); |
||||||
|
return Task.FromResult(signature); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<byte[]> GetRandomBytesAsync(int size) |
||||||
|
{ |
||||||
|
var bytes = new byte[size]; |
||||||
|
RandomNumberGenerator.Fill(bytes); |
||||||
|
return Task.FromResult(bytes); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,162 @@ |
|||||||
|
using Bit.CryptoAgent.Repositories; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public class CryptoService : ICryptoService |
||||||
|
{ |
||||||
|
private readonly IRsaKeyService _rsaKeyService; |
||||||
|
private readonly ICryptoFunctionService _cryptoFunctionService; |
||||||
|
private readonly IApplicationDataRepository _applicationDataRepository; |
||||||
|
|
||||||
|
private byte[] _symmetricKey; |
||||||
|
|
||||||
|
public CryptoService( |
||||||
|
IRsaKeyService rsaKeyService, |
||||||
|
ICryptoFunctionService cryptoFunctionService, |
||||||
|
IApplicationDataRepository applicationDataRepository) |
||||||
|
{ |
||||||
|
_rsaKeyService = rsaKeyService; |
||||||
|
_cryptoFunctionService = cryptoFunctionService; |
||||||
|
_applicationDataRepository = applicationDataRepository; |
||||||
|
} |
||||||
|
|
||||||
|
// AES Decrypt |
||||||
|
|
||||||
|
public async Task<byte[]> AesDecryptAsync(byte[] data, byte[] key = null) |
||||||
|
{ |
||||||
|
if (data == null) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
if (key == null) |
||||||
|
{ |
||||||
|
key = await GetSymmetricKeyAsync(); |
||||||
|
} |
||||||
|
var plainData = await _cryptoFunctionService.AesGcmDecryptAsync(data, key); |
||||||
|
return plainData; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<string> AesDecryptToB64Async(byte[] data, byte[] key = null) |
||||||
|
{ |
||||||
|
var plainData = await AesDecryptAsync(data, key); |
||||||
|
return Convert.ToBase64String(plainData); |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> AesDecryptAsync(string b64Data, byte[] key = null) |
||||||
|
{ |
||||||
|
var data = Convert.FromBase64String(b64Data); |
||||||
|
var plainData = await AesDecryptAsync(data, key); |
||||||
|
return plainData; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<string> AesDecryptToB64Async(string b64Data, byte[] key = null) |
||||||
|
{ |
||||||
|
var data = Convert.FromBase64String(b64Data); |
||||||
|
var plainData = await AesDecryptToB64Async(data, key); |
||||||
|
return plainData; |
||||||
|
} |
||||||
|
|
||||||
|
// AES Encrypt |
||||||
|
|
||||||
|
public async Task<byte[]> AesEncryptAsync(byte[] data, byte[] key = null) |
||||||
|
{ |
||||||
|
if (data == null) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
if (key == null) |
||||||
|
{ |
||||||
|
key = await GetSymmetricKeyAsync(); |
||||||
|
} |
||||||
|
var encData = await _cryptoFunctionService.AesGcmEncryptAsync(data, key); |
||||||
|
return encData; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> AesEncryptAsync(string b64Data, byte[] key = null) |
||||||
|
{ |
||||||
|
var data = Convert.FromBase64String(b64Data); |
||||||
|
var encData = await AesEncryptAsync(data, key); |
||||||
|
return encData; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<string> AesEncryptToB64Async(byte[] data, byte[] key = null) |
||||||
|
{ |
||||||
|
var encData = await AesEncryptAsync(data, key); |
||||||
|
return Convert.ToBase64String(encData); |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<string> AesEncryptToB64Async(string b64Data, byte[] key = null) |
||||||
|
{ |
||||||
|
var encData = await AesEncryptAsync(b64Data, key); |
||||||
|
return Convert.ToBase64String(encData); |
||||||
|
} |
||||||
|
|
||||||
|
// RSA Encrypt |
||||||
|
|
||||||
|
public async Task<byte[]> RsaEncryptAsync(byte[] data, byte[] publicKey = null) |
||||||
|
{ |
||||||
|
if (data == null) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
if (publicKey == null) |
||||||
|
{ |
||||||
|
return await _rsaKeyService.EncryptAsync(data); |
||||||
|
} |
||||||
|
var encData = await _cryptoFunctionService.RsaEncryptAsync(data, publicKey); |
||||||
|
return encData; |
||||||
|
} |
||||||
|
|
||||||
|
// RSA Decrypt |
||||||
|
|
||||||
|
public async Task<byte[]> RsaDecryptAsync(byte[] data) |
||||||
|
{ |
||||||
|
if (data == null) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
return await _rsaKeyService.DecryptAsync(data); |
||||||
|
} |
||||||
|
|
||||||
|
// RSA Verify |
||||||
|
|
||||||
|
public async Task<bool> RsaVerifyAsync(byte[] data, byte[] signature, byte[] publicKey = null) |
||||||
|
{ |
||||||
|
if (data == null || signature == null) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (publicKey == null) |
||||||
|
{ |
||||||
|
return await _rsaKeyService.VerifyAsync(data, signature); |
||||||
|
} |
||||||
|
return await _cryptoFunctionService.RsaVerifyAsync(data, signature, publicKey); |
||||||
|
} |
||||||
|
|
||||||
|
// Helpers |
||||||
|
|
||||||
|
private async Task<byte[]> GetSymmetricKeyAsync() |
||||||
|
{ |
||||||
|
if (_symmetricKey == null) |
||||||
|
{ |
||||||
|
var encKey = await _applicationDataRepository.ReadSymmetricKeyAsync(); |
||||||
|
if (encKey != null) |
||||||
|
{ |
||||||
|
var decodedEncKey = Convert.FromBase64String(encKey); |
||||||
|
_symmetricKey = await RsaDecryptAsync(decodedEncKey); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
_symmetricKey = await _cryptoFunctionService.GetRandomBytesAsync(32); |
||||||
|
var decodedEncKey = await RsaEncryptAsync(_symmetricKey); |
||||||
|
encKey = Convert.ToBase64String(decodedEncKey); |
||||||
|
await _applicationDataRepository.UpdateSymmetricKeyAsync(encKey); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return _symmetricKey; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
using System.Security.Cryptography.X509Certificates; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public class FilesystemCertificateProviderService : ICertificateProviderService |
||||||
|
{ |
||||||
|
private readonly CryptoAgentSettings _settings; |
||||||
|
|
||||||
|
public FilesystemCertificateProviderService(CryptoAgentSettings settings) |
||||||
|
{ |
||||||
|
_settings = settings; |
||||||
|
} |
||||||
|
|
||||||
|
public Task<X509Certificate2> GetCertificateAsync() |
||||||
|
{ |
||||||
|
var cert = new X509Certificate2(_settings.Certificate.FilesystemPath, |
||||||
|
_settings.Certificate.FilesystemPassword); |
||||||
|
return Task.FromResult(cert); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
using System.Security.Cryptography.X509Certificates; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public interface ICertificateProviderService |
||||||
|
{ |
||||||
|
Task<X509Certificate2> GetCertificateAsync(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
using System.Security.Cryptography; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public interface ICryptoFunctionService |
||||||
|
{ |
||||||
|
Task<byte[]> AesGcmDecryptAsync(byte[] data, byte[] key); |
||||||
|
Task<byte[]> AesGcmEncryptAsync(byte[] data, byte[] key); |
||||||
|
Task<byte[]> RsaDecryptAsync(byte[] data, byte[] privateKey); |
||||||
|
Task<byte[]> RsaDecryptAsync(byte[] data, RSA privateKey); |
||||||
|
Task<byte[]> RsaEncryptAsync(byte[] data, byte[] publicKey); |
||||||
|
Task<byte[]> RsaEncryptAsync(byte[] data, RSA publicKey); |
||||||
|
Task<byte[]> RsaSignAsync(byte[] data, byte[] privateKey); |
||||||
|
Task<byte[]> RsaSignAsync(byte[] data, RSA privateKey); |
||||||
|
Task<bool> RsaVerifyAsync(byte[] data, byte[] signature, byte[] publicKey); |
||||||
|
Task<bool> RsaVerifyAsync(byte[] data, byte[] signature, RSA publicKey); |
||||||
|
Task<byte[]> GetRandomBytesAsync(int size); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public interface ICryptoService |
||||||
|
{ |
||||||
|
Task<byte[]> AesDecryptAsync(byte[] data, byte[] key = null); |
||||||
|
Task<byte[]> AesDecryptAsync(string b64Data, byte[] key = null); |
||||||
|
Task<string> AesDecryptToB64Async(byte[] data, byte[] key = null); |
||||||
|
Task<string> AesDecryptToB64Async(string b64Data, byte[] key = null); |
||||||
|
Task<byte[]> AesEncryptAsync(byte[] data, byte[] key = null); |
||||||
|
Task<byte[]> AesEncryptAsync(string b64Data, byte[] key = null); |
||||||
|
Task<string> AesEncryptToB64Async(byte[] data, byte[] key = null); |
||||||
|
Task<string> AesEncryptToB64Async(string b64Data, byte[] key = null); |
||||||
|
Task<byte[]> RsaEncryptAsync(byte[] data, byte[] publicKey = null); |
||||||
|
Task<byte[]> RsaDecryptAsync(byte[] data); |
||||||
|
Task<bool> RsaVerifyAsync(byte[] data, byte[] signature, byte[] publicKey = null); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public interface IRsaKeyService |
||||||
|
{ |
||||||
|
Task<byte[]> DecryptAsync(byte[] data); |
||||||
|
Task<byte[]> EncryptAsync(byte[] data); |
||||||
|
Task<byte[]> SignAsync(byte[] data); |
||||||
|
Task<bool> VerifyAsync(byte[] data, byte[] signature); |
||||||
|
Task<byte[]> GetPublicKeyAsync(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,81 @@ |
|||||||
|
using System.Security.Cryptography.X509Certificates; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public class LocalCertificateRsaKeyService : IRsaKeyService |
||||||
|
{ |
||||||
|
private readonly ICertificateProviderService _certificateProviderService; |
||||||
|
private readonly ICryptoFunctionService _cryptoFunctionService; |
||||||
|
|
||||||
|
private X509Certificate2 _certificate; |
||||||
|
|
||||||
|
public LocalCertificateRsaKeyService( |
||||||
|
ICertificateProviderService certificateProviderService, |
||||||
|
ICryptoFunctionService cryptoFunctionService) |
||||||
|
{ |
||||||
|
_certificateProviderService = certificateProviderService; |
||||||
|
_cryptoFunctionService = cryptoFunctionService; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> EncryptAsync(byte[] data) |
||||||
|
{ |
||||||
|
if (data == null) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
var encData = await _cryptoFunctionService.RsaEncryptAsync(data, await GetPublicKeyAsync()); |
||||||
|
return encData; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> DecryptAsync(byte[] data) |
||||||
|
{ |
||||||
|
if (data == null) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
var plainData = await _cryptoFunctionService.RsaDecryptAsync(data, await GetPrivateKeyAsync()); |
||||||
|
return plainData; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> SignAsync(byte[] data) |
||||||
|
{ |
||||||
|
if (data == null) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
return await _cryptoFunctionService.RsaSignAsync(data, await GetPrivateKeyAsync()); |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<bool> VerifyAsync(byte[] data, byte[] signature) |
||||||
|
{ |
||||||
|
if (data == null || signature == null) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
return await _cryptoFunctionService.RsaVerifyAsync(data, signature, await GetPublicKeyAsync()); |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<byte[]> GetPublicKeyAsync() |
||||||
|
{ |
||||||
|
var certificate = await GetCertificateAsync(); |
||||||
|
return certificate.GetRSAPublicKey().ExportRSAPublicKey(); |
||||||
|
} |
||||||
|
|
||||||
|
private async Task<X509Certificate2> GetCertificateAsync() |
||||||
|
{ |
||||||
|
if (_certificate == null) |
||||||
|
{ |
||||||
|
_certificate = await _certificateProviderService.GetCertificateAsync(); |
||||||
|
} |
||||||
|
|
||||||
|
return _certificate; |
||||||
|
} |
||||||
|
|
||||||
|
private async Task<System.Security.Cryptography.RSA> GetPrivateKeyAsync() |
||||||
|
{ |
||||||
|
var certificate = await GetCertificateAsync(); |
||||||
|
return certificate.GetRSAPrivateKey(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,38 @@ |
|||||||
|
using System.Security.Cryptography.X509Certificates; |
||||||
|
using System.Text.RegularExpressions; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent.Services |
||||||
|
{ |
||||||
|
public class StoreCertificateProviderService : ICertificateProviderService |
||||||
|
{ |
||||||
|
private readonly CryptoAgentSettings _settings; |
||||||
|
|
||||||
|
public StoreCertificateProviderService(CryptoAgentSettings settings) |
||||||
|
{ |
||||||
|
_settings = settings; |
||||||
|
} |
||||||
|
|
||||||
|
public Task<X509Certificate2> GetCertificateAsync() |
||||||
|
{ |
||||||
|
X509Certificate2 cert = null; |
||||||
|
var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); |
||||||
|
certStore.Open(OpenFlags.ReadOnly); |
||||||
|
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, |
||||||
|
CleanThumbprint(_settings.Certificate.StoreThumbprint), false); |
||||||
|
if (certCollection.Count > 0) |
||||||
|
{ |
||||||
|
cert = certCollection[0]; |
||||||
|
} |
||||||
|
certStore.Close(); |
||||||
|
return Task.FromResult(cert); |
||||||
|
} |
||||||
|
|
||||||
|
public static string CleanThumbprint(string thumbprint) |
||||||
|
{ |
||||||
|
// Clean possible garbage characters from thumbprint copy/paste |
||||||
|
// ref http://stackoverflow.com/questions/8448147/problems-with-x509store-certificates-find-findbythumbprint |
||||||
|
return Regex.Replace(thumbprint, @"[^\da-fA-F]", string.Empty).ToUpper(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,104 @@ |
|||||||
|
using Bit.CryptoAgent.Repositories; |
||||||
|
using Bit.CryptoAgent.Services; |
||||||
|
using JsonFlatFileDataStore; |
||||||
|
using Microsoft.AspNetCore.Builder; |
||||||
|
using Microsoft.AspNetCore.Hosting; |
||||||
|
using Microsoft.Extensions.Configuration; |
||||||
|
using Microsoft.Extensions.DependencyInjection; |
||||||
|
using Microsoft.Extensions.Hosting; |
||||||
|
using System; |
||||||
|
using System.Globalization; |
||||||
|
|
||||||
|
namespace Bit.CryptoAgent |
||||||
|
{ |
||||||
|
public class Startup |
||||||
|
{ |
||||||
|
public Startup(IWebHostEnvironment env, IConfiguration configuration) |
||||||
|
{ |
||||||
|
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US"); |
||||||
|
Configuration = configuration; |
||||||
|
Environment = env; |
||||||
|
} |
||||||
|
|
||||||
|
public IConfiguration Configuration { get; } |
||||||
|
public IWebHostEnvironment Environment { get; set; } |
||||||
|
|
||||||
|
public void ConfigureServices(IServiceCollection services) |
||||||
|
{ |
||||||
|
var settings = new CryptoAgentSettings(); |
||||||
|
ConfigurationBinder.Bind(Configuration.GetSection("CryptoAgentSettings"), settings); |
||||||
|
services.AddSingleton(s => settings); |
||||||
|
|
||||||
|
var rsaKeyProvider = settings.RsaKey.Provider?.ToLowerInvariant(); |
||||||
|
if (rsaKeyProvider == "certificate") |
||||||
|
{ |
||||||
|
services.AddSingleton<IRsaKeyService, LocalCertificateRsaKeyService>(); |
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(settings.Certificate?.StoreThumbprint)) |
||||||
|
{ |
||||||
|
services.AddSingleton<ICertificateProviderService, StoreCertificateProviderService>(); |
||||||
|
} |
||||||
|
else if (!string.IsNullOrWhiteSpace(settings.Certificate?.FilesystemPath)) |
||||||
|
{ |
||||||
|
services.AddSingleton<ICertificateProviderService, FilesystemCertificateProviderService>(); |
||||||
|
} |
||||||
|
else if (!string.IsNullOrWhiteSpace(settings.Certificate?.AzureStorageConnectionString)) |
||||||
|
{ |
||||||
|
services.AddSingleton<ICertificateProviderService, AzureStorageCertificateProviderService>(); |
||||||
|
} |
||||||
|
else if (!string.IsNullOrWhiteSpace(settings.Certificate?.AzureKeyvaultUri)) |
||||||
|
{ |
||||||
|
services.AddSingleton<ICertificateProviderService, AzureKeyVaultCertificateProviderService>(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
throw new Exception("No certificate provider configured."); |
||||||
|
} |
||||||
|
} |
||||||
|
else if (rsaKeyProvider == "azure") |
||||||
|
{ |
||||||
|
if (!string.IsNullOrWhiteSpace(settings.RsaKey?.AzureKeyvaultUri)) |
||||||
|
{ |
||||||
|
services.AddSingleton<IRsaKeyService, AzureKeyVaultRsaKeyService>(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
throw new Exception("No azure key vault configured."); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
throw new Exception("Unknown rsa key provider."); |
||||||
|
} |
||||||
|
|
||||||
|
services.AddSingleton<ICryptoFunctionService, CryptoFunctionService>(); |
||||||
|
services.AddSingleton<ICryptoService, CryptoService>(); |
||||||
|
|
||||||
|
// JsonFlatFileDataStore |
||||||
|
if (!string.IsNullOrWhiteSpace(settings.Database?.JsonFilePath)) |
||||||
|
{ |
||||||
|
// Assign foobar to keyProperty in order to not use incrementing Id functionality |
||||||
|
services.AddSingleton<IDataStore>(new DataStore(settings.Database.JsonFilePath, keyProperty: "--foobar--")); |
||||||
|
services.AddSingleton<IApplicationDataRepository, Repositories.JsonFile.ApplicationDataRepository>(); |
||||||
|
services.AddSingleton<IUserKeyRepository, Repositories.JsonFile.UserKeyRepository>(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
throw new Exception("No database configured."); |
||||||
|
} |
||||||
|
|
||||||
|
services.AddControllers(); |
||||||
|
} |
||||||
|
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
||||||
|
{ |
||||||
|
if (env.IsDevelopment()) |
||||||
|
{ |
||||||
|
app.UseDeveloperExceptionPage(); |
||||||
|
} |
||||||
|
|
||||||
|
app.UseRouting(); |
||||||
|
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
{ |
||||||
|
"Logging": { |
||||||
|
"LogLevel": { |
||||||
|
"Default": "Information", |
||||||
|
"Microsoft": "Warning", |
||||||
|
"Microsoft.Hosting.Lifetime": "Information" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue