1
0
Форкнуть 0

Port kotlin SDK to new Android project

This commit is contained in:
Andreas Mikolajewski 2020-02-05 18:48:09 -08:00
Родитель 3d8b57d164
Коммит bd432c2224
359 изменённых файлов: 3121 добавлений и 162488 удалений

38
.github/ISSUE_TEMPLATE/bug_report.md поставляемый
Просмотреть файл

@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

20
.github/ISSUE_TEMPLATE/feature_request.md поставляемый
Просмотреть файл

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

88
.gitignore поставляемый
Просмотреть файл

@ -1,76 +1,14 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# vs code settings
.vscode
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
dist
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
# distribution folder
dist
# nunit test results
nunitresults.xml
# macOS metadata files
.DS_Store
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx

1
.idea/.name Normal file
Просмотреть файл

@ -0,0 +1 @@
PortableIdentityCard-ClientSDK-Android

Просмотреть файл

@ -0,0 +1,122 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

Просмотреть файл

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

19
.idea/gradle.xml Normal file
Просмотреть файл

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/PortableIdentityCard-ClientSDK" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
<option name="testRunner" value="PLATFORM" />
</GradleProjectSettings>
</option>
</component>
</project>

9
.idea/misc.xml Normal file
Просмотреть файл

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

Просмотреть файл

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

6
.idea/vcs.xml Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

30
.vscode/launch.json поставляемый
Просмотреть файл

@ -1,30 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Jasmine - Current file",
"program": "${workspaceFolder}/node_modules/jasmine-ts/lib/index",
"args": ["${file}"],
"env": {
"JASMINE_CONFIG_PATH": "${workspaceFolder}/tests/jasmine.json"
},
"console": "internalConsole"
},
{
"type": "node",
"request": "launch",
"name": "Unit Test",
"program": "${workspaceFolder}/node_modules/jasmine-ts/lib/index.js",
"args": [
"--config=tests/jasmine.json"
],
"console": "internalConsole",
"smartStep": false
}
]
}

6
.vscode/settings.json поставляемый
Просмотреть файл

@ -1,6 +0,0 @@
{
"git.ignoreLimitWarning": true,
"editor.detectIndentation": false,
"editor.tabSize": 2,
"prettier.singleQuote": true
}

Просмотреть файл

@ -1,4 +0,0 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners.
* @codeglobally

Просмотреть файл

@ -1,61 +0,0 @@
# Coding Guidelines
We follow a modified form of the [StandardJS](https://standardjs.com/) style guide. Additions and modifications have been made on this styleguide that will be aparent from linter output and our existing repos.
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
## Indentation & Lines
* We use spaces (2 to be exact), not tabs
* Lines must not be over 160 characters
* Lines must end in semicolons
## Names
* Use PascalCase for `type` names
* Use PascalCase for `enum` values
* Use camelCase for `function` and `method` names
* Use camelCase for `property` names and `local variables`
* Use whole words in names when possible
* Avoid prefixing names with underscore
## Types
* Do not export `types` or `functions` unless you need to share it across multiple components
* Do not introduce new `types` or `values` to the global namespace
## Comments
* Use JSDoc style comments for `functions`, `interfaces`, `enums`, and `classes`
* Favour clarity over brevity
## Strings
* Use "double quotes" for strings shown to the user that need to be externalized (localized)
* Use 'single quotes' otherwise
* All strings visible to the user need to be externalized
## Style
* Use arrow functions `=>` over anonymous function expressions
* Only surround arrow function parameters when necessary. For example, `(x) => x + x` is wrong but the following are correct:
``` javascript
x => x + x
(x,y) => x + y
<T>(x: T, y: T) => x === y
```
* Always surround loop and conditional bodies with curly braces
* Open curly braces always go on the same line as whatever necessitates them
* Parenthesized constructs should have no surrounding whitespace. A single space follows commas, colons, and semicolons in those constructs. For example:
``` javascript
for (var i = 0, n = str.length; i < 10; i++) { }
if (x < 10) { }
function f(x: number, y: string): void { }
```
* Keys in objects must remain consistent, but prefer no quotes.
```typescript
{
foo: 1,
bar: 'string',
baz: true
}
{
'foo': 1,
'bar': 'string',
'baz-1': true
}
```

21
LICENSE
Просмотреть файл

@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

1
PortableIdentityCard-ClientSDK/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
/build

Просмотреть файл

@ -0,0 +1,34 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.microsoft.did.sdk"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

21
PortableIdentityCard-ClientSDK/proguard-rules.pro поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Просмотреть файл

@ -0,0 +1,24 @@
package com.microsoft.did.sdk
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.microsoft.did.sdk", appContext.packageName)
}
}

Просмотреть файл

@ -0,0 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.microsoft.did.sdk">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" />
</manifest>

Просмотреть файл

@ -1,45 +1,45 @@
package com.microsoft.did.sdk
import com.microsoft.did.sdk.crypto.plugins.AndroidSubtle
import android.content.Context
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.keyStore.AndroidKeyStore
import com.microsoft.did.sdk.crypto.keyStore.IKeyStore
import com.microsoft.did.sdk.crypto.models.webCryptoApi.SubtleCrypto
import com.microsoft.did.sdk.crypto.models.webCryptoApi.W3cCryptoApiConstants
import com.microsoft.did.sdk.crypto.plugins.EllipticCurveSubtleCrypto
import com.microsoft.did.sdk.crypto.plugins.SubtleCryptoMapItem
import com.microsoft.did.sdk.crypto.plugins.SubtleCryptoScope
import com.microsoft.did.sdk.utilities.ConsoleLogger
import com.microsoft.did.sdk.utilities.ILogger
class Agent private constructor (
registrationUrl: String = defaultRegistrationUrl,
resolverUrl: String = defaultResolverUrl,
cryptoOperations: CryptoOperations,
logger: ILogger): AbstractAgent(
registrationUrl = registrationUrl,
resolverUrl = resolverUrl,
signatureKeyReference = defaultSignatureKeyReference,
encryptionKeyReference = defaultEncryptionKeyReference,
cryptoOperations = cryptoOperations,
logger = logger
) {
companion object {
fun getInstance(context: Context, logger: ILogger = ConsoleLogger()): Agent {
val keyStore = AndroidKeyStore(context, logger)
val subtleCrypto = AndroidSubtle(keyStore, logger)
val crypto = CryptoOperations(subtleCrypto, keyStore, logger)
val ecSubtle = EllipticCurveSubtleCrypto(subtleCrypto, logger)
crypto.subtleCryptoFactory.addMessageSigner(W3cCryptoApiConstants.EcDsa.value,
SubtleCryptoMapItem(ecSubtle, SubtleCryptoScope.All)
);
return Agent(
registrationUrl = "https://beta.core.ion.msidentity.com/",
cryptoOperations = crypto,
logger = logger
)
}
}
}
package com.microsoft.did.sdk
import com.microsoft.did.sdk.crypto.plugins.AndroidSubtle
import android.content.Context
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.keyStore.AndroidKeyStore
import com.microsoft.did.sdk.crypto.keyStore.IKeyStore
import com.microsoft.did.sdk.crypto.models.webCryptoApi.SubtleCrypto
import com.microsoft.did.sdk.crypto.models.webCryptoApi.W3cCryptoApiConstants
import com.microsoft.did.sdk.crypto.plugins.EllipticCurveSubtleCrypto
import com.microsoft.did.sdk.crypto.plugins.SubtleCryptoMapItem
import com.microsoft.did.sdk.crypto.plugins.SubtleCryptoScope
import com.microsoft.did.sdk.utilities.ConsoleLogger
import com.microsoft.did.sdk.utilities.ILogger
class Agent private constructor (
registrationUrl: String = defaultRegistrationUrl,
resolverUrl: String = defaultResolverUrl,
cryptoOperations: CryptoOperations,
logger: ILogger): AbstractAgent(
registrationUrl = registrationUrl,
resolverUrl = resolverUrl,
signatureKeyReference = defaultSignatureKeyReference,
encryptionKeyReference = defaultEncryptionKeyReference,
cryptoOperations = cryptoOperations,
logger = logger
) {
companion object {
fun getInstance(context: Context, logger: ILogger = ConsoleLogger()): Agent {
val keyStore = AndroidKeyStore(context, logger)
val subtleCrypto = AndroidSubtle(keyStore, logger)
val crypto = CryptoOperations(subtleCrypto, keyStore, logger)
val ecSubtle = EllipticCurveSubtleCrypto(subtleCrypto, logger)
crypto.subtleCryptoFactory.addMessageSigner(W3cCryptoApiConstants.EcDsa.value,
SubtleCryptoMapItem(ecSubtle, SubtleCryptoScope.All)
);
return Agent(
registrationUrl = "https://beta.core.ion.msidentity.com/",
cryptoOperations = crypto,
logger = logger
)
}
}
}

Просмотреть файл

@ -1,378 +1,378 @@
package com.microsoft.did.sdk.crypto.keyStore
import android.annotation.TargetApi
import android.content.Context
import android.content.SharedPreferences
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.security.keystore.KeyProtection
import android.util.Base64
import com.microsoft.did.sdk.crypto.keys.*
import com.microsoft.did.sdk.crypto.keys.ellipticCurve.EllipticCurvePrivateKey
import com.microsoft.did.sdk.crypto.keys.ellipticCurve.EllipticCurvePublicKey
import com.microsoft.did.sdk.crypto.keys.rsa.RsaPrivateKey
import com.microsoft.did.sdk.crypto.keys.rsa.RsaPublicKey
import com.microsoft.did.sdk.crypto.models.KeyUse
import com.microsoft.did.sdk.crypto.models.webCryptoApi.JsonWebKey
import com.microsoft.did.sdk.crypto.models.webCryptoApi.KeyUsage
import kotlinx.serialization.json.Json
import java.security.KeyStore
import java.security.interfaces.ECPublicKey
import java.security.interfaces.RSAPublicKey
import javax.crypto.spec.SecretKeySpec
import androidx.security.crypto.EncryptedSharedPreferences
import com.microsoft.did.sdk.utilities.*
import kotlinx.serialization.parse
import javax.crypto.KeyGenerator
class AndroidKeyStore(private val context: Context, logger: ILogger): IKeyStore(logger) {
companion object {
const val provider = "AndroidKeyStore"
private val regexForKeyReference = Regex("^#(.*)\\.[^.]+$")
private val regexForKeyIndex = Regex("^#.*\\.([^.]+$)")
val keyStore: KeyStore = KeyStore.getInstance(provider).apply {
load(null)
}
}
override fun getSecretKey(keyReference: String): KeyContainer<SecretKey> {
val softwareKeys = listSecureData()
val key = softwareKeys[keyReference] ?: throw logger.error("Key $keyReference not found")
if (key.kty != KeyType.Octets) {
throw logger.error("Key $keyReference (type ${key.kty.value}) is not a secret.")
}
return KeyContainer(
kty = key.kty,
keys = key.kids.map{
getSecureSecretkey(it)!!
}
)
}
override fun getPrivateKey(keyReference: String): KeyContainer<PrivateKey> {
val nativeKeys = listNativeKeys()
var key = nativeKeys[keyReference]
if (key != null) {
return KeyContainer(
kty = key.kty,
keys = key.kids.map{
AndroidKeyConverter.androidPrivateKeyToPrivateKey(it, keyStore, logger)
}
)
}
val softwareKeys = listSecureData()
key = softwareKeys[keyReference]
if (key != null) {
return KeyContainer(
kty = key.kty,
keys = key.kids.map{
getSecurePrivateKey(it)!!
}
)
}
throw logger.error("Key $keyReference not found")
}
override fun getPublicKey(keyReference: String): KeyContainer<PublicKey> {
val nativeKeys = listNativeKeys()
var key = nativeKeys[keyReference]
if (key != null) {
return KeyContainer(
kty = key.kty,
keys = key.kids.map {
val entry = keyStore.getCertificate(it).publicKey
?: throw logger.error("Key $it is not a private key.")
AndroidKeyConverter.androidPublicKeyToPublicKey(it, entry, logger)
}
)
}
val softwareKeys = listSecureData()
key = softwareKeys[keyReference]
if (key != null) {
return KeyContainer(
kty = key.kty,
keys = key.kids.map{
getSecurePublicKey(it)!!
}
)
}
throw logger.error("Key $keyReference not found")
}
override fun getSecretKeyById(keyId: String): SecretKey? {
val keyRef = findReferenceInList(this.list(), keyId)
return if (!keyRef.isNullOrBlank()) {
getSecretKey(keyRef).keys.firstOrNull { it.kid == keyId }
} else {
null
}
}
override fun getPrivateKeyById(keyId: String): PrivateKey? {
val nativeKeys = listNativeKeys()
var keyRef = findReferenceInList(nativeKeys, keyId)
if (!keyRef.isNullOrBlank()) { // This keyID exists within the android keystore
return AndroidKeyConverter.androidPrivateKeyToPrivateKey(keyId, keyStore, logger)
}
val softwareKeys = listSecureData()
keyRef = findReferenceInList(softwareKeys, keyId)
if (!keyRef.isNullOrBlank()) {
return getSecurePrivateKey(keyId)
}
return null
}
override fun getPublicKeyById(keyId: String): PublicKey? {
val nativeKeys = listNativeKeys()
var keyRef = findReferenceInList(nativeKeys, keyId)
if (!keyRef.isNullOrBlank()) { // This keyID exists within the android keystore
val entry = keyStore.getCertificate(keyId).publicKey ?: throw logger.error("Key $keyId is not a private key.")
return AndroidKeyConverter.androidPublicKeyToPublicKey(keyId, entry, logger)
}
val softwareKeys = listSecureData()
keyRef = findReferenceInList(softwareKeys, keyId)
if (!keyRef.isNullOrBlank()) {
return getSecurePublicKey(keyId)
}
return null
}
public fun deletePrivateKey(keyId: String) {
val nativeKeys = listNativeKeys()
var keyRef = findReferenceInList(nativeKeys, keyId)
if (!keyRef.isNullOrBlank()) {
AndroidKeyStore.keyStore.deleteEntry(keyId)
return
}
val softwareKeys = listSecureData()
keyRef = findReferenceInList(softwareKeys, keyId)
if (!keyRef.isNullOrBlank()) {
deleteSecureData(keyId)
}
}
private fun findReferenceInList(list: Map<String, KeyStoreListItem>, keyId: String): String? {
return list.filter {
it.value.kids.contains(keyId)
}.entries.firstOrNull()?.key
}
@TargetApi(23)
override fun save(keyReference: String, key: SecretKey) {
val alias = checkOrCreateKeyId(keyReference, key.kid)
var jwk = key.toJWK();
jwk.kid = alias;
val jwkString = MinimalJson.serializer.stringify(JsonWebKey.serializer(), jwk)
val keyValue = stringToByteArray(jwkString)
saveSecureData(alias, keyValue)
}
@TargetApi(23)
override fun save(keyReference: String, key: PrivateKey) {
val alias = checkOrCreateKeyId(keyReference, key.kid)
if (keyStore.containsAlias(alias)) {
// do nothing, the key is already there.
return
}
// This key is not natively supported
var jwk = key.toJWK();
jwk.kid = alias;
val jwkString = MinimalJson.serializer.stringify(JsonWebKey.serializer(), jwk)
val keyValue = stringToByteArray(jwkString)
saveSecureData(alias, keyValue)
}
override fun save(keyReference: String, key: PublicKey) {
val alias = checkOrCreateKeyId(keyReference, key.kid)
if (keyStore.containsAlias(alias)) {
// do nothing, the key is already there.
return
}
throw logger.error("Why are you even saving a public key; this makes no sense. Rethink your life.")
}
override fun list(): Map<String, KeyStoreListItem> {
val nativeList = listNativeKeys()
val softwareList = listSecureData()
return com.microsoft.did.sdk.utilities.Map.join(softwareList, nativeList)
}
private fun listNativeKeys(): Map<String, KeyStoreListItem> {
val output = emptyMap<String, KeyStoreListItem>().toMutableMap()
val aliases = keyStore.aliases()
// KeyRef (as key reference) -> KeyRef.VersionNumber (as key identifier)
for (alias in aliases) {
if (alias.matches(regexForKeyReference)) {
val entry = keyStore.getCertificate(alias)
val matches = regexForKeyReference.matchEntire(alias)
val values = matches!!.groupValues
// Get the keyType associated with this key.
val kty: KeyType = AndroidKeyConverter.whatKeyTypeIs(entry.publicKey, logger)
// Add the key to an ListItem or make a new one
if (output.containsKey(values[1])) {
val listItem = output[values[1]]!!
if (listItem.kty != kty) {
throw logger.error("Key Container ${values[1]} contains keys of two different " +
"types (${listItem.kty.value}, ${kty.value})")
}
listItem.kids.add(alias)
} else {
output[values[1]] = KeyStoreListItem(kty, mutableListOf(alias))
}
}
}
return output
}
private fun listSecureData(): Map<String, KeyStoreListItem> {
val sharedPreferences = getSharedPreferences();
val keys = sharedPreferences.all.keys;
// all stored keys should be in JWT format
val keyMap = mutableMapOf<String, KeyStoreListItem>()
keys.forEach{
// verify that it matches the regex and grab the key reference
val keyReferenceMatch = AndroidKeyStore.regexForKeyReference.matchEntire(it)
if (keyReferenceMatch != null) {
val keyRef = keyReferenceMatch.groupValues[1];
val jwkBase64 = sharedPreferences.getString(it, null)!!
val jwkData = Base64.decode(jwkBase64, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
val key = MinimalJson.serializer.parse(JsonWebKey.serializer(), byteArrayToString(jwkData))
val keyType = toKeyType(key.kty, logger)
if (!keyMap.containsKey(keyRef)) {
keyMap[keyRef] = KeyStoreListItem(keyType, mutableListOf(it))
} else {
val listItem = keyMap[keyRef]!!
if (keyType != listItem.kty) {
throw logger.error("Key $keyRef has two different key types (${keyType.value}, ${listItem.kty.value})")
}
listItem.kids.add(it)
keyMap[keyRef] = listItem
}
}
}
return keyMap
}
private fun getSecurePublicKey(alias: String): PublicKey? {
return getSecurePrivateKey(alias)?.getPublicKey()
}
private fun getSecurePrivateKey(alias: String): PrivateKey? {
val data = getSecureData(alias) ?: return null
val jwk = MinimalJson.serializer.parse(JsonWebKey.serializer(), byteArrayToString(data))
if (jwk.kty == KeyType.RSA.value) {
return RsaPrivateKey(jwk, logger = logger)
} else if (jwk.kty == KeyType.EllipticCurve.value) {
return EllipticCurvePrivateKey(jwk, logger = logger)
} else {
throw logger.error("Unknown key type ${jwk.kty}")
}
}
private fun getSecureSecretkey(alias: String): SecretKey? {
val data = getSecureData(alias) ?: return null
val jwk = MinimalJson.serializer.parse(JsonWebKey.serializer(), byteArrayToString(data))
if (jwk.kty != KeyType.Octets.value) {
throw logger.error("$alias is not a secret key.")
}
return SecretKey(jwk, logger)
}
private fun getSecureData(alias: String): ByteArray? {
val sharedPreferences = getSharedPreferences();
val base64UrlEncodedData = sharedPreferences.getString(alias, null)
if (base64UrlEncodedData != null) {
return Base64.decode(base64UrlEncodedData, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
}
return null
}
private fun deleteSecureData(alias: String) {
val sharedPreferences = getSharedPreferences()
val editor = sharedPreferences.edit()
editor.remove(alias)
editor.apply()
}
private fun saveSecureData(alias: String, data: ByteArray) {
val sharedPreferences = getSharedPreferences();
val editor = sharedPreferences.edit();
editor.putString(alias, Base64.encodeToString(data, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP));
editor.apply()
}
private fun getSharedPreferences(): SharedPreferences {
val masterKeyAlias = getSecretVaultMasterKey()
return EncryptedSharedPreferences.create(
"secret_shared_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
@TargetApi(23)
private fun getSecretVaultMasterKey(): String {
val alias = "ms-useragent-secret-masterkey"
if (!keyStore.containsAlias(alias)) {
// Generate the master key
val generator = KeyGenerator.getInstance("AES", provider)
generator.init(KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(256)
.build())
generator.generateKey();
}
return alias
}
fun checkOrCreateKeyId(keyReference: String, kid: String?): String {
if (!kid.isNullOrBlank() && !kid.startsWith(keyReference) && !kid.startsWith("#$keyReference")) {
throw logger.error("Key ID must begin with key reference")
// This could be relaxed later if we flush keys and use a format of
// KEYREFERENCE.KEYID and ensure KEYID does not contain the dot delimiter
}
return if (!kid.isNullOrBlank()) {
logger.debug("Using key $kid")
kid
} else {
// generate a key id
val listItem = this.list()[keyReference]
if (listItem == null) { // no previous keys
logger.debug("New key reference #$keyReference.1")
"#$keyReference.1"
} else {
// heuristic, find the last digit and count up
var latestVersion = listItem.kids.reduce {
acc: String, current: String ->
val currentValue = regexForKeyIndex.matchEntire(current)?.groupValues?.get(1)?.toInt()
val accValue = acc.toIntOrNull()
if (currentValue != null && accValue == null) {
current
} else if (currentValue != null && accValue != null && currentValue > accValue) {
current
} else {
acc
}
}.toIntOrNull() ?: 0
latestVersion++
logger.debug("New key reference #$keyReference.$latestVersion")
"#$keyReference.$latestVersion"
}
}
}
package com.microsoft.did.sdk.crypto.keyStore
import android.annotation.TargetApi
import android.content.Context
import android.content.SharedPreferences
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.security.keystore.KeyProtection
import android.util.Base64
import com.microsoft.did.sdk.crypto.keys.*
import com.microsoft.did.sdk.crypto.keys.ellipticCurve.EllipticCurvePrivateKey
import com.microsoft.did.sdk.crypto.keys.ellipticCurve.EllipticCurvePublicKey
import com.microsoft.did.sdk.crypto.keys.rsa.RsaPrivateKey
import com.microsoft.did.sdk.crypto.keys.rsa.RsaPublicKey
import com.microsoft.did.sdk.crypto.models.KeyUse
import com.microsoft.did.sdk.crypto.models.webCryptoApi.JsonWebKey
import com.microsoft.did.sdk.crypto.models.webCryptoApi.KeyUsage
import kotlinx.serialization.json.Json
import java.security.KeyStore
import java.security.interfaces.ECPublicKey
import java.security.interfaces.RSAPublicKey
import javax.crypto.spec.SecretKeySpec
import androidx.security.crypto.EncryptedSharedPreferences
import com.microsoft.did.sdk.utilities.*
import kotlinx.serialization.parse
import javax.crypto.KeyGenerator
class AndroidKeyStore(private val context: Context, logger: ILogger): IKeyStore(logger) {
companion object {
const val provider = "AndroidKeyStore"
private val regexForKeyReference = Regex("^#(.*)\\.[^.]+$")
private val regexForKeyIndex = Regex("^#.*\\.([^.]+$)")
val keyStore: KeyStore = KeyStore.getInstance(provider).apply {
load(null)
}
}
override fun getSecretKey(keyReference: String): KeyContainer<SecretKey> {
val softwareKeys = listSecureData()
val key = softwareKeys[keyReference] ?: throw logger.error("Key $keyReference not found")
if (key.kty != KeyType.Octets) {
throw logger.error("Key $keyReference (type ${key.kty.value}) is not a secret.")
}
return KeyContainer(
kty = key.kty,
keys = key.kids.map{
getSecureSecretkey(it)!!
}
)
}
override fun getPrivateKey(keyReference: String): KeyContainer<PrivateKey> {
val nativeKeys = listNativeKeys()
var key = nativeKeys[keyReference]
if (key != null) {
return KeyContainer(
kty = key.kty,
keys = key.kids.map{
AndroidKeyConverter.androidPrivateKeyToPrivateKey(it, keyStore, logger)
}
)
}
val softwareKeys = listSecureData()
key = softwareKeys[keyReference]
if (key != null) {
return KeyContainer(
kty = key.kty,
keys = key.kids.map{
getSecurePrivateKey(it)!!
}
)
}
throw logger.error("Key $keyReference not found")
}
override fun getPublicKey(keyReference: String): KeyContainer<PublicKey> {
val nativeKeys = listNativeKeys()
var key = nativeKeys[keyReference]
if (key != null) {
return KeyContainer(
kty = key.kty,
keys = key.kids.map {
val entry = keyStore.getCertificate(it).publicKey
?: throw logger.error("Key $it is not a private key.")
AndroidKeyConverter.androidPublicKeyToPublicKey(it, entry, logger)
}
)
}
val softwareKeys = listSecureData()
key = softwareKeys[keyReference]
if (key != null) {
return KeyContainer(
kty = key.kty,
keys = key.kids.map{
getSecurePublicKey(it)!!
}
)
}
throw logger.error("Key $keyReference not found")
}
override fun getSecretKeyById(keyId: String): SecretKey? {
val keyRef = findReferenceInList(this.list(), keyId)
return if (!keyRef.isNullOrBlank()) {
getSecretKey(keyRef).keys.firstOrNull { it.kid == keyId }
} else {
null
}
}
override fun getPrivateKeyById(keyId: String): PrivateKey? {
val nativeKeys = listNativeKeys()
var keyRef = findReferenceInList(nativeKeys, keyId)
if (!keyRef.isNullOrBlank()) { // This keyID exists within the android keystore
return AndroidKeyConverter.androidPrivateKeyToPrivateKey(keyId, keyStore, logger)
}
val softwareKeys = listSecureData()
keyRef = findReferenceInList(softwareKeys, keyId)
if (!keyRef.isNullOrBlank()) {
return getSecurePrivateKey(keyId)
}
return null
}
override fun getPublicKeyById(keyId: String): PublicKey? {
val nativeKeys = listNativeKeys()
var keyRef = findReferenceInList(nativeKeys, keyId)
if (!keyRef.isNullOrBlank()) { // This keyID exists within the android keystore
val entry = keyStore.getCertificate(keyId).publicKey ?: throw logger.error("Key $keyId is not a private key.")
return AndroidKeyConverter.androidPublicKeyToPublicKey(keyId, entry, logger)
}
val softwareKeys = listSecureData()
keyRef = findReferenceInList(softwareKeys, keyId)
if (!keyRef.isNullOrBlank()) {
return getSecurePublicKey(keyId)
}
return null
}
public fun deletePrivateKey(keyId: String) {
val nativeKeys = listNativeKeys()
var keyRef = findReferenceInList(nativeKeys, keyId)
if (!keyRef.isNullOrBlank()) {
AndroidKeyStore.keyStore.deleteEntry(keyId)
return
}
val softwareKeys = listSecureData()
keyRef = findReferenceInList(softwareKeys, keyId)
if (!keyRef.isNullOrBlank()) {
deleteSecureData(keyId)
}
}
private fun findReferenceInList(list: Map<String, KeyStoreListItem>, keyId: String): String? {
return list.filter {
it.value.kids.contains(keyId)
}.entries.firstOrNull()?.key
}
@TargetApi(23)
override fun save(keyReference: String, key: SecretKey) {
val alias = checkOrCreateKeyId(keyReference, key.kid)
var jwk = key.toJWK();
jwk.kid = alias;
val jwkString = MinimalJson.serializer.stringify(JsonWebKey.serializer(), jwk)
val keyValue = stringToByteArray(jwkString)
saveSecureData(alias, keyValue)
}
@TargetApi(23)
override fun save(keyReference: String, key: PrivateKey) {
val alias = checkOrCreateKeyId(keyReference, key.kid)
if (keyStore.containsAlias(alias)) {
// do nothing, the key is already there.
return
}
// This key is not natively supported
var jwk = key.toJWK();
jwk.kid = alias;
val jwkString = MinimalJson.serializer.stringify(JsonWebKey.serializer(), jwk)
val keyValue = stringToByteArray(jwkString)
saveSecureData(alias, keyValue)
}
override fun save(keyReference: String, key: PublicKey) {
val alias = checkOrCreateKeyId(keyReference, key.kid)
if (keyStore.containsAlias(alias)) {
// do nothing, the key is already there.
return
}
throw logger.error("Why are you even saving a public key; this makes no sense. Rethink your life.")
}
override fun list(): Map<String, KeyStoreListItem> {
val nativeList = listNativeKeys()
val softwareList = listSecureData()
return com.microsoft.did.sdk.utilities.Map.join(softwareList, nativeList)
}
private fun listNativeKeys(): Map<String, KeyStoreListItem> {
val output = emptyMap<String, KeyStoreListItem>().toMutableMap()
val aliases = keyStore.aliases()
// KeyRef (as key reference) -> KeyRef.VersionNumber (as key identifier)
for (alias in aliases) {
if (alias.matches(regexForKeyReference)) {
val entry = keyStore.getCertificate(alias)
val matches = regexForKeyReference.matchEntire(alias)
val values = matches!!.groupValues
// Get the keyType associated with this key.
val kty: KeyType = AndroidKeyConverter.whatKeyTypeIs(entry.publicKey, logger)
// Add the key to an ListItem or make a new one
if (output.containsKey(values[1])) {
val listItem = output[values[1]]!!
if (listItem.kty != kty) {
throw logger.error("Key Container ${values[1]} contains keys of two different " +
"types (${listItem.kty.value}, ${kty.value})")
}
listItem.kids.add(alias)
} else {
output[values[1]] = KeyStoreListItem(kty, mutableListOf(alias))
}
}
}
return output
}
private fun listSecureData(): Map<String, KeyStoreListItem> {
val sharedPreferences = getSharedPreferences();
val keys = sharedPreferences.all.keys;
// all stored keys should be in JWT format
val keyMap = mutableMapOf<String, KeyStoreListItem>()
keys.forEach{
// verify that it matches the regex and grab the key reference
val keyReferenceMatch = AndroidKeyStore.regexForKeyReference.matchEntire(it)
if (keyReferenceMatch != null) {
val keyRef = keyReferenceMatch.groupValues[1];
val jwkBase64 = sharedPreferences.getString(it, null)!!
val jwkData = Base64.decode(jwkBase64, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
val key = MinimalJson.serializer.parse(JsonWebKey.serializer(), byteArrayToString(jwkData))
val keyType = toKeyType(key.kty, logger)
if (!keyMap.containsKey(keyRef)) {
keyMap[keyRef] = KeyStoreListItem(keyType, mutableListOf(it))
} else {
val listItem = keyMap[keyRef]!!
if (keyType != listItem.kty) {
throw logger.error("Key $keyRef has two different key types (${keyType.value}, ${listItem.kty.value})")
}
listItem.kids.add(it)
keyMap[keyRef] = listItem
}
}
}
return keyMap
}
private fun getSecurePublicKey(alias: String): PublicKey? {
return getSecurePrivateKey(alias)?.getPublicKey()
}
private fun getSecurePrivateKey(alias: String): PrivateKey? {
val data = getSecureData(alias) ?: return null
val jwk = MinimalJson.serializer.parse(JsonWebKey.serializer(), byteArrayToString(data))
if (jwk.kty == KeyType.RSA.value) {
return RsaPrivateKey(jwk, logger = logger)
} else if (jwk.kty == KeyType.EllipticCurve.value) {
return EllipticCurvePrivateKey(jwk, logger = logger)
} else {
throw logger.error("Unknown key type ${jwk.kty}")
}
}
private fun getSecureSecretkey(alias: String): SecretKey? {
val data = getSecureData(alias) ?: return null
val jwk = MinimalJson.serializer.parse(JsonWebKey.serializer(), byteArrayToString(data))
if (jwk.kty != KeyType.Octets.value) {
throw logger.error("$alias is not a secret key.")
}
return SecretKey(jwk, logger)
}
private fun getSecureData(alias: String): ByteArray? {
val sharedPreferences = getSharedPreferences();
val base64UrlEncodedData = sharedPreferences.getString(alias, null)
if (base64UrlEncodedData != null) {
return Base64.decode(base64UrlEncodedData, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
}
return null
}
private fun deleteSecureData(alias: String) {
val sharedPreferences = getSharedPreferences()
val editor = sharedPreferences.edit()
editor.remove(alias)
editor.apply()
}
private fun saveSecureData(alias: String, data: ByteArray) {
val sharedPreferences = getSharedPreferences();
val editor = sharedPreferences.edit();
editor.putString(alias, Base64.encodeToString(data, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP));
editor.apply()
}
private fun getSharedPreferences(): SharedPreferences {
val masterKeyAlias = getSecretVaultMasterKey()
return EncryptedSharedPreferences.create(
"secret_shared_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
@TargetApi(23)
private fun getSecretVaultMasterKey(): String {
val alias = "ms-useragent-secret-masterkey"
if (!keyStore.containsAlias(alias)) {
// Generate the master key
val generator = KeyGenerator.getInstance("AES", provider)
generator.init(KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(256)
.build())
generator.generateKey();
}
return alias
}
fun checkOrCreateKeyId(keyReference: String, kid: String?): String {
if (!kid.isNullOrBlank() && !kid.startsWith(keyReference) && !kid.startsWith("#$keyReference")) {
throw logger.error("Key ID must begin with key reference")
// This could be relaxed later if we flush keys and use a format of
// KEYREFERENCE.KEYID and ensure KEYID does not contain the dot delimiter
}
return if (!kid.isNullOrBlank()) {
logger.debug("Using key $kid")
kid
} else {
// generate a key id
val listItem = this.list()[keyReference]
if (listItem == null) { // no previous keys
logger.debug("New key reference #$keyReference.1")
"#$keyReference.1"
} else {
// heuristic, find the last digit and count up
var latestVersion = listItem.kids.reduce {
acc: String, current: String ->
val currentValue = regexForKeyIndex.matchEntire(current)?.groupValues?.get(1)?.toInt()
val accValue = acc.toIntOrNull()
if (currentValue != null && accValue == null) {
current
} else if (currentValue != null && accValue != null && currentValue > accValue) {
current
} else {
acc
}
}.toIntOrNull() ?: 0
latestVersion++
logger.debug("New key reference #$keyReference.$latestVersion")
"#$keyReference.$latestVersion"
}
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
package com.microsoft.did.sdk.crypto.keys
data class AndroidKeyHandle(val alias: String, val key: Any?)

Просмотреть файл

@ -1,34 +1,34 @@
package com.microsoft.did.sdk.crypto.keys
import java.math.BigInteger
import java.security.spec.ECFieldFp
import java.security.spec.ECParameterSpec
import java.security.spec.ECPoint
import java.security.spec.EllipticCurve
// We'll come back to this later but this is for native curves. It may not be necessary.
enum class EllipticCurves(spec: ECParameterSpec) {
secp256k1(ECParameterSpec(
EllipticCurve(
ECFieldFp(
BigInteger(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" +
"FFFFFC2F", 16
)
),
BigInteger("00000000000000000000000000000000000000000000000000000000" +
"00000000", 16),
BigInteger("00000000000000000000000000000000000000000000000000000000" +
"00000007", 16)
),
ECPoint(
BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0" +
"F4A13945D898C296", 16),
BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE3357" +
"6B315ECECBB6406837BF51F5", 16)
),
BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2" +
"FC632551", 16),
0x01
))
package com.microsoft.did.sdk.crypto.keys
import java.math.BigInteger
import java.security.spec.ECFieldFp
import java.security.spec.ECParameterSpec
import java.security.spec.ECPoint
import java.security.spec.EllipticCurve
// We'll come back to this later but this is for native curves. It may not be necessary.
enum class EllipticCurves(spec: ECParameterSpec) {
secp256k1(ECParameterSpec(
EllipticCurve(
ECFieldFp(
BigInteger(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" +
"FFFFFC2F", 16
)
),
BigInteger("00000000000000000000000000000000000000000000000000000000" +
"00000000", 16),
BigInteger("00000000000000000000000000000000000000000000000000000000" +
"00000007", 16)
),
ECPoint(
BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0" +
"F4A13945D898C296", 16),
BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE3357" +
"6B315ECECBB6406837BF51F5", 16)
),
BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2" +
"FC632551", 16),
0x01
))
}

Просмотреть файл

@ -1,28 +1,28 @@
package com.microsoft.did.sdk.crypto.models
enum class AndroidConstants(val value: String) {
// key factory algorithm names from https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyFactory
Rsa("RSA"),
Ec("EC"),
Aes("AES"),
AesWrap("AESWRAP"),
// Signature algorithm names from https://developer.android.com/training/articles/keystore.html#SupportedSignatures
EcDsaSha1("SHA1withECDSA"),
EcDsaSha224("SHA224withECDSA"),
EcDsaSha256("SHA256withECDSA"),
EcDsaSha384("SHA384withECDSA"),
EcDsaSha512("SHA512withECDSA"),
RsSha1("SHA1withRSA"),
RsSha224("SHA224withRSA"),
RsSha256("SHA256withRSA"),
RsSha384("SHA384withRSA"),
RsSha512("SHA512withRSA"),
//////////////////////
// Custom Constants //
//////////////////////
KeyReference("KeyReference")
package com.microsoft.did.sdk.crypto.models
enum class AndroidConstants(val value: String) {
// key factory algorithm names from https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyFactory
Rsa("RSA"),
Ec("EC"),
Aes("AES"),
AesWrap("AESWRAP"),
// Signature algorithm names from https://developer.android.com/training/articles/keystore.html#SupportedSignatures
EcDsaSha1("SHA1withECDSA"),
EcDsaSha224("SHA224withECDSA"),
EcDsaSha256("SHA256withECDSA"),
EcDsaSha384("SHA384withECDSA"),
EcDsaSha512("SHA512withECDSA"),
RsSha1("SHA1withRSA"),
RsSha224("SHA224withRSA"),
RsSha256("SHA256withRSA"),
RsSha384("SHA384withRSA"),
RsSha512("SHA512withRSA"),
//////////////////////
// Custom Constants //
//////////////////////
KeyReference("KeyReference")
}

Просмотреть файл

@ -1,295 +1,295 @@
package com.microsoft.did.sdk.crypto.plugins
import android.annotation.TargetApi
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import com.microsoft.did.sdk.crypto.models.AndroidConstants
import com.microsoft.did.sdk.crypto.keyStore.AndroidKeyStore
import com.microsoft.did.sdk.crypto.keys.AndroidKeyHandle
import com.microsoft.did.sdk.crypto.models.webCryptoApi.*
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithms.AesKeyGenParams
import com.microsoft.did.sdk.crypto.protocols.jose.JwaCryptoConverter
import com.microsoft.did.sdk.utilities.AndroidKeyConverter
import com.microsoft.did.sdk.utilities.ILogger
import java.math.BigInteger
import java.security.*
import java.security.spec.*
import javax.crypto.KeyGenerator
class AndroidSubtle(private var keyStore: AndroidKeyStore, private val logger: ILogger): SubtleCrypto {
override fun encrypt(algorithm: Algorithm, key: CryptoKey, data: ByteArray): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun decrypt(algorithm: Algorithm, key: CryptoKey, data: ByteArray): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun sign(algorithm: Algorithm, key: CryptoKey, data: ByteArray): ByteArray {
// verify we're signing with a private key
if (key.type != KeyType.Private) {
throw logger.error("Sign must use a private key")
}
// key's handle should be an Android keyStore key reference.
val handle = cryptoKeyToPrivateKey(key)
return Signature.getInstance(signAlgorithmToAndroid(algorithm, key)).run {
initSign(handle)
update(data)
sign()
}
}
override fun verify(algorithm: Algorithm, key: CryptoKey, signature: ByteArray, data: ByteArray): Boolean {
val handle = cryptoKeyToPublicKey(key)
val s = Signature.getInstance(signAlgorithmToAndroid(algorithm, key)).apply {
initVerify(handle)
update(data)
}
return s.verify(signature)
}
override fun digest(algorithm: Algorithm, data: ByteArray): ByteArray {
val digest = MessageDigest.getInstance(algorithm.name)
return digest.digest(data)
}
override fun generateKey(algorithm: Algorithm, extractable: Boolean, keyUsages: List<KeyUsage>): CryptoKey {
val secret = when(algorithm.name) {
W3cCryptoApiConstants.AesCbc.value, W3cCryptoApiConstants.AesCtr.value,
W3cCryptoApiConstants.AesGcm.value, W3cCryptoApiConstants.AesKw.value -> {
val generator = KeyGenerator.getInstance(AndroidConstants.Aes.value)
val alg = algorithm as AesKeyGenParams
generator.init(alg.length.toInt())
generator.generateKey()
}
else -> throw logger.error("Unsupported symmetric key algorithm: ${algorithm.name}")
}
return CryptoKey(
type = KeyType.Secret,
extractable = extractable,
usages = keyUsages,
handle = secret,
algorithm = algorithm
)
}
@TargetApi(23)
override fun generateKeyPair(algorithm: Algorithm, extractable: Boolean, keyUsages: List<KeyUsage>): CryptoKeyPair {
if (!algorithm.additionalParams.containsKey(AndroidConstants.KeyReference.value)){
throw logger.error("Algorithm must contain an additional parameter \"${AndroidConstants.KeyReference.value}\"")
}
val alias = keyStore.checkOrCreateKeyId(algorithm.additionalParams[AndroidConstants.KeyReference.value] as String, null)
logger.debug("Generating ${algorithm.name} key with alias $alias")
val keyPairGenerator = KeyPairGenerator.getInstance(keyPairAlgorithmToAndroid(algorithm), AndroidKeyStore.provider)
keyPairGenerator.initialize(
KeyGenParameterSpec.Builder(
alias,
keyPairUsageToAndroid(keyUsages)
).setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.build()
)
val keyPair = keyPairGenerator.genKeyPair()
// AndroidKeyStore.keyStore.
logger.debug("Key pair generated ($alias)")
// convert keypair.
return CryptoKeyPair(
CryptoKey(
KeyType.Public,
extractable,
algorithm,
keyUsages,
AndroidKeyHandle(
alias,
keyPair.public
)
),
CryptoKey(
KeyType.Private,
false,
algorithm,
keyUsages,
AndroidKeyHandle(
alias,
null
)
)
)
}
override fun deriveKey(
algorithm: Algorithm,
baseKey: CryptoKey,
derivedKeyType: Algorithm,
extractable: Boolean,
keyUsages: List<KeyUsage>
): CryptoKey {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun deriveBits(algorithm: Algorithm, baseKey: CryptoKey, length: ULong): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun importKey(
format: KeyFormat,
keyData: ByteArray,
algorithm: Algorithm,
extractable: Boolean,
keyUsages: List<KeyUsage>
): CryptoKey {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun importKey(
format: KeyFormat,
keyData: JsonWebKey,
algorithm: Algorithm,
extractable: Boolean,
keyUsages: List<KeyUsage>
): CryptoKey {
when (keyData.kty) {
com.microsoft.did.sdk.crypto.keys.KeyType.RSA.value -> {
val keyFactory = KeyFactory.getInstance(KeyProperties.KEY_ALGORITHM_RSA)
if (keyData.d != null) { // Private RSA key being imported
if (!AndroidKeyStore.keyStore.isKeyEntry(keyData.kid ?: "")) {
throw logger.error("Software private keys are not supported.")
}
val entry = AndroidKeyHandle(keyData.kid!!, null)
return CryptoKey(
KeyType.Private,
extractable,
JwaCryptoConverter.jwaAlgToWebCrypto(keyData.alg!!, logger = logger),
keyUsages,
entry
)
} else { // Public RSA key being imported
val key = keyFactory.generatePublic(RSAPublicKeySpec(
BigInteger(1, Base64.decode(keyData.n, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)),
BigInteger(1, Base64.decode(keyData.e, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP))
))
val entry = AndroidKeyHandle(keyData.kid ?: "", key)
return CryptoKey(
KeyType.Public,
extractable,
JwaCryptoConverter.jwaAlgToWebCrypto(keyData.alg!!, logger = logger),
keyUsages,
entry
)
}
}
com.microsoft.did.sdk.crypto.keys.KeyType.EllipticCurve.value -> {
TODO("Standard Elliptic Curves are not currently supported.")
}
else -> throw logger.error("Cannot import JWK key type ${keyData.kty}")
}
}
override fun exportKey(format: KeyFormat, key: CryptoKey): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun exportKeyJwk(key: CryptoKey): JsonWebKey {
val internalHandle = key.handle as? AndroidKeyHandle ?: throw logger.error("Unknown format for CryptoKey passed")
return when (internalHandle.key) {
is PublicKey -> {
AndroidKeyConverter.androidPublicKeyToPublicKey(internalHandle.alias, internalHandle.key, logger).toJWK()
}
null -> {
AndroidKeyConverter.androidPrivateKeyToPrivateKey(internalHandle.alias, AndroidKeyStore.keyStore, logger).toJWK()
}
else -> {
throw logger.error("Unknown CryptoKey format")
}
}
}
override fun wrapKey(
format: KeyFormat,
key: CryptoKey,
wrappingKey: CryptoKey,
wrapAlgorithm: Algorithm
): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun unwrapKey(
format: KeyFormat,
wrappedKey: ByteArray,
unwrappingKey: CryptoKey,
unwrapAlgorithm: Algorithm,
unwrappedKeyAlgorithm: Algorithm,
extractable: Boolean,
keyUsages: List<KeyUsage>
): CryptoKey {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun cryptoKeyToPublicKey(key: CryptoKey): PublicKey {
val internalHandle = key.handle as? AndroidKeyHandle
?: throw logger.error("Unknown format for CryptoKey passed")
return internalHandle.key as? PublicKey
?: throw logger.error("Private key passed when a public key was expected")
}
private fun cryptoKeyToPrivateKey(key: CryptoKey): PrivateKey {
val internalHandle = key.handle as? AndroidKeyHandle
?: throw logger.error("Unknown format for CryptoKey passed")
return AndroidKeyStore.keyStore.getKey(internalHandle.alias, null) as? PrivateKey
?: throw logger.error("Software private keys are not supported by the native Subtle.")
}
private fun signAlgorithmToAndroid(algorithm: Algorithm, cryptoKey: CryptoKey): String {
return when (algorithm.name) {
W3cCryptoApiConstants.EcDsa.value -> {
val ecDsaParams = algorithm as EcdsaParams
when (ecDsaParams.hash.name) {
W3cCryptoApiConstants.Sha1.value -> AndroidConstants.EcDsaSha1.value
W3cCryptoApiConstants.Sha224.value -> AndroidConstants.EcDsaSha224.value
W3cCryptoApiConstants.Sha256.value -> AndroidConstants.EcDsaSha256.value
W3cCryptoApiConstants.Sha384.value -> AndroidConstants.EcDsaSha384.value
W3cCryptoApiConstants.Sha512.value -> AndroidConstants.EcDsaSha512.value
else -> throw logger.error("Unsupported ECDSA hash algorithm: ${ecDsaParams.hash.name}")
}
}
W3cCryptoApiConstants.RsaSsaPkcs1V15.value -> {
// The hash is indicated by the key's "algorithm" slot.
val keyAlgorithm = cryptoKey.algorithm as? RsaHashedKeyAlgorithm ?: throw logger.error("Unsupported RSA key algorithm: ${cryptoKey.algorithm.name}")
when (keyAlgorithm.hash.name) {
W3cCryptoApiConstants.Sha1.value -> AndroidConstants.RsSha1.value
W3cCryptoApiConstants.Sha224.value -> AndroidConstants.RsSha224.value
W3cCryptoApiConstants.Sha256.value -> AndroidConstants.RsSha256.value
W3cCryptoApiConstants.Sha384.value -> AndroidConstants.RsSha384.value
W3cCryptoApiConstants.Sha512.value -> AndroidConstants.RsSha512.value
else -> throw logger.error("Unsupported RSA hash algorithm: ${keyAlgorithm.hash.name}")
}
}
else -> throw logger.error("Unsupported algorithm: ${algorithm.name}")
}
}
private fun keyPairAlgorithmToAndroid(algorithm: Algorithm): String {
return when (algorithm.name) {
W3cCryptoApiConstants.RsaSsaPkcs1V15.value -> AndroidConstants.Rsa.value
W3cCryptoApiConstants.EcDsa.value -> AndroidConstants.Ec.value
else -> throw logger.error("Unknown algorithm used: ${algorithm.name}")
}
}
private fun keyPairUsageToAndroid(usages: List<KeyUsage>): Int {
var flags = 0
usages.forEach { usage ->
flags = flags.or(when (usage) {
KeyUsage.Decrypt -> KeyProperties.PURPOSE_DECRYPT
KeyUsage.Encrypt -> KeyProperties.PURPOSE_ENCRYPT
KeyUsage.Sign -> KeyProperties.PURPOSE_SIGN
KeyUsage.Verify -> KeyProperties.PURPOSE_VERIFY
KeyUsage.WrapKey -> KeyProperties.PURPOSE_ENCRYPT
KeyUsage.UnwrapKey -> KeyProperties.PURPOSE_DECRYPT
else -> 0
})
}
return flags
}
package com.microsoft.did.sdk.crypto.plugins
import android.annotation.TargetApi
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import com.microsoft.did.sdk.crypto.models.AndroidConstants
import com.microsoft.did.sdk.crypto.keyStore.AndroidKeyStore
import com.microsoft.did.sdk.crypto.keys.AndroidKeyHandle
import com.microsoft.did.sdk.crypto.models.webCryptoApi.*
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithms.AesKeyGenParams
import com.microsoft.did.sdk.crypto.protocols.jose.JwaCryptoConverter
import com.microsoft.did.sdk.utilities.AndroidKeyConverter
import com.microsoft.did.sdk.utilities.ILogger
import java.math.BigInteger
import java.security.*
import java.security.spec.*
import javax.crypto.KeyGenerator
class AndroidSubtle(private var keyStore: AndroidKeyStore, private val logger: ILogger): SubtleCrypto {
override fun encrypt(algorithm: Algorithm, key: CryptoKey, data: ByteArray): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun decrypt(algorithm: Algorithm, key: CryptoKey, data: ByteArray): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun sign(algorithm: Algorithm, key: CryptoKey, data: ByteArray): ByteArray {
// verify we're signing with a private key
if (key.type != KeyType.Private) {
throw logger.error("Sign must use a private key")
}
// key's handle should be an Android keyStore key reference.
val handle = cryptoKeyToPrivateKey(key)
return Signature.getInstance(signAlgorithmToAndroid(algorithm, key)).run {
initSign(handle)
update(data)
sign()
}
}
override fun verify(algorithm: Algorithm, key: CryptoKey, signature: ByteArray, data: ByteArray): Boolean {
val handle = cryptoKeyToPublicKey(key)
val s = Signature.getInstance(signAlgorithmToAndroid(algorithm, key)).apply {
initVerify(handle)
update(data)
}
return s.verify(signature)
}
override fun digest(algorithm: Algorithm, data: ByteArray): ByteArray {
val digest = MessageDigest.getInstance(algorithm.name)
return digest.digest(data)
}
override fun generateKey(algorithm: Algorithm, extractable: Boolean, keyUsages: List<KeyUsage>): CryptoKey {
val secret = when(algorithm.name) {
W3cCryptoApiConstants.AesCbc.value, W3cCryptoApiConstants.AesCtr.value,
W3cCryptoApiConstants.AesGcm.value, W3cCryptoApiConstants.AesKw.value -> {
val generator = KeyGenerator.getInstance(AndroidConstants.Aes.value)
val alg = algorithm as AesKeyGenParams
generator.init(alg.length.toInt())
generator.generateKey()
}
else -> throw logger.error("Unsupported symmetric key algorithm: ${algorithm.name}")
}
return CryptoKey(
type = KeyType.Secret,
extractable = extractable,
usages = keyUsages,
handle = secret,
algorithm = algorithm
)
}
@TargetApi(23)
override fun generateKeyPair(algorithm: Algorithm, extractable: Boolean, keyUsages: List<KeyUsage>): CryptoKeyPair {
if (!algorithm.additionalParams.containsKey(AndroidConstants.KeyReference.value)){
throw logger.error("Algorithm must contain an additional parameter \"${AndroidConstants.KeyReference.value}\"")
}
val alias = keyStore.checkOrCreateKeyId(algorithm.additionalParams[AndroidConstants.KeyReference.value] as String, null)
logger.debug("Generating ${algorithm.name} key with alias $alias")
val keyPairGenerator = KeyPairGenerator.getInstance(keyPairAlgorithmToAndroid(algorithm), AndroidKeyStore.provider)
keyPairGenerator.initialize(
KeyGenParameterSpec.Builder(
alias,
keyPairUsageToAndroid(keyUsages)
).setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.build()
)
val keyPair = keyPairGenerator.genKeyPair()
// AndroidKeyStore.keyStore.
logger.debug("Key pair generated ($alias)")
// convert keypair.
return CryptoKeyPair(
CryptoKey(
KeyType.Public,
extractable,
algorithm,
keyUsages,
AndroidKeyHandle(
alias,
keyPair.public
)
),
CryptoKey(
KeyType.Private,
false,
algorithm,
keyUsages,
AndroidKeyHandle(
alias,
null
)
)
)
}
override fun deriveKey(
algorithm: Algorithm,
baseKey: CryptoKey,
derivedKeyType: Algorithm,
extractable: Boolean,
keyUsages: List<KeyUsage>
): CryptoKey {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun deriveBits(algorithm: Algorithm, baseKey: CryptoKey, length: ULong): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun importKey(
format: KeyFormat,
keyData: ByteArray,
algorithm: Algorithm,
extractable: Boolean,
keyUsages: List<KeyUsage>
): CryptoKey {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun importKey(
format: KeyFormat,
keyData: JsonWebKey,
algorithm: Algorithm,
extractable: Boolean,
keyUsages: List<KeyUsage>
): CryptoKey {
when (keyData.kty) {
com.microsoft.did.sdk.crypto.keys.KeyType.RSA.value -> {
val keyFactory = KeyFactory.getInstance(KeyProperties.KEY_ALGORITHM_RSA)
if (keyData.d != null) { // Private RSA key being imported
if (!AndroidKeyStore.keyStore.isKeyEntry(keyData.kid ?: "")) {
throw logger.error("Software private keys are not supported.")
}
val entry = AndroidKeyHandle(keyData.kid!!, null)
return CryptoKey(
KeyType.Private,
extractable,
JwaCryptoConverter.jwaAlgToWebCrypto(keyData.alg!!, logger = logger),
keyUsages,
entry
)
} else { // Public RSA key being imported
val key = keyFactory.generatePublic(RSAPublicKeySpec(
BigInteger(1, Base64.decode(keyData.n, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)),
BigInteger(1, Base64.decode(keyData.e, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP))
))
val entry = AndroidKeyHandle(keyData.kid ?: "", key)
return CryptoKey(
KeyType.Public,
extractable,
JwaCryptoConverter.jwaAlgToWebCrypto(keyData.alg!!, logger = logger),
keyUsages,
entry
)
}
}
com.microsoft.did.sdk.crypto.keys.KeyType.EllipticCurve.value -> {
TODO("Standard Elliptic Curves are not currently supported.")
}
else -> throw logger.error("Cannot import JWK key type ${keyData.kty}")
}
}
override fun exportKey(format: KeyFormat, key: CryptoKey): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun exportKeyJwk(key: CryptoKey): JsonWebKey {
val internalHandle = key.handle as? AndroidKeyHandle ?: throw logger.error("Unknown format for CryptoKey passed")
return when (internalHandle.key) {
is PublicKey -> {
AndroidKeyConverter.androidPublicKeyToPublicKey(internalHandle.alias, internalHandle.key, logger).toJWK()
}
null -> {
AndroidKeyConverter.androidPrivateKeyToPrivateKey(internalHandle.alias, AndroidKeyStore.keyStore, logger).toJWK()
}
else -> {
throw logger.error("Unknown CryptoKey format")
}
}
}
override fun wrapKey(
format: KeyFormat,
key: CryptoKey,
wrappingKey: CryptoKey,
wrapAlgorithm: Algorithm
): ByteArray {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun unwrapKey(
format: KeyFormat,
wrappedKey: ByteArray,
unwrappingKey: CryptoKey,
unwrapAlgorithm: Algorithm,
unwrappedKeyAlgorithm: Algorithm,
extractable: Boolean,
keyUsages: List<KeyUsage>
): CryptoKey {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun cryptoKeyToPublicKey(key: CryptoKey): PublicKey {
val internalHandle = key.handle as? AndroidKeyHandle
?: throw logger.error("Unknown format for CryptoKey passed")
return internalHandle.key as? PublicKey
?: throw logger.error("Private key passed when a public key was expected")
}
private fun cryptoKeyToPrivateKey(key: CryptoKey): PrivateKey {
val internalHandle = key.handle as? AndroidKeyHandle
?: throw logger.error("Unknown format for CryptoKey passed")
return AndroidKeyStore.keyStore.getKey(internalHandle.alias, null) as? PrivateKey
?: throw logger.error("Software private keys are not supported by the native Subtle.")
}
private fun signAlgorithmToAndroid(algorithm: Algorithm, cryptoKey: CryptoKey): String {
return when (algorithm.name) {
W3cCryptoApiConstants.EcDsa.value -> {
val ecDsaParams = algorithm as EcdsaParams
when (ecDsaParams.hash.name) {
W3cCryptoApiConstants.Sha1.value -> AndroidConstants.EcDsaSha1.value
W3cCryptoApiConstants.Sha224.value -> AndroidConstants.EcDsaSha224.value
W3cCryptoApiConstants.Sha256.value -> AndroidConstants.EcDsaSha256.value
W3cCryptoApiConstants.Sha384.value -> AndroidConstants.EcDsaSha384.value
W3cCryptoApiConstants.Sha512.value -> AndroidConstants.EcDsaSha512.value
else -> throw logger.error("Unsupported ECDSA hash algorithm: ${ecDsaParams.hash.name}")
}
}
W3cCryptoApiConstants.RsaSsaPkcs1V15.value -> {
// The hash is indicated by the key's "algorithm" slot.
val keyAlgorithm = cryptoKey.algorithm as? RsaHashedKeyAlgorithm ?: throw logger.error("Unsupported RSA key algorithm: ${cryptoKey.algorithm.name}")
when (keyAlgorithm.hash.name) {
W3cCryptoApiConstants.Sha1.value -> AndroidConstants.RsSha1.value
W3cCryptoApiConstants.Sha224.value -> AndroidConstants.RsSha224.value
W3cCryptoApiConstants.Sha256.value -> AndroidConstants.RsSha256.value
W3cCryptoApiConstants.Sha384.value -> AndroidConstants.RsSha384.value
W3cCryptoApiConstants.Sha512.value -> AndroidConstants.RsSha512.value
else -> throw logger.error("Unsupported RSA hash algorithm: ${keyAlgorithm.hash.name}")
}
}
else -> throw logger.error("Unsupported algorithm: ${algorithm.name}")
}
}
private fun keyPairAlgorithmToAndroid(algorithm: Algorithm): String {
return when (algorithm.name) {
W3cCryptoApiConstants.RsaSsaPkcs1V15.value -> AndroidConstants.Rsa.value
W3cCryptoApiConstants.EcDsa.value -> AndroidConstants.Ec.value
else -> throw logger.error("Unknown algorithm used: ${algorithm.name}")
}
}
private fun keyPairUsageToAndroid(usages: List<KeyUsage>): Int {
var flags = 0
usages.forEach { usage ->
flags = flags.or(when (usage) {
KeyUsage.Decrypt -> KeyProperties.PURPOSE_DECRYPT
KeyUsage.Encrypt -> KeyProperties.PURPOSE_ENCRYPT
KeyUsage.Sign -> KeyProperties.PURPOSE_SIGN
KeyUsage.Verify -> KeyProperties.PURPOSE_VERIFY
KeyUsage.WrapKey -> KeyProperties.PURPOSE_ENCRYPT
KeyUsage.UnwrapKey -> KeyProperties.PURPOSE_DECRYPT
else -> 0
})
}
return flags
}
}

Просмотреть файл

@ -1,8 +1,8 @@
package com.microsoft.did.sdk.crypto.plugins
import com.microsoft.did.sdk.crypto.models.webCryptoApi.SubtleCrypto
import com.microsoft.did.sdk.crypto.plugins.subtleCrypto.Subtle
import com.microsoft.did.sdk.utilities.ILogger
class EllipticCurveSubtleCrypto(default: SubtleCrypto, logger: ILogger): Subtle(setOf(Secp256k1Provider(default, logger)), logger), SubtleCrypto {
package com.microsoft.did.sdk.crypto.plugins
import com.microsoft.did.sdk.crypto.models.webCryptoApi.SubtleCrypto
import com.microsoft.did.sdk.crypto.plugins.subtleCrypto.Subtle
import com.microsoft.did.sdk.utilities.ILogger
class EllipticCurveSubtleCrypto(default: SubtleCrypto, logger: ILogger): Subtle(setOf(Secp256k1Provider(default, logger)), logger), SubtleCrypto {
}

Просмотреть файл

@ -1,215 +1,215 @@
package com.microsoft.did.sdk.crypto.plugins
import android.util.Base64
import com.microsoft.did.sdk.crypto.models.Sha
import com.microsoft.did.sdk.crypto.models.webCryptoApi.*
import com.microsoft.did.sdk.crypto.plugins.subtleCrypto.Provider
import com.microsoft.did.sdk.crypto.protocols.jose.JwaCryptoConverter
import com.microsoft.did.sdk.utilities.ILogger
import com.microsoft.did.sdk.utilities.printBytes
import com.microsoft.did.sdk.utilities.stringToByteArray
import org.bitcoin.NativeSecp256k1
import org.bitcoin.Secp256k1Context
import java.security.SecureRandom
import java.util.*
class Secp256k1Provider(val subtleCryptoSha: SubtleCrypto, logger: ILogger): Provider(logger) {
companion object {
init {
if (!Secp256k1Context.isEnabled()) {
System.loadLibrary("secp256k1")
}
}
}
data class Secp256k1Handle(val alias: String, val data: ByteArray)
override val name: String = "ECDSA"
override val privateKeyUsage: Set<KeyUsage> = setOf(KeyUsage.Sign)
override val publicKeyUsage: Set<KeyUsage> = setOf(KeyUsage.Verify)
override val symmetricKeyUsage: Set<KeyUsage>? = null
override fun onGenerateKeyPair(
algorithm: Algorithm,
extractable: Boolean,
keyUsages: Set<KeyUsage>
): CryptoKeyPair {
val seed = ByteArray(32)
val random = SecureRandom()
random.nextBytes(seed)
NativeSecp256k1.randomize(seed)
val secret = ByteArray(32)
random.nextBytes(secret)
val publicKey = NativeSecp256k1.computePubkey(secret)
val signAlgorithm = EcdsaParams(
hash = algorithm.additionalParams["hash"] as? Algorithm ?: Sha.Sha256,
additionalParams = mapOf(
"namedCurve" to W3cCryptoApiConstants.Secp256k1.value
)
)
val keyPair = CryptoKeyPair(
privateKey = CryptoKey(
KeyType.Private,
extractable,
signAlgorithm,
keyUsages.toList(),
Secp256k1Handle("", secret)
),
publicKey = CryptoKey(
KeyType.Public,
true,
signAlgorithm,
publicKeyUsage.toList(),
Secp256k1Handle("", publicKey)
))
return return keyPair
}
override fun checkGenerateKeyParams(algorithm: Algorithm) {
val keyGenParams = algorithm as? EcKeyGenParams ?: throw logger.error("EcKeyGenParams expected as algorithm")
if (keyGenParams.namedCurve.toUpperCase(Locale.ROOT) != W3cCryptoApiConstants.Secp256k1.value.toUpperCase(Locale.ROOT) &&
keyGenParams.namedCurve.toUpperCase(Locale.ROOT) != W3cCryptoApiConstants.Secp256k1.name.toUpperCase(Locale.ROOT)) {
throw logger.error("The curve ${keyGenParams.namedCurve} is not supported by Secp256k1Provider")
}
}
override fun onSign(algorithm: Algorithm, key: CryptoKey, data: ByteArray): ByteArray {
val keyData = (key.handle as Secp256k1Handle).data
val ecAlgorithm = algorithm as EcdsaParams
val hashedData = subtleCryptoSha.digest(ecAlgorithm.hash, data)
if (hashedData.size != 32) {
throw logger.error("Data must be 32 bytes")
}
return NativeSecp256k1.sign(hashedData, keyData)
}
override fun onVerify(algorithm: Algorithm, key: CryptoKey, signature: ByteArray, data: ByteArray): Boolean {
val keyData = (key.handle as Secp256k1Handle).data
val ecAlgorithm = algorithm as EcdsaParams
val hashedData = subtleCryptoSha.digest(ecAlgorithm.hash, data)
if (hashedData.size != 32) {
throw logger.error("Data must be 32 bytes")
}
print("KEY DATA: ")
printBytes(keyData)
return NativeSecp256k1.verify(hashedData, signature, keyData)
}
override fun onImportKey(
format: KeyFormat,
keyData: JsonWebKey,
algorithm: Algorithm,
extractable: Boolean,
keyUsages: Set<KeyUsage>
): CryptoKey {
val alias = keyData.kid ?: ""
return if (keyData.d != null) { // import d as the private key handle
CryptoKey(
type = KeyType.Private,
extractable = extractable,
algorithm = algorithm,
usages = keyUsages.toList(),
handle = Secp256k1Handle(alias, Base64.decode(stringToByteArray(keyData.d!!), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP))
)
} else {// public key
val x = Base64.decode(stringToByteArray(keyData.x!!), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
val y = Base64.decode(stringToByteArray(keyData.y!!), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
val xyData = ByteArray(65)
xyData[0] = secp256k1Tag.uncompressed.byte
x.forEachIndexed { index, byte ->
xyData[index + 1] = byte
}
y.forEachIndexed { index, byte ->
xyData[index + 33] = byte
}
CryptoKey(
type = KeyType.Public,
extractable = extractable,
algorithm = algorithm,
usages = keyUsages.toList(),
handle = Secp256k1Handle(alias, xyData)
)
}
}
override fun onExportKeyJwk(key: CryptoKey): JsonWebKey {
val keyOps = mutableListOf<String>()
for (usage in key.usages) {
keyOps.add(usage.value)
}
val publicKey: ByteArray
val handle = key.handle as Secp256k1Handle
val d: String? = if (key.type == KeyType.Private) {
publicKey = NativeSecp256k1.computePubkey(handle.data)
Base64.encodeToString(handle.data, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
} else {
publicKey = handle.data
null
}
val xyData = publicToXY(publicKey)
return JsonWebKey(
kty = com.microsoft.did.sdk.crypto.keys.KeyType.EllipticCurve.value,
kid = handle.alias,
crv = W3cCryptoApiConstants.Secp256k1.value,
use = "sig",
key_ops = keyOps,
alg = JwaCryptoConverter.webCryptoToJwa(key.algorithm, logger),
ext = key.extractable,
d = d?.trim(),
x = xyData.first.trim(),
y = xyData.second.trim()
)
}
override fun checkCryptoKey(key: CryptoKey, keyUsage: KeyUsage) {
super.checkCryptoKey(key, keyUsage)
if (key.type == KeyType.Private) {
val keyData = (key.handle as Secp256k1Handle).data
if (!NativeSecp256k1.secKeyVerify(keyData)) {
throw logger.error("Private key invalid")
}
}
}
// mapped from secp256k1_eckey_pubkey_parse
private fun publicToXY(keyData: ByteArray): Pair<String, String> {
if (keyData.size == 33 && (
keyData[0] == secp256k1Tag.even.byte ||
keyData[0] == secp256k1Tag.odd.byte)) {
// compressed form
return Pair(
"",
""
)
} else if (keyData.size == 65 && (
keyData[0] == secp256k1Tag.uncompressed.byte ||
keyData[0] == secp256k1Tag.hybridEven.byte ||
keyData[0] == secp256k1Tag.hybridOdd.byte
)) {
// uncompressed, bytes 1-32, and 33-end are x and y
val x = keyData.sliceArray(1..32)
val y = keyData.sliceArray(33..64)
return Pair(
Base64.encodeToString(x, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
Base64.encodeToString(y, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
)
} else {
throw logger.error("Public key improperly formatted")
}
}
enum class secp256k1Tag(val byte: Byte) {
even(0x02),
odd(0x03),
uncompressed(0x04),
hybridEven(0x06),
hybridOdd(0x07)
}
package com.microsoft.did.sdk.crypto.plugins
import android.util.Base64
import com.microsoft.did.sdk.crypto.models.Sha
import com.microsoft.did.sdk.crypto.models.webCryptoApi.*
import com.microsoft.did.sdk.crypto.plugins.subtleCrypto.Provider
import com.microsoft.did.sdk.crypto.protocols.jose.JwaCryptoConverter
import com.microsoft.did.sdk.utilities.ILogger
import com.microsoft.did.sdk.utilities.printBytes
import com.microsoft.did.sdk.utilities.stringToByteArray
import org.bitcoin.NativeSecp256k1
import org.bitcoin.Secp256k1Context
import java.security.SecureRandom
import java.util.*
class Secp256k1Provider(val subtleCryptoSha: SubtleCrypto, logger: ILogger): Provider(logger) {
companion object {
init {
if (!Secp256k1Context.isEnabled()) {
System.loadLibrary("secp256k1")
}
}
}
data class Secp256k1Handle(val alias: String, val data: ByteArray)
override val name: String = "ECDSA"
override val privateKeyUsage: Set<KeyUsage> = setOf(KeyUsage.Sign)
override val publicKeyUsage: Set<KeyUsage> = setOf(KeyUsage.Verify)
override val symmetricKeyUsage: Set<KeyUsage>? = null
override fun onGenerateKeyPair(
algorithm: Algorithm,
extractable: Boolean,
keyUsages: Set<KeyUsage>
): CryptoKeyPair {
val seed = ByteArray(32)
val random = SecureRandom()
random.nextBytes(seed)
NativeSecp256k1.randomize(seed)
val secret = ByteArray(32)
random.nextBytes(secret)
val publicKey = NativeSecp256k1.computePubkey(secret)
val signAlgorithm = EcdsaParams(
hash = algorithm.additionalParams["hash"] as? Algorithm ?: Sha.Sha256,
additionalParams = mapOf(
"namedCurve" to W3cCryptoApiConstants.Secp256k1.value
)
)
val keyPair = CryptoKeyPair(
privateKey = CryptoKey(
KeyType.Private,
extractable,
signAlgorithm,
keyUsages.toList(),
Secp256k1Handle("", secret)
),
publicKey = CryptoKey(
KeyType.Public,
true,
signAlgorithm,
publicKeyUsage.toList(),
Secp256k1Handle("", publicKey)
))
return return keyPair
}
override fun checkGenerateKeyParams(algorithm: Algorithm) {
val keyGenParams = algorithm as? EcKeyGenParams ?: throw logger.error("EcKeyGenParams expected as algorithm")
if (keyGenParams.namedCurve.toUpperCase(Locale.ROOT) != W3cCryptoApiConstants.Secp256k1.value.toUpperCase(Locale.ROOT) &&
keyGenParams.namedCurve.toUpperCase(Locale.ROOT) != W3cCryptoApiConstants.Secp256k1.name.toUpperCase(Locale.ROOT)) {
throw logger.error("The curve ${keyGenParams.namedCurve} is not supported by Secp256k1Provider")
}
}
override fun onSign(algorithm: Algorithm, key: CryptoKey, data: ByteArray): ByteArray {
val keyData = (key.handle as Secp256k1Handle).data
val ecAlgorithm = algorithm as EcdsaParams
val hashedData = subtleCryptoSha.digest(ecAlgorithm.hash, data)
if (hashedData.size != 32) {
throw logger.error("Data must be 32 bytes")
}
return NativeSecp256k1.sign(hashedData, keyData)
}
override fun onVerify(algorithm: Algorithm, key: CryptoKey, signature: ByteArray, data: ByteArray): Boolean {
val keyData = (key.handle as Secp256k1Handle).data
val ecAlgorithm = algorithm as EcdsaParams
val hashedData = subtleCryptoSha.digest(ecAlgorithm.hash, data)
if (hashedData.size != 32) {
throw logger.error("Data must be 32 bytes")
}
print("KEY DATA: ")
printBytes(keyData)
return NativeSecp256k1.verify(hashedData, signature, keyData)
}
override fun onImportKey(
format: KeyFormat,
keyData: JsonWebKey,
algorithm: Algorithm,
extractable: Boolean,
keyUsages: Set<KeyUsage>
): CryptoKey {
val alias = keyData.kid ?: ""
return if (keyData.d != null) { // import d as the private key handle
CryptoKey(
type = KeyType.Private,
extractable = extractable,
algorithm = algorithm,
usages = keyUsages.toList(),
handle = Secp256k1Handle(alias, Base64.decode(stringToByteArray(keyData.d!!), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP))
)
} else {// public key
val x = Base64.decode(stringToByteArray(keyData.x!!), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
val y = Base64.decode(stringToByteArray(keyData.y!!), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
val xyData = ByteArray(65)
xyData[0] = secp256k1Tag.uncompressed.byte
x.forEachIndexed { index, byte ->
xyData[index + 1] = byte
}
y.forEachIndexed { index, byte ->
xyData[index + 33] = byte
}
CryptoKey(
type = KeyType.Public,
extractable = extractable,
algorithm = algorithm,
usages = keyUsages.toList(),
handle = Secp256k1Handle(alias, xyData)
)
}
}
override fun onExportKeyJwk(key: CryptoKey): JsonWebKey {
val keyOps = mutableListOf<String>()
for (usage in key.usages) {
keyOps.add(usage.value)
}
val publicKey: ByteArray
val handle = key.handle as Secp256k1Handle
val d: String? = if (key.type == KeyType.Private) {
publicKey = NativeSecp256k1.computePubkey(handle.data)
Base64.encodeToString(handle.data, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
} else {
publicKey = handle.data
null
}
val xyData = publicToXY(publicKey)
return JsonWebKey(
kty = com.microsoft.did.sdk.crypto.keys.KeyType.EllipticCurve.value,
kid = handle.alias,
crv = W3cCryptoApiConstants.Secp256k1.value,
use = "sig",
key_ops = keyOps,
alg = JwaCryptoConverter.webCryptoToJwa(key.algorithm, logger),
ext = key.extractable,
d = d?.trim(),
x = xyData.first.trim(),
y = xyData.second.trim()
)
}
override fun checkCryptoKey(key: CryptoKey, keyUsage: KeyUsage) {
super.checkCryptoKey(key, keyUsage)
if (key.type == KeyType.Private) {
val keyData = (key.handle as Secp256k1Handle).data
if (!NativeSecp256k1.secKeyVerify(keyData)) {
throw logger.error("Private key invalid")
}
}
}
// mapped from secp256k1_eckey_pubkey_parse
private fun publicToXY(keyData: ByteArray): Pair<String, String> {
if (keyData.size == 33 && (
keyData[0] == secp256k1Tag.even.byte ||
keyData[0] == secp256k1Tag.odd.byte)) {
// compressed form
return Pair(
"",
""
)
} else if (keyData.size == 65 && (
keyData[0] == secp256k1Tag.uncompressed.byte ||
keyData[0] == secp256k1Tag.hybridEven.byte ||
keyData[0] == secp256k1Tag.hybridOdd.byte
)) {
// uncompressed, bytes 1-32, and 33-end are x and y
val x = keyData.sliceArray(1..32)
val y = keyData.sliceArray(33..64)
return Pair(
Base64.encodeToString(x, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
Base64.encodeToString(y, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
)
} else {
throw logger.error("Public key improperly formatted")
}
}
enum class secp256k1Tag(val byte: Byte) {
even(0x02),
odd(0x03),
uncompressed(0x04),
hybridEven(0x06),
hybridOdd(0x07)
}
}

Просмотреть файл

@ -1,24 +1,24 @@
package com.microsoft.did.sdk.crypto.plugins
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
import com.microsoft.did.sdk.crypto.models.webCryptoApi.KeyUsage
import com.microsoft.did.sdk.crypto.models.webCryptoApi.SubtleCrypto
import com.microsoft.did.sdk.crypto.plugins.subtleCrypto.Provider
import com.microsoft.did.sdk.crypto.plugins.subtleCrypto.Subtle
import com.microsoft.did.sdk.utilities.ILogger
import java.security.MessageDigest
class ShaSubtleCrypto(logger: ILogger): Subtle(setOf(ShaProvider(logger)), logger), SubtleCrypto {
class ShaProvider(logger: ILogger) : Provider(logger) {
override val name: String = "SHA-256"
override val privateKeyUsage: Set<KeyUsage>? = null
override val publicKeyUsage: Set<KeyUsage>? = null
override val symmetricKeyUsage: Set<KeyUsage> = emptySet()
override fun onDigest(algorithm: Algorithm, data: ByteArray): ByteArray {
val digest = MessageDigest.getInstance(algorithm.name)
return digest.digest(data)
}
}
package com.microsoft.did.sdk.crypto.plugins
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
import com.microsoft.did.sdk.crypto.models.webCryptoApi.KeyUsage
import com.microsoft.did.sdk.crypto.models.webCryptoApi.SubtleCrypto
import com.microsoft.did.sdk.crypto.plugins.subtleCrypto.Provider
import com.microsoft.did.sdk.crypto.plugins.subtleCrypto.Subtle
import com.microsoft.did.sdk.utilities.ILogger
import java.security.MessageDigest
class ShaSubtleCrypto(logger: ILogger): Subtle(setOf(ShaProvider(logger)), logger), SubtleCrypto {
class ShaProvider(logger: ILogger) : Provider(logger) {
override val name: String = "SHA-256"
override val privateKeyUsage: Set<KeyUsage>? = null
override val publicKeyUsage: Set<KeyUsage>? = null
override val symmetricKeyUsage: Set<KeyUsage> = emptySet()
override fun onDigest(algorithm: Algorithm, data: ByteArray): ByteArray {
val digest = MessageDigest.getInstance(algorithm.name)
return digest.digest(data)
}
}
}

Просмотреть файл

@ -1,99 +1,99 @@
package com.microsoft.did.sdk.utilities
import android.util.Base64
import com.microsoft.did.sdk.crypto.keys.KeyType
import com.microsoft.did.sdk.crypto.keys.PrivateKey
import com.microsoft.did.sdk.crypto.keys.PublicKey
import com.microsoft.did.sdk.crypto.keys.SecretKey
import com.microsoft.did.sdk.crypto.keys.ellipticCurve.EllipticCurvePrivateKey
import com.microsoft.did.sdk.crypto.keys.ellipticCurve.EllipticCurvePublicKey
import com.microsoft.did.sdk.crypto.keys.rsa.RsaPrivateKey
import com.microsoft.did.sdk.crypto.keys.rsa.RsaPublicKey
import com.microsoft.did.sdk.crypto.models.KeyUse
import com.microsoft.did.sdk.crypto.models.webCryptoApi.JsonWebKey
import com.microsoft.did.sdk.crypto.models.webCryptoApi.KeyUsage
import java.security.KeyStore
import java.security.interfaces.ECPublicKey
import java.security.interfaces.RSAPublicKey
object AndroidKeyConverter {
fun androidPublicKeyToPublicKey(alias: String, publicKey: java.security.PublicKey, logger: ILogger): PublicKey {
return when (whatKeyTypeIs(publicKey, logger)) {
KeyType.RSA -> {
RsaPublicKey(
JsonWebKey(
kty = KeyType.RSA.value,
kid = alias,
key_ops = listOf(KeyUsage.Encrypt.value),
use = KeyUse.Encryption.value,
n = Base64.encodeToString((publicKey as RSAPublicKey).modulus.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP).trim(),
e = Base64.encodeToString(publicKey.publicExponent.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP).trim()
),
logger = logger
)
}
KeyType.EllipticCurve -> {
EllipticCurvePublicKey(
JsonWebKey(
kty = KeyType.EllipticCurve.value,
kid = alias,
key_ops = listOf(KeyUsage.Verify.value),
use = KeyUse.Signature.value,
x = Base64.encodeToString((publicKey as ECPublicKey).w.affineX.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP).trim(),
y = Base64.encodeToString(publicKey.w.affineY.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP).trim()
),
logger = logger
)
}
else -> throw logger.error("Cannot convert key type.")
}
}
fun androidPrivateKeyToPrivateKey(alias: String, keyStore: KeyStore, logger: ILogger): PrivateKey {
val key = keyStore.getCertificate(alias).publicKey
return when (whatKeyTypeIs(key, logger)) {
KeyType.RSA -> {
RsaPrivateKey (
JsonWebKey(
kty = KeyType.RSA.value,
kid = alias,
key_ops = listOf(KeyUsage.Decrypt.value),
use = KeyUse.Encryption.value,
n = Base64.encodeToString((key as RSAPublicKey).modulus.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
e = Base64.encodeToString(key.publicExponent.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
d = "0",
p = "0",
q = "0",
dp = "0",
dq = "0",
qi = "0"
),
logger = logger
)
}
KeyType.EllipticCurve -> {
EllipticCurvePrivateKey (
JsonWebKey(
kty = KeyType.EllipticCurve.value,
kid = alias,
key_ops = listOf(KeyUsage.Sign.value),
use = KeyUse.Signature.value,
x = Base64.encodeToString((key as ECPublicKey).w.affineX.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
y = Base64.encodeToString(key.w.affineY.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
d = "0"
),
logger = logger
)
}
else -> throw logger.error("Cannot convert key type.")
}
}
fun whatKeyTypeIs(publicKey: java.security.PublicKey, logger: ILogger): KeyType {
return when (publicKey) {
is RSAPublicKey -> KeyType.RSA
is ECPublicKey -> KeyType.EllipticCurve
else -> throw logger.error("Unknown Key Type")
}
}
package com.microsoft.did.sdk.utilities
import android.util.Base64
import com.microsoft.did.sdk.crypto.keys.KeyType
import com.microsoft.did.sdk.crypto.keys.PrivateKey
import com.microsoft.did.sdk.crypto.keys.PublicKey
import com.microsoft.did.sdk.crypto.keys.SecretKey
import com.microsoft.did.sdk.crypto.keys.ellipticCurve.EllipticCurvePrivateKey
import com.microsoft.did.sdk.crypto.keys.ellipticCurve.EllipticCurvePublicKey
import com.microsoft.did.sdk.crypto.keys.rsa.RsaPrivateKey
import com.microsoft.did.sdk.crypto.keys.rsa.RsaPublicKey
import com.microsoft.did.sdk.crypto.models.KeyUse
import com.microsoft.did.sdk.crypto.models.webCryptoApi.JsonWebKey
import com.microsoft.did.sdk.crypto.models.webCryptoApi.KeyUsage
import java.security.KeyStore
import java.security.interfaces.ECPublicKey
import java.security.interfaces.RSAPublicKey
object AndroidKeyConverter {
fun androidPublicKeyToPublicKey(alias: String, publicKey: java.security.PublicKey, logger: ILogger): PublicKey {
return when (whatKeyTypeIs(publicKey, logger)) {
KeyType.RSA -> {
RsaPublicKey(
JsonWebKey(
kty = KeyType.RSA.value,
kid = alias,
key_ops = listOf(KeyUsage.Encrypt.value),
use = KeyUse.Encryption.value,
n = Base64.encodeToString((publicKey as RSAPublicKey).modulus.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP).trim(),
e = Base64.encodeToString(publicKey.publicExponent.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP).trim()
),
logger = logger
)
}
KeyType.EllipticCurve -> {
EllipticCurvePublicKey(
JsonWebKey(
kty = KeyType.EllipticCurve.value,
kid = alias,
key_ops = listOf(KeyUsage.Verify.value),
use = KeyUse.Signature.value,
x = Base64.encodeToString((publicKey as ECPublicKey).w.affineX.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP).trim(),
y = Base64.encodeToString(publicKey.w.affineY.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP).trim()
),
logger = logger
)
}
else -> throw logger.error("Cannot convert key type.")
}
}
fun androidPrivateKeyToPrivateKey(alias: String, keyStore: KeyStore, logger: ILogger): PrivateKey {
val key = keyStore.getCertificate(alias).publicKey
return when (whatKeyTypeIs(key, logger)) {
KeyType.RSA -> {
RsaPrivateKey (
JsonWebKey(
kty = KeyType.RSA.value,
kid = alias,
key_ops = listOf(KeyUsage.Decrypt.value),
use = KeyUse.Encryption.value,
n = Base64.encodeToString((key as RSAPublicKey).modulus.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
e = Base64.encodeToString(key.publicExponent.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
d = "0",
p = "0",
q = "0",
dp = "0",
dq = "0",
qi = "0"
),
logger = logger
)
}
KeyType.EllipticCurve -> {
EllipticCurvePrivateKey (
JsonWebKey(
kty = KeyType.EllipticCurve.value,
kid = alias,
key_ops = listOf(KeyUsage.Sign.value),
use = KeyUse.Signature.value,
x = Base64.encodeToString((key as ECPublicKey).w.affineX.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
y = Base64.encodeToString(key.w.affineY.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP),
d = "0"
),
logger = logger
)
}
else -> throw logger.error("Cannot convert key type.")
}
}
fun whatKeyTypeIs(publicKey: java.security.PublicKey, logger: ILogger): KeyType {
return when (publicKey) {
is RSAPublicKey -> KeyType.RSA
is ECPublicKey -> KeyType.EllipticCurve
else -> throw logger.error("Unknown Key Type")
}
}
}

Просмотреть файл

@ -1,10 +1,10 @@
package com.microsoft.did.sdk.utilities
import java.util.*
/**
* Returns the current time in milliseconds since UNIX epoch
*/
actual fun getCurrentTime(): Long {
return Date().time
package com.microsoft.did.sdk.utilities
import java.util.*
/**
* Returns the current time in milliseconds since UNIX epoch
*/
actual fun getCurrentTime(): Long {
return Date().time
}

Просмотреть файл

@ -1,30 +1,30 @@
package com.microsoft.did.sdk.auth
enum class OAuthRequestParameter(val value: String) {
// Required
Scope("scope"),
ResponseType("response_type"),
ClientId("client_id"),
RedirectUri("redirect_uri"),
// Recommended
State("state"),
// Optional
ResponseMode("response_mode"),
Nonce("nonce"),
MaxAge("max_age"),
UiLocales("ui_locales"),
IdTokenHint("id_token_hint"),
// Self-issued parameters (optional)
Registration("registration"),
Request("request"),
RequestUri("request_uri"),
Claims("claims"),
IdToken("id_token"),
// custom parameters
Offer("vc_offer")
package com.microsoft.did.sdk.auth
enum class OAuthRequestParameter(val value: String) {
// Required
Scope("scope"),
ResponseType("response_type"),
ClientId("client_id"),
RedirectUri("redirect_uri"),
// Recommended
State("state"),
// Optional
ResponseMode("response_mode"),
Nonce("nonce"),
MaxAge("max_age"),
UiLocales("ui_locales"),
IdTokenHint("id_token_hint"),
// Self-issued parameters (optional)
Registration("registration"),
Request("request"),
RequestUri("request_uri"),
Claims("claims"),
IdToken("id_token"),
// custom parameters
Offer("vc_offer")
}

Просмотреть файл

@ -1,22 +1,22 @@
package com.microsoft.did.sdk.auth
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class IdToken(
val iss: String,
val sub: String,
val aud: String,
val exp: Int,
val iat: Int,
@SerialName("auth_time")
val authTime: Int? = null,
val nonce: String,
@SerialName("acr")
val authenticationContextClass: String? = null,
@SerialName("amr")
val authenticationMethods: List<String>? = null,
@SerialName("azp")
val authorizedParty: String? = null
package com.microsoft.did.sdk.auth
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class IdToken(
val iss: String,
val sub: String,
val aud: String,
val exp: Int,
val iat: Int,
@SerialName("auth_time")
val authTime: Int? = null,
val nonce: String,
@SerialName("acr")
val authenticationContextClass: String? = null,
@SerialName("amr")
val authenticationMethods: List<String>? = null,
@SerialName("azp")
val authorizedParty: String? = null
)

Просмотреть файл

@ -1,33 +1,33 @@
package com.microsoft.did.sdk.auth.oidc
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Registration(
@SerialName("redirect_uris")
val redirectUris: List<String>?,
@SerialName("response_types")
val responseTypes: List<String>?,
@SerialName("grant_types")
val grantTypes: List<String>?,
@SerialName("application_type")
val applicationType: String?,
val contacts: List<String>?,
@SerialName("client_name")
val clientName: String?,
@SerialName("logo_uri")
val logoUri: String?,
@SerialName("client_uri")
val clientUri: String?,
@SerialName("policy_uri")
val policyUri: String?,
@SerialName("tos_uri")
val TermsOfServiceUri: String?,
@SerialName("Jwks_uri")
val JsonWebKeySetUri: String?,
// @SerialName("jwks")
// val JsonWebKeySet: TODO: Implement JsonWebKeySet,
@SerialName("request_uris")
val requestUris: List<String>?
package com.microsoft.did.sdk.auth.oidc
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Registration(
@SerialName("redirect_uris")
val redirectUris: List<String>?,
@SerialName("response_types")
val responseTypes: List<String>?,
@SerialName("grant_types")
val grantTypes: List<String>?,
@SerialName("application_type")
val applicationType: String?,
val contacts: List<String>?,
@SerialName("client_name")
val clientName: String?,
@SerialName("logo_uri")
val logoUri: String?,
@SerialName("client_uri")
val clientUri: String?,
@SerialName("policy_uri")
val policyUri: String?,
@SerialName("tos_uri")
val TermsOfServiceUri: String?,
@SerialName("Jwks_uri")
val JsonWebKeySetUri: String?,
// @SerialName("jwks")
// val JsonWebKeySet: TODO: Implement JsonWebKeySet,
@SerialName("request_uris")
val requestUris: List<String>?
)

Просмотреть файл

@ -1,92 +1,92 @@
package com.microsoft.did.sdk.auth.oidc
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class RequestClaimParameter (
val userInfo: UserInfoRequest? = null,
@SerialName("id_token")
val idToken: Map<String, MemberScope<String>>? = null
) {
@Serializable
data class UserInfoRequest(
val name: MemberScope<String>? = MemberScope.default(),
@SerialName("given_name")
val givenName: MemberScope<String>? = MemberScope.default(),
@SerialName("family_name")
val familyName: MemberScope<String>? = MemberScope.default(),
@SerialName("middle_name")
val middleName: MemberScope<String>? = MemberScope.default(),
val nickname: MemberScope<String>? = MemberScope.default(),
@SerialName("preferred_username")
val preferredUsername: MemberScope<String>? = MemberScope.default(),
val profile: MemberScope<String>? = MemberScope.default(),
val picture: MemberScope<String>? = MemberScope.default(),
val website: MemberScope<String>? = MemberScope.default(),
val email: MemberScope<String>? = MemberScope.default(),
@SerialName("email_verified")
val emailVerified: MemberScope<Boolean>? = MemberScope.default(),
val gender: MemberScope<String>? = MemberScope.default(),
val birthdate: MemberScope<String>? = MemberScope.default(),
val zoneinfo: MemberScope<String>? = MemberScope.default(),
val locale: MemberScope<String>? = MemberScope.default(),
@SerialName("phone_number")
val phoneNumber: MemberScope<String>? = MemberScope.default(),
@SerialName("phone_number_verified")
val phoneNumberVerified: MemberScope<Boolean>? = MemberScope.default(),
val address: MemberScope<Address>? = MemberScope.default(),
@SerialName("updated_at")
val updatedAt: MemberScope<Int>? = MemberScope.default()
) {
@Serializable
data class Address(
val formatted: String?,
@SerialName("street_address")
val streetAddress: String?,
val locality: String?,
val region: String?,
@SerialName("postal_code")
val postalCode: String?,
val country: String?
)
}
@Serializable
data class MemberScope<T>(
val essential: Boolean? = false,
val value: T? = null,
val values: List<T>? = null,
val undefined: Boolean = false
) {
companion object {
fun <T>default(): MemberScope<T> {
return MemberScope(undefined = true)
}
}
}
fun getRequestedClaimClasses(): Map<String, Boolean> {
return if (idToken == null) {
emptyMap()
} else {
val idTokenClaims = listOf("iss",
"sub",
"aud",
"exp",
"iat",
"auth_time",
"nonce",
"acr",
"amr",
"azp")
val requestedClasses = mutableMapOf<String, Boolean>()
idToken.filter {
!(it.key in idTokenClaims)
}.forEach {
requestedClasses[it.key] = it.value.essential ?: false
}
requestedClasses
}
}
package com.microsoft.did.sdk.auth.oidc
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class RequestClaimParameter (
val userInfo: UserInfoRequest? = null,
@SerialName("id_token")
val idToken: Map<String, MemberScope<String>>? = null
) {
@Serializable
data class UserInfoRequest(
val name: MemberScope<String>? = MemberScope.default(),
@SerialName("given_name")
val givenName: MemberScope<String>? = MemberScope.default(),
@SerialName("family_name")
val familyName: MemberScope<String>? = MemberScope.default(),
@SerialName("middle_name")
val middleName: MemberScope<String>? = MemberScope.default(),
val nickname: MemberScope<String>? = MemberScope.default(),
@SerialName("preferred_username")
val preferredUsername: MemberScope<String>? = MemberScope.default(),
val profile: MemberScope<String>? = MemberScope.default(),
val picture: MemberScope<String>? = MemberScope.default(),
val website: MemberScope<String>? = MemberScope.default(),
val email: MemberScope<String>? = MemberScope.default(),
@SerialName("email_verified")
val emailVerified: MemberScope<Boolean>? = MemberScope.default(),
val gender: MemberScope<String>? = MemberScope.default(),
val birthdate: MemberScope<String>? = MemberScope.default(),
val zoneinfo: MemberScope<String>? = MemberScope.default(),
val locale: MemberScope<String>? = MemberScope.default(),
@SerialName("phone_number")
val phoneNumber: MemberScope<String>? = MemberScope.default(),
@SerialName("phone_number_verified")
val phoneNumberVerified: MemberScope<Boolean>? = MemberScope.default(),
val address: MemberScope<Address>? = MemberScope.default(),
@SerialName("updated_at")
val updatedAt: MemberScope<Int>? = MemberScope.default()
) {
@Serializable
data class Address(
val formatted: String?,
@SerialName("street_address")
val streetAddress: String?,
val locality: String?,
val region: String?,
@SerialName("postal_code")
val postalCode: String?,
val country: String?
)
}
@Serializable
data class MemberScope<T>(
val essential: Boolean? = false,
val value: T? = null,
val values: List<T>? = null,
val undefined: Boolean = false
) {
companion object {
fun <T>default(): MemberScope<T> {
return MemberScope(undefined = true)
}
}
}
fun getRequestedClaimClasses(): Map<String, Boolean> {
return if (idToken == null) {
emptyMap()
} else {
val idTokenClaims = listOf("iss",
"sub",
"aud",
"exp",
"iat",
"auth_time",
"nonce",
"acr",
"amr",
"azp")
val requestedClasses = mutableMapOf<String, Boolean>()
idToken.filter {
!(it.key in idTokenClaims)
}.forEach {
requestedClasses[it.key] = it.value.essential ?: false
}
requestedClasses
}
}
}

Просмотреть файл

@ -1,47 +1,47 @@
package com.microsoft.did.sdk.auth.oidc
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class UserInfo (
val name: String? = null,
@SerialName("given_name")
val givenName: String? = null,
@SerialName("family_name")
val familyName: String? = null,
@SerialName("middle_name")
val middleName: String? = null,
val nickname: String? = null,
@SerialName("preferred_username")
val preferredUsername: String? = null,
val profile: String? = null,
val picture: String? = null,
val website: String? = null,
val email: String? = null,
@SerialName("email_verified")
val emailVerified: Boolean? = null,
val gender: String? = null,
val birthdate: String? = null,
val zoneinfo: String? = null,
val locale: String? = null,
@SerialName("phone_number")
val phoneNumber: String? = null,
@SerialName("phone_number_verified")
val phoneNumberVerified: Boolean? = null,
val address: Address? = null,
@SerialName("updated_at")
val updatedAt: Int? = null
) {
@Serializable
data class Address (
val formatted: String?,
@SerialName("street_address")
val streetAddress: String?,
val locality: String?,
val region: String?,
@SerialName("postal_code")
val postalCode: String?,
val country: String?
)
package com.microsoft.did.sdk.auth.oidc
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class UserInfo (
val name: String? = null,
@SerialName("given_name")
val givenName: String? = null,
@SerialName("family_name")
val familyName: String? = null,
@SerialName("middle_name")
val middleName: String? = null,
val nickname: String? = null,
@SerialName("preferred_username")
val preferredUsername: String? = null,
val profile: String? = null,
val picture: String? = null,
val website: String? = null,
val email: String? = null,
@SerialName("email_verified")
val emailVerified: Boolean? = null,
val gender: String? = null,
val birthdate: String? = null,
val zoneinfo: String? = null,
val locale: String? = null,
@SerialName("phone_number")
val phoneNumber: String? = null,
@SerialName("phone_number_verified")
val phoneNumberVerified: Boolean? = null,
val address: Address? = null,
@SerialName("updated_at")
val updatedAt: Int? = null
) {
@Serializable
data class Address (
val formatted: String?,
@SerialName("street_address")
val streetAddress: String?,
val locality: String?,
val region: String?,
@SerialName("postal_code")
val postalCode: String?,
val country: String?
)
}

Просмотреть файл

@ -1,16 +1,16 @@
package com.microsoft.did.sdk.auth.oidc
import com.microsoft.did.sdk.auth.OAuthRequestParameter
import com.microsoft.did.sdk.utilities.ILogger
import com.microsoft.did.sdk.utilities.PercentEncoding
fun getQueryStringParameter(name: OAuthRequestParameter, url: String, required: Boolean = false, logger: ILogger): String? {
val findResults = Regex("${name.value}=([^&]+)").find(url)
if (findResults != null) {
return PercentEncoding.decode(findResults.groupValues[1], logger = logger)
} else if (required) {
throw logger.error("Openid requires a \"${name.value}\" parameter")
}
return null
package com.microsoft.did.sdk.auth.oidc
import com.microsoft.did.sdk.auth.OAuthRequestParameter
import com.microsoft.did.sdk.utilities.ILogger
import com.microsoft.did.sdk.utilities.PercentEncoding
fun getQueryStringParameter(name: OAuthRequestParameter, url: String, required: Boolean = false, logger: ILogger): String? {
val findResults = Regex("${name.value}=([^&]+)").find(url)
if (findResults != null) {
return PercentEncoding.decode(findResults.groupValues[1], logger = logger)
} else if (required) {
throw logger.error("Openid requires a \"${name.value}\" parameter")
}
return null
}

Просмотреть файл

@ -1,82 +1,82 @@
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsFormat
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsToken
import com.microsoft.did.sdk.identifier.Identifier
import com.microsoft.did.sdk.utilities.ILogger
import com.microsoft.did.sdk.utilities.MinimalJson
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.stringify
class ClaimBuilder(forClass: ClaimClass? = null, private val logger: ILogger) {
var context: String? = null
var type: String? = null
var issuerName: String? = forClass?.issuerName
var claimLogo: ClaimClass.ClaimLogo? = forClass?.claimLogo
var claimName: String? = forClass?.claimName
var hexBackgroundColor: String? = forClass?.hexBackgroundColor
var hexFontColor: String? = forClass?.hexFontColor
var moreInfo: String? = forClass?.moreInfo
val helpLinks: MutableMap<String, String> = forClass?.helpLinks?.toMutableMap() ?: mutableMapOf()
private val claimClassDescriptions: MutableList<ClaimDescription> = forClass?.claimDescriptions?.toMutableList() ?: mutableListOf()
var readPermissionDescription: PermissionDescription? = forClass?.readPermissionDescription
private val claimDescriptions: MutableList<ClaimDescription> = mutableListOf()
private val claimDetails: MutableList<Map<String, String>> = mutableListOf()
fun addClassDescription(header: String, body: String) {
claimClassDescriptions.add(ClaimDescription(header, body))
}
fun addClaimDescription(header: String, body: String) {
claimDescriptions.add(ClaimDescription(header, body))
}
@ImplicitReflectionSerializer
fun addClaimDetail(claim: Map<String, String>) {
claimDetails.add(claim)
}
fun buildClass(): ClaimClass {
return ClaimClass(
issuerName,
claimLogo,
claimName,
hexBackgroundColor,
hexFontColor,
moreInfo,
helpLinks,
claimClassDescriptions,
readPermissionDescription
)
}
@ImplicitReflectionSerializer
fun buildObject(classUri: String, identifier: Identifier, cryptoOperations: CryptoOperations? = null): ClaimObject {
if (context.isNullOrBlank() || type.isNullOrBlank()) {
throw logger.error("Context and Type must be set.")
}
val claims = if (cryptoOperations != null) {
val serializedData = MinimalJson.serializer.stringify(claimDetails)
val token = JwsToken(serializedData, logger = logger)
token.sign(identifier.signatureKeyReference, cryptoOperations)
SignedClaimDetail(
data = token.serialize(JwsFormat.Compact)
)
} else {
UnsignedClaimDetail(
data = claimDetails.toList()
)
}
return ClaimObject(
classUri,
context!!,
type!!,
claimDescriptions,
identifier.document.id,
claims
)
}
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsFormat
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsToken
import com.microsoft.did.sdk.identifier.Identifier
import com.microsoft.did.sdk.utilities.ILogger
import com.microsoft.did.sdk.utilities.MinimalJson
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.stringify
class ClaimBuilder(forClass: ClaimClass? = null, private val logger: ILogger) {
var context: String? = null
var type: String? = null
var issuerName: String? = forClass?.issuerName
var claimLogo: ClaimClass.ClaimLogo? = forClass?.claimLogo
var claimName: String? = forClass?.claimName
var hexBackgroundColor: String? = forClass?.hexBackgroundColor
var hexFontColor: String? = forClass?.hexFontColor
var moreInfo: String? = forClass?.moreInfo
val helpLinks: MutableMap<String, String> = forClass?.helpLinks?.toMutableMap() ?: mutableMapOf()
private val claimClassDescriptions: MutableList<ClaimDescription> = forClass?.claimDescriptions?.toMutableList() ?: mutableListOf()
var readPermissionDescription: PermissionDescription? = forClass?.readPermissionDescription
private val claimDescriptions: MutableList<ClaimDescription> = mutableListOf()
private val claimDetails: MutableList<Map<String, String>> = mutableListOf()
fun addClassDescription(header: String, body: String) {
claimClassDescriptions.add(ClaimDescription(header, body))
}
fun addClaimDescription(header: String, body: String) {
claimDescriptions.add(ClaimDescription(header, body))
}
@ImplicitReflectionSerializer
fun addClaimDetail(claim: Map<String, String>) {
claimDetails.add(claim)
}
fun buildClass(): ClaimClass {
return ClaimClass(
issuerName,
claimLogo,
claimName,
hexBackgroundColor,
hexFontColor,
moreInfo,
helpLinks,
claimClassDescriptions,
readPermissionDescription
)
}
@ImplicitReflectionSerializer
fun buildObject(classUri: String, identifier: Identifier, cryptoOperations: CryptoOperations? = null): ClaimObject {
if (context.isNullOrBlank() || type.isNullOrBlank()) {
throw logger.error("Context and Type must be set.")
}
val claims = if (cryptoOperations != null) {
val serializedData = MinimalJson.serializer.stringify(claimDetails)
val token = JwsToken(serializedData, logger = logger)
token.sign(identifier.signatureKeyReference, cryptoOperations)
SignedClaimDetail(
data = token.serialize(JwsFormat.Compact)
)
} else {
UnsignedClaimDetail(
data = claimDetails.toList()
)
}
return ClaimObject(
classUri,
context!!,
type!!,
claimDescriptions,
identifier.document.id,
claims
)
}
}

Просмотреть файл

@ -1,45 +1,45 @@
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.utilities.MinimalJson
import com.microsoft.did.sdk.utilities.getHttpClient
import io.ktor.client.request.get
import kotlinx.serialization.Serializable
@Serializable
data class ClaimClass(
val issuerName: String? = null,
val claimLogo: ClaimLogo? = null,
val claimName: String? = null,
val hexBackgroundColor: String? = null,
val hexFontColor: String? = null,
val moreInfo: String? = null,
val helpLinks: Map<String, String>? = null,
val claimDescriptions: List<ClaimDescription>? = null,
val readPermissionDescription: PermissionDescription? = null
) {
@Serializable
data class ClaimLogo(
val sourceUri: SourceUri
) {
@Serializable
data class SourceUri(
val uri: String,
val description: String? = null
)
}
companion object {
suspend fun resolve(url: String): ClaimClass {
val document = getHttpClient().get<String>(url)
return deserialize(document)
}
fun deserialize(claimClass: String): ClaimClass {
return MinimalJson.serializer.parse(ClaimClass.serializer(), claimClass)
}
}
fun serialize(): String {
return MinimalJson.serializer.stringify(ClaimClass.serializer(), this)
}
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.utilities.MinimalJson
import com.microsoft.did.sdk.utilities.getHttpClient
import io.ktor.client.request.get
import kotlinx.serialization.Serializable
@Serializable
data class ClaimClass(
val issuerName: String? = null,
val claimLogo: ClaimLogo? = null,
val claimName: String? = null,
val hexBackgroundColor: String? = null,
val hexFontColor: String? = null,
val moreInfo: String? = null,
val helpLinks: Map<String, String>? = null,
val claimDescriptions: List<ClaimDescription>? = null,
val readPermissionDescription: PermissionDescription? = null
) {
@Serializable
data class ClaimLogo(
val sourceUri: SourceUri
) {
@Serializable
data class SourceUri(
val uri: String,
val description: String? = null
)
}
companion object {
suspend fun resolve(url: String): ClaimClass {
val document = getHttpClient().get<String>(url)
return deserialize(document)
}
fun deserialize(claimClass: String): ClaimClass {
return MinimalJson.serializer.parse(ClaimClass.serializer(), claimClass)
}
}
fun serialize(): String {
return MinimalJson.serializer.stringify(ClaimClass.serializer(), this)
}
}

Просмотреть файл

@ -1,6 +1,6 @@
package com.microsoft.did.sdk.credentials
import kotlinx.serialization.Serializable
@Serializable
package com.microsoft.did.sdk.credentials
import kotlinx.serialization.Serializable
@Serializable
data class ClaimDescription (val header: String, val body: String)

Просмотреть файл

@ -1,11 +1,11 @@
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
interface ClaimDetail {
val type: String
suspend fun verify(cryptoOperations: CryptoOperations, resolver: IResolver, logger: ILogger)
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
interface ClaimDetail {
val type: String
suspend fun verify(cryptoOperations: CryptoOperations, resolver: IResolver, logger: ILogger)
}

Просмотреть файл

@ -1,40 +1,40 @@
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.protocols.jose.DidKeyResolver
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsToken
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
import com.microsoft.did.sdk.utilities.MinimalJson
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class ClaimObject(val claimClass: String,
@SerialName("@context")
val context: String,
@SerialName("@type")
val type: String,
val claimDescriptions: List<ClaimDescription>,
val claimIssuer: String,
val claimDetails: ClaimDetail) {
companion object {
fun deserialize(claimObject: String): ClaimObject {
return MinimalJson.serializer.parse(ClaimObject.serializer(), claimObject)
}
}
fun serialize(): String {
return MinimalJson.serializer.stringify(ClaimObject.serializer(), this)
}
suspend fun getClaimClass(): ClaimClass {
return ClaimClass.resolve(claimClass)
}
@ImplicitReflectionSerializer
suspend fun verify(cryptoOperations: CryptoOperations, resolver: IResolver, logger: ILogger) {
claimDetails.verify(cryptoOperations, resolver, logger = logger)
}
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.protocols.jose.DidKeyResolver
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsToken
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
import com.microsoft.did.sdk.utilities.MinimalJson
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class ClaimObject(val claimClass: String,
@SerialName("@context")
val context: String,
@SerialName("@type")
val type: String,
val claimDescriptions: List<ClaimDescription>,
val claimIssuer: String,
val claimDetails: ClaimDetail) {
companion object {
fun deserialize(claimObject: String): ClaimObject {
return MinimalJson.serializer.parse(ClaimObject.serializer(), claimObject)
}
}
fun serialize(): String {
return MinimalJson.serializer.stringify(ClaimObject.serializer(), this)
}
suspend fun getClaimClass(): ClaimClass {
return ClaimClass.resolve(claimClass)
}
@ImplicitReflectionSerializer
suspend fun verify(cryptoOperations: CryptoOperations, resolver: IResolver, logger: ILogger) {
claimDetails.verify(cryptoOperations, resolver, logger = logger)
}
}

Просмотреть файл

@ -1,13 +1,13 @@
package com.microsoft.did.sdk.credentials
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class ClaimResponse(
@SerialName("credential-id")
val id: String,
val state: String,
@SerialName("credential")
val claimObject: ClaimObject
package com.microsoft.did.sdk.credentials
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class ClaimResponse(
@SerialName("credential-id")
val id: String,
val state: String,
@SerialName("credential")
val claimObject: ClaimObject
)

Просмотреть файл

@ -1,13 +1,13 @@
package com.microsoft.did.sdk.credentials
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class PermissionDescription (
val name: String,
val description: String,
@SerialName("icon_uri")
val iconUri: String
) {
package com.microsoft.did.sdk.credentials
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class PermissionDescription (
val name: String,
val description: String,
@SerialName("icon_uri")
val iconUri: String
) {
}

Просмотреть файл

@ -1,27 +1,27 @@
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.protocols.jose.DidKeyResolver
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsToken
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.Required
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("jws")
data class SignedClaimDetail(
val data: String
): ClaimDetail {
@Required
override val type: String
get() = "jws"
@ImplicitReflectionSerializer
override suspend fun verify(cryptoOperations: CryptoOperations, resolver: IResolver, logger: ILogger) {
val claimDetail = JwsToken(data, logger = logger)
DidKeyResolver.verifyJws(claimDetail, cryptoOperations, resolver, logger = logger)
}
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.protocols.jose.DidKeyResolver
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsToken
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.Required
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("jws")
data class SignedClaimDetail(
val data: String
): ClaimDetail {
@Required
override val type: String
get() = "jws"
@ImplicitReflectionSerializer
override suspend fun verify(cryptoOperations: CryptoOperations, resolver: IResolver, logger: ILogger) {
val claimDetail = JwsToken(data, logger = logger)
DidKeyResolver.verifyJws(claimDetail, cryptoOperations, resolver, logger = logger)
}
}

Просмотреть файл

@ -1,22 +1,22 @@
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
import kotlinx.serialization.Required
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("unsigned")
data class UnsignedClaimDetail(
val data: List<Map<String, String>>
): ClaimDetail {
@Required
override val type: String
get() = "unsigned"
override suspend fun verify(cryptoOperations: CryptoOperations, resolver: IResolver, logger: ILogger) {
// nothing to do
}
package com.microsoft.did.sdk.credentials
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
import kotlinx.serialization.Required
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("unsigned")
data class UnsignedClaimDetail(
val data: List<Map<String, String>>
): ClaimDetail {
@Required
override val type: String
get() = "unsigned"
override suspend fun verify(cryptoOperations: CryptoOperations, resolver: IResolver, logger: ILogger) {
// nothing to do
}
}

Просмотреть файл

@ -1,148 +1,148 @@
package com.microsoft.did.sdk.crypto.keyStore
import com.microsoft.did.sdk.crypto.keys.*
import com.microsoft.did.sdk.crypto.protocols.jose.JwaCryptoConverter
import com.microsoft.did.sdk.utilities.ILogger
class InMemoryKeyStore(logger: ILogger): IKeyStore(logger) {
private val secretKeys: MutableMap<String, KeyContainer<SecretKey>> = mutableMapOf()
private val privateKeys: MutableMap<String, KeyContainer<PrivateKey>> = mutableMapOf()
private val publicKeys: MutableMap<String, KeyContainer<PublicKey>> = mutableMapOf()
override fun getSecretKey(keyReference: String): KeyContainer<SecretKey> {
return secretKeys[keyReference]?: throw logger.error("key $keyReference does not exist.")
}
override fun getPrivateKey(keyReference: String): KeyContainer<PrivateKey> {
return privateKeys[keyReference]?: throw logger.error("key $keyReference does not exist.")
}
override fun getPublicKey(keyReference: String): KeyContainer<PublicKey> {
return if (publicKeys.containsKey(keyReference)) {
publicKeys[keyReference]!!
} else {
val keyContainer = privateKeys[keyReference] ?: throw logger.error("key $keyReference does not exist.")
KeyContainer(
keyContainer.kty,
keyContainer.keys.map { it.getPublicKey() },
keyContainer.use,
keyContainer.alg
)
}
}
override fun getSecretKeyById(keyId: String): SecretKey? {
return findKeyMatchingIdIn(secretKeys, keyId)
}
override fun getPrivateKeyById(keyId: String): PrivateKey? {
return findKeyMatchingIdIn(privateKeys, keyId)
}
override fun getPublicKeyById(keyId: String): PublicKey? {
return findKeyMatchingIdIn(publicKeys, keyId) ?:
findKeyMatchingIdIn(privateKeys, keyId)?.getPublicKey()
}
private fun <T: IKeyStoreItem> findKeyMatchingIdIn(map: Map<String, KeyContainer<T>>, keyId: String): T? {
return map.map {
val key = it.value.keys.firstOrNull {
it.kid == keyId
}
if (key != null) {
key
} else {
null
}
}.firstOrNull {
it != null
}
}
override fun save(keyReference: String, key: SecretKey) {
if (secretKeys.containsKey(keyReference)) {
val keyContainer = secretKeys[keyReference]!!
val keys = keyContainer.keys.toMutableList()
keys.add(key)
secretKeys[keyReference] = KeyContainer(
keyContainer.kty,
keys,
keyContainer.use,
keyContainer.alg
)
} else {
secretKeys[keyReference] = KeyContainer<SecretKey>(
key.kty,
listOf(key),
key.use,
key.alg?.let { JwaCryptoConverter.jwaAlgToWebCrypto(it, logger = logger) }
)
}
}
override fun save(keyReference: String, key: PrivateKey) {
if (privateKeys.containsKey(keyReference)) {
val keyContainer = privateKeys[keyReference]!!
val keys = keyContainer.keys.toMutableList()
keys.add(key)
privateKeys[keyReference] = KeyContainer(
keyContainer.kty,
keys,
keyContainer.use,
keyContainer.alg
)
} else {
privateKeys[keyReference] = KeyContainer<PrivateKey>(
key.kty,
listOf(key),
key.use,
key.alg?.let { JwaCryptoConverter.jwaAlgToWebCrypto(it, logger = logger) }
)
}
}
override fun save(keyReference: String, key: PublicKey) {
if (publicKeys.containsKey(keyReference)) {
val keyContainer = publicKeys[keyReference]!!
val keys = keyContainer.keys.toMutableList()
keys.add(key)
publicKeys[keyReference] = KeyContainer(
keyContainer.kty,
keys,
keyContainer.use,
keyContainer.alg
)
} else {
publicKeys[keyReference] = KeyContainer<PublicKey>(
key.kty,
listOf(key),
key.use,
key.alg?.let { JwaCryptoConverter.jwaAlgToWebCrypto(it, logger = logger) }
)
}
}
override fun list(): Map<String, KeyStoreListItem> {
val result = mutableMapOf<String, KeyStoreListItem>()
secretKeys.forEach {
result[it.key] = KeyStoreListItem(
it.value.kty,
it.value.keys.filter { it.kid != null }.map { it.kid!! }.toMutableList()
)
}
privateKeys.forEach {
result[it.key] = KeyStoreListItem(
it.value.kty,
it.value.keys.filter { it.kid != null }.map { it.kid!! }.toMutableList()
)
}
publicKeys.forEach {
result[it.key] = KeyStoreListItem(
it.value.kty,
it.value.keys.filter { it.kid != null }.map { it.kid!! }.toMutableList()
)
}
return result
}
package com.microsoft.did.sdk.crypto.keyStore
import com.microsoft.did.sdk.crypto.keys.*
import com.microsoft.did.sdk.crypto.protocols.jose.JwaCryptoConverter
import com.microsoft.did.sdk.utilities.ILogger
class InMemoryKeyStore(logger: ILogger): IKeyStore(logger) {
private val secretKeys: MutableMap<String, KeyContainer<SecretKey>> = mutableMapOf()
private val privateKeys: MutableMap<String, KeyContainer<PrivateKey>> = mutableMapOf()
private val publicKeys: MutableMap<String, KeyContainer<PublicKey>> = mutableMapOf()
override fun getSecretKey(keyReference: String): KeyContainer<SecretKey> {
return secretKeys[keyReference]?: throw logger.error("key $keyReference does not exist.")
}
override fun getPrivateKey(keyReference: String): KeyContainer<PrivateKey> {
return privateKeys[keyReference]?: throw logger.error("key $keyReference does not exist.")
}
override fun getPublicKey(keyReference: String): KeyContainer<PublicKey> {
return if (publicKeys.containsKey(keyReference)) {
publicKeys[keyReference]!!
} else {
val keyContainer = privateKeys[keyReference] ?: throw logger.error("key $keyReference does not exist.")
KeyContainer(
keyContainer.kty,
keyContainer.keys.map { it.getPublicKey() },
keyContainer.use,
keyContainer.alg
)
}
}
override fun getSecretKeyById(keyId: String): SecretKey? {
return findKeyMatchingIdIn(secretKeys, keyId)
}
override fun getPrivateKeyById(keyId: String): PrivateKey? {
return findKeyMatchingIdIn(privateKeys, keyId)
}
override fun getPublicKeyById(keyId: String): PublicKey? {
return findKeyMatchingIdIn(publicKeys, keyId) ?:
findKeyMatchingIdIn(privateKeys, keyId)?.getPublicKey()
}
private fun <T: IKeyStoreItem> findKeyMatchingIdIn(map: Map<String, KeyContainer<T>>, keyId: String): T? {
return map.map {
val key = it.value.keys.firstOrNull {
it.kid == keyId
}
if (key != null) {
key
} else {
null
}
}.firstOrNull {
it != null
}
}
override fun save(keyReference: String, key: SecretKey) {
if (secretKeys.containsKey(keyReference)) {
val keyContainer = secretKeys[keyReference]!!
val keys = keyContainer.keys.toMutableList()
keys.add(key)
secretKeys[keyReference] = KeyContainer(
keyContainer.kty,
keys,
keyContainer.use,
keyContainer.alg
)
} else {
secretKeys[keyReference] = KeyContainer<SecretKey>(
key.kty,
listOf(key),
key.use,
key.alg?.let { JwaCryptoConverter.jwaAlgToWebCrypto(it, logger = logger) }
)
}
}
override fun save(keyReference: String, key: PrivateKey) {
if (privateKeys.containsKey(keyReference)) {
val keyContainer = privateKeys[keyReference]!!
val keys = keyContainer.keys.toMutableList()
keys.add(key)
privateKeys[keyReference] = KeyContainer(
keyContainer.kty,
keys,
keyContainer.use,
keyContainer.alg
)
} else {
privateKeys[keyReference] = KeyContainer<PrivateKey>(
key.kty,
listOf(key),
key.use,
key.alg?.let { JwaCryptoConverter.jwaAlgToWebCrypto(it, logger = logger) }
)
}
}
override fun save(keyReference: String, key: PublicKey) {
if (publicKeys.containsKey(keyReference)) {
val keyContainer = publicKeys[keyReference]!!
val keys = keyContainer.keys.toMutableList()
keys.add(key)
publicKeys[keyReference] = KeyContainer(
keyContainer.kty,
keys,
keyContainer.use,
keyContainer.alg
)
} else {
publicKeys[keyReference] = KeyContainer<PublicKey>(
key.kty,
listOf(key),
key.use,
key.alg?.let { JwaCryptoConverter.jwaAlgToWebCrypto(it, logger = logger) }
)
}
}
override fun list(): Map<String, KeyStoreListItem> {
val result = mutableMapOf<String, KeyStoreListItem>()
secretKeys.forEach {
result[it.key] = KeyStoreListItem(
it.value.kty,
it.value.keys.filter { it.kid != null }.map { it.kid!! }.toMutableList()
)
}
privateKeys.forEach {
result[it.key] = KeyStoreListItem(
it.value.kty,
it.value.keys.filter { it.kid != null }.map { it.kid!! }.toMutableList()
)
}
publicKeys.forEach {
result[it.key] = KeyStoreListItem(
it.value.kty,
it.value.keys.filter { it.kid != null }.map { it.kid!! }.toMutableList()
)
}
return result
}
}

Просмотреть файл

@ -1,9 +1,9 @@
package com.microsoft.did.sdk.crypto.keyStore
import com.microsoft.did.sdk.crypto.keys.KeyType
data class KeyStoreListItem (val kty: KeyType, val kids: MutableList<String>) {
fun getLatestKeyId(): String {
return kids.first()
}
package com.microsoft.did.sdk.crypto.keyStore
import com.microsoft.did.sdk.crypto.keys.KeyType
data class KeyStoreListItem (val kty: KeyType, val kids: MutableList<String>) {
fun getLatestKeyId(): String {
return kids.first()
}
}

Просмотреть файл

@ -1,7 +1,7 @@
package com.microsoft.did.sdk.crypto.keys
import com.microsoft.did.sdk.utilities.ILogger
interface IKeyStoreItem {
val kid: String
package com.microsoft.did.sdk.crypto.keys
import com.microsoft.did.sdk.utilities.ILogger
interface IKeyStoreItem {
val kid: String
}

Просмотреть файл

@ -1,14 +1,14 @@
package com.microsoft.did.sdk.crypto.keys
import com.microsoft.did.sdk.crypto.models.KeyUse
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
class KeyContainer<T: IKeyStoreItem> (val kty: KeyType, val keys: List<T> = emptyList(), val use: KeyUse? = null, val alg: Algorithm? = null) {
fun getKey(id: String? = null): T {
return if (id.isNullOrBlank()) {
keys.first()
} else {
keys.first { it.kid == id }
}
}
package com.microsoft.did.sdk.crypto.keys
import com.microsoft.did.sdk.crypto.models.KeyUse
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
class KeyContainer<T: IKeyStoreItem> (val kty: KeyType, val keys: List<T> = emptyList(), val use: KeyUse? = null, val alg: Algorithm? = null) {
fun getKey(id: String? = null): T {
return if (id.isNullOrBlank()) {
keys.first()
} else {
keys.first { it.kid == id }
}
}
}

Просмотреть файл

@ -1,46 +1,46 @@
package com.microsoft.did.sdk.crypto.keys.rsa
import com.microsoft.did.sdk.crypto.keys.KeyType
import com.microsoft.did.sdk.crypto.keys.PrivateKey
import com.microsoft.did.sdk.crypto.keys.PublicKey
import com.microsoft.did.sdk.crypto.models.webCryptoApi.JsonWebKey
import com.microsoft.did.sdk.utilities.ILogger
class RsaPrivateKey(jwk: JsonWebKey, logger: ILogger): PrivateKey(jwk, logger) {
override var kty = KeyType.RSA
override var alg: String? = if (key.alg != null) key.alg!! else "RS256"
val n = key.n
val e = key.e
val d = key.d
val p = key.p
val q = key.q
val dp = key.dp
val dq = key.dq
val qi = key.qi
val oth = key.oth
override fun toJWK(): JsonWebKey {
return JsonWebKey(
kty = kty.value,
alg = alg,
kid = kid,
key_ops = key_ops?.map { use -> use.value },
use = use?.value,
n = this.n,
e = this.e,
d = this.d,
p = this.p,
q = this.q,
dp = this.dp,
dq = this.dq,
qi = this.qi,
oth = this.oth
)
}
override fun getPublicKey(): PublicKey {
return RsaPublicKey(this.toJWK(), logger = logger)
}
package com.microsoft.did.sdk.crypto.keys.rsa
import com.microsoft.did.sdk.crypto.keys.KeyType
import com.microsoft.did.sdk.crypto.keys.PrivateKey
import com.microsoft.did.sdk.crypto.keys.PublicKey
import com.microsoft.did.sdk.crypto.models.webCryptoApi.JsonWebKey
import com.microsoft.did.sdk.utilities.ILogger
class RsaPrivateKey(jwk: JsonWebKey, logger: ILogger): PrivateKey(jwk, logger) {
override var kty = KeyType.RSA
override var alg: String? = if (key.alg != null) key.alg!! else "RS256"
val n = key.n
val e = key.e
val d = key.d
val p = key.p
val q = key.q
val dp = key.dp
val dq = key.dq
val qi = key.qi
val oth = key.oth
override fun toJWK(): JsonWebKey {
return JsonWebKey(
kty = kty.value,
alg = alg,
kid = kid,
key_ops = key_ops?.map { use -> use.value },
use = use?.value,
n = this.n,
e = this.e,
d = this.d,
p = this.p,
q = this.q,
dp = this.dp,
dq = this.dq,
qi = this.qi,
oth = this.oth
)
}
override fun getPublicKey(): PublicKey {
return RsaPublicKey(this.toJWK(), logger = logger)
}
}

Просмотреть файл

@ -1,29 +1,29 @@
package com.microsoft.did.sdk.crypto.keys.rsa
import com.microsoft.did.sdk.crypto.keys.KeyType
import com.microsoft.did.sdk.crypto.keys.PublicKey
import com.microsoft.did.sdk.crypto.models.webCryptoApi.JsonWebKey
import com.microsoft.did.sdk.utilities.ILogger
class RsaPublicKey(jwk: JsonWebKey, logger: ILogger): PublicKey(jwk, logger = logger) {
override fun minimumAlphabeticJwk(): String {
return "{\"e\":\"${e}\",\"kty\":\"${kty.value}\",\"n\":\"${n}\"}"
}
override var kty = KeyType.RSA
val n = key.n
val e = key.e
override fun toJWK(): JsonWebKey {
return JsonWebKey(
kty = kty.value,
alg = alg,
use = use?.value,
key_ops = key_ops?.map { use -> use.value },
kid = kid,
e = e,
n = n
)
}
package com.microsoft.did.sdk.crypto.keys.rsa
import com.microsoft.did.sdk.crypto.keys.KeyType
import com.microsoft.did.sdk.crypto.keys.PublicKey
import com.microsoft.did.sdk.crypto.models.webCryptoApi.JsonWebKey
import com.microsoft.did.sdk.utilities.ILogger
class RsaPublicKey(jwk: JsonWebKey, logger: ILogger): PublicKey(jwk, logger = logger) {
override fun minimumAlphabeticJwk(): String {
return "{\"e\":\"${e}\",\"kty\":\"${kty.value}\",\"n\":\"${n}\"}"
}
override var kty = KeyType.RSA
val n = key.n
val e = key.e
override fun toJWK(): JsonWebKey {
return JsonWebKey(
kty = kty.value,
alg = alg,
use = use?.value,
key_ops = key_ops?.map { use -> use.value },
kid = kid,
e = e,
n = n
)
}
}

Просмотреть файл

@ -1,37 +1,37 @@
package com.microsoft.did.sdk.crypto.models
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
import com.microsoft.did.sdk.crypto.models.webCryptoApi.W3cCryptoApiConstants
import com.microsoft.did.sdk.utilities.ILogger
// convenience class for SHA algorithms
class Sha(algorithm: Algorithm) {
companion object {
val Sha1 = Algorithm(
name = W3cCryptoApiConstants.Sha1.value
)
val Sha224 = Algorithm(
name = W3cCryptoApiConstants.Sha224.value
)
val Sha256 = Algorithm(
name = W3cCryptoApiConstants.Sha256.value
)
val Sha384 = Algorithm(
name = W3cCryptoApiConstants.Sha384.value
)
val Sha512 = Algorithm(
name = W3cCryptoApiConstants.Sha512.value
)
fun get(length: Int, logger: ILogger): Algorithm {
return when (length) {
1 -> Sha1
224 -> Sha224
256 -> Sha256
384 -> Sha384
512 -> Sha512
else -> throw logger.error("No SHA at this length.")
}
}
}
package com.microsoft.did.sdk.crypto.models
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
import com.microsoft.did.sdk.crypto.models.webCryptoApi.W3cCryptoApiConstants
import com.microsoft.did.sdk.utilities.ILogger
// convenience class for SHA algorithms
class Sha(algorithm: Algorithm) {
companion object {
val Sha1 = Algorithm(
name = W3cCryptoApiConstants.Sha1.value
)
val Sha224 = Algorithm(
name = W3cCryptoApiConstants.Sha224.value
)
val Sha256 = Algorithm(
name = W3cCryptoApiConstants.Sha256.value
)
val Sha384 = Algorithm(
name = W3cCryptoApiConstants.Sha384.value
)
val Sha512 = Algorithm(
name = W3cCryptoApiConstants.Sha512.value
)
fun get(length: Int, logger: ILogger): Algorithm {
return when (length) {
1 -> Sha1
224 -> Sha224
256 -> Sha256
384 -> Sha384
512 -> Sha512
else -> throw logger.error("No SHA at this length.")
}
}
}
}

Просмотреть файл

@ -1,6 +1,6 @@
package com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithms
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
class AesKeyGenParams(name: String, val length: UShort, additionalParams: Map<String, String> = emptyMap()): Algorithm(name, additionalParams) {
package com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithms
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
class AesKeyGenParams(name: String, val length: UShort, additionalParams: Map<String, String> = emptyMap()): Algorithm(name, additionalParams) {
}

Просмотреть файл

@ -1,5 +1,5 @@
package com.microsoft.did.sdk.crypto.models.webCryptoApi
open class RsaHashedKeyAlgorithm (modulusLength: ULong, publicExponent: ULong, val hash: Algorithm,
additionalParams: Map<String, String> = emptyMap()):
package com.microsoft.did.sdk.crypto.models.webCryptoApi
open class RsaHashedKeyAlgorithm (modulusLength: ULong, publicExponent: ULong, val hash: Algorithm,
additionalParams: Map<String, String> = emptyMap()):
RsaKeyAlgorithm(modulusLength, publicExponent, additionalParams)

Просмотреть файл

@ -1,8 +1,8 @@
package com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithms
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
import com.microsoft.did.sdk.crypto.models.webCryptoApi.RsaKeyAlgorithm
open class RsaHashedKeyGenParams(modulusLength: ULong, publicExponent: ULong,
val hash: Algorithm, additionalParams: Map<String, String> = emptyMap()):
package com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithms
import com.microsoft.did.sdk.crypto.models.webCryptoApi.Algorithm
import com.microsoft.did.sdk.crypto.models.webCryptoApi.RsaKeyAlgorithm
open class RsaHashedKeyGenParams(modulusLength: ULong, publicExponent: ULong,
val hash: Algorithm, additionalParams: Map<String, String> = emptyMap()):
RsaKeyAlgorithm(modulusLength, publicExponent, additionalParams)

Просмотреть файл

@ -1,4 +1,4 @@
package com.microsoft.did.sdk.crypto.models.webCryptoApi
open class RsaKeyAlgorithm(val modulusLength: ULong, val publicExponent: ULong,
package com.microsoft.did.sdk.crypto.models.webCryptoApi
open class RsaKeyAlgorithm(val modulusLength: ULong, val publicExponent: ULong,
additionalParams: Map<String, String> = emptyMap()): Algorithm(W3cCryptoApiConstants.RsaSsaPkcs1V15.value, additionalParams)

Просмотреть файл

@ -1,5 +1,5 @@
package com.microsoft.did.sdk.crypto.plugins
import com.microsoft.did.sdk.crypto.models.webCryptoApi.SubtleCrypto
package com.microsoft.did.sdk.crypto.plugins
import com.microsoft.did.sdk.crypto.models.webCryptoApi.SubtleCrypto
data class SubtleCryptoMapItem (val subtleCrypto: SubtleCrypto, val scope: SubtleCryptoScope)

Просмотреть файл

@ -1,7 +1,7 @@
package com.microsoft.did.sdk.crypto.plugins
enum class SubtleCryptoScope {
All,
Private,
Public
package com.microsoft.did.sdk.crypto.plugins
enum class SubtleCryptoScope {
All,
Private,
Public
}

Просмотреть файл

@ -1,49 +1,49 @@
package com.microsoft.did.sdk.crypto.protocols.jose
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.keys.PublicKey
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsToken
import com.microsoft.did.sdk.identifier.Identifier
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
import kotlinx.serialization.ImplicitReflectionSerializer
object DidKeyResolver {
suspend fun resolveIdentiferFromKid(kid: String, crypto: CryptoOperations, resolver: IResolver, logger: ILogger): Identifier {
val did = Regex("^([^#]+)#.+$").matchEntire(kid) ?: throw logger.error("No identifier found in key id")
return resolver.resolve(did.groupValues[1], crypto)
}
suspend fun resolveKeyFromKid(kid: String, crypto: CryptoOperations, resolver: IResolver, logger: ILogger): PublicKey {
val identifier = resolveIdentiferFromKid(kid, crypto, resolver, logger = logger)
val did = Regex("^[^#]+(#.+)$").matchEntire(kid)!!
return identifier.document.publicKeys.filter {
it.publicKeyJwk.kid?.endsWith(did.groupValues[1]) ?: false ||
it.id.endsWith(did.groupValues[1])
}.firstOrNull()?.toPublicKey(logger = logger) ?: throw logger.error("Could not find key $kid")
}
@ImplicitReflectionSerializer
suspend fun verifyJws(jws: JwsToken, crypto: CryptoOperations, forDid: Identifier, logger: ILogger) {
val keys = forDid.document.publicKeys.map {
it.toPublicKey(logger = logger)
}
jws.verify(crypto, keys)
}
@ImplicitReflectionSerializer
suspend fun verifyJws(jws: JwsToken, crypto: CryptoOperations, resolver: IResolver, forDid: String? = null, logger: ILogger) {
if (forDid.isNullOrBlank()) {
val sender = resolver.resolve(forDid!!, crypto)
// verify the request
verifyJws(jws, crypto, sender, logger = logger)
} else {
val keys = mutableListOf<PublicKey>()
jws.signatures.forEachIndexed { index, signature ->
val kid = signature.getKid(logger = logger) ?: throw logger.error("Could not find kid in signature $index")
keys.add(resolveKeyFromKid(kid, crypto, resolver, logger))
}
jws.verify(crypto, keys)
}
}
package com.microsoft.did.sdk.crypto.protocols.jose
import com.microsoft.did.sdk.crypto.CryptoOperations
import com.microsoft.did.sdk.crypto.keys.PublicKey
import com.microsoft.did.sdk.crypto.protocols.jose.jws.JwsToken
import com.microsoft.did.sdk.identifier.Identifier
import com.microsoft.did.sdk.resolvers.IResolver
import com.microsoft.did.sdk.utilities.ILogger
import kotlinx.serialization.ImplicitReflectionSerializer
object DidKeyResolver {
suspend fun resolveIdentiferFromKid(kid: String, crypto: CryptoOperations, resolver: IResolver, logger: ILogger): Identifier {
val did = Regex("^([^#]+)#.+$").matchEntire(kid) ?: throw logger.error("No identifier found in key id")
return resolver.resolve(did.groupValues[1], crypto)
}
suspend fun resolveKeyFromKid(kid: String, crypto: CryptoOperations, resolver: IResolver, logger: ILogger): PublicKey {
val identifier = resolveIdentiferFromKid(kid, crypto, resolver, logger = logger)
val did = Regex("^[^#]+(#.+)$").matchEntire(kid)!!
return identifier.document.publicKeys.filter {
it.publicKeyJwk.kid?.endsWith(did.groupValues[1]) ?: false ||
it.id.endsWith(did.groupValues[1])
}.firstOrNull()?.toPublicKey(logger = logger) ?: throw logger.error("Could not find key $kid")
}
@ImplicitReflectionSerializer
suspend fun verifyJws(jws: JwsToken, crypto: CryptoOperations, forDid: Identifier, logger: ILogger) {
val keys = forDid.document.publicKeys.map {
it.toPublicKey(logger = logger)
}
jws.verify(crypto, keys)
}
@ImplicitReflectionSerializer
suspend fun verifyJws(jws: JwsToken, crypto: CryptoOperations, resolver: IResolver, forDid: String? = null, logger: ILogger) {
if (forDid.isNullOrBlank()) {
val sender = resolver.resolve(forDid!!, crypto)
// verify the request
verifyJws(jws, crypto, sender, logger = logger)
} else {
val keys = mutableListOf<PublicKey>()
jws.signatures.forEachIndexed { index, signature ->
val kid = signature.getKid(logger = logger) ?: throw logger.error("Could not find kid in signature $index")
keys.add(resolveKeyFromKid(kid, crypto, resolver, logger))
}
jws.verify(crypto, keys)
}
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше