This commit is contained in:
Josh Mandel 2020-10-20 11:43:42 -05:00
Родитель eafac6ad37
Коммит 61c91032e0
5 изменённых файлов: 138 добавлений и 65 удалений

49
package-lock.json сгенерированный
Просмотреть файл

@ -3425,6 +3425,14 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.1.tgz",
"integrity": "sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g=="
},
"@types/node-jose": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@types/node-jose/-/node-jose-1.1.5.tgz",
"integrity": "sha512-fScnd7GdaHC31PYouWR0xYSOXQLrmxPhLM92CYlVy4UetSwis2u5e6khMazj1Xyidt8zPeRU0PHLmI+mpamhGQ==",
"requires": {
"@types/node": "*"
}
},
"@types/normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
@ -14932,6 +14940,11 @@
"chalk": "^2.0.1"
}
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -15930,6 +15943,39 @@
"integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
"dev": true
},
"node-jose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/node-jose/-/node-jose-2.0.0.tgz",
"integrity": "sha512-j8zoFze1gijl8+DK/dSXXqX7+o2lMYv1XS+ptnXgGV/eloQaqq1YjNtieepbKs9jBS4WTnMOqyKSaQuunJzx0A==",
"requires": {
"base64url": "^3.0.1",
"buffer": "^5.5.0",
"es6-promise": "^4.2.8",
"lodash": "^4.17.15",
"long": "^4.0.0",
"node-forge": "^0.10.0",
"pako": "^1.0.11",
"process": "^0.11.10",
"uuid": "^3.3.3"
},
"dependencies": {
"node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
},
"pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
"node-libs-browser": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
@ -17906,8 +17952,7 @@
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
"dev": true
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
},
"process-nextick-args": {
"version": "2.0.1",

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

@ -21,6 +21,7 @@
"license": "ISC",
"dependencies": {
"@decentralized-identity/sidetree": "^0.9.1",
"@types/node-jose": "^1.1.5",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.6",
"axios": "^0.19.2",
@ -42,6 +43,7 @@
"json-canonicalize": "^1.0.3",
"jsonschema": "^1.2.10",
"multihashes": "^0.4.19",
"node-jose": "^2.0.0",
"qr-scanner": "^1.1.1",
"qrcode": "^1.4.4",
"querystring": "^0.2.0",

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

@ -18,17 +18,20 @@ export async function verifyJws(jws: string, {
}
// TODO remove 'JwsVerificationKey2020' when prototypes have updated
const ENCRYPTION_KEY_TYPES = ['RSAEncryptionPublicKey', 'JwsVerificationKey2020'];
const ENCRYPTION_KEY_TYPES = ['JsonWebKey2020', 'JwsVerificationKey2020'];
export async function encryptFor(jws: string, did: string, { generateEncryptionKey }: KeyGenerators, keyId?: string) {
export async function encryptFor(jws: string, did: string, { generateEncryptionKey }: KeyGenerators, keyIdIn?: string) {
const didDoc = (await axios.get(resolveUrl + encodeURIComponent(did))).data;
const encryptionKeys = didDoc.publicKey.filter(k => ENCRYPTION_KEY_TYPES.includes(k.type));
const encryptionKey = keyId ? encryptionKeys.filter(k => k.id === keyId)[0] :
encryptionKeys[0];
const keyId = keyIdIn ? keyIdIn : encryptionKeys[0].id;
const encryptionKey = encryptionKeys.filter(k => k.id === keyId)[0]
const ek = await generateEncryptionKey(encryptionKey.publicKeyJwk);
return ek.encrypt({ kid: encryptionKey.kid }, jws);
const ek = await generateEncryptionKey({
...encryptionKey.publicKeyJwk,
kid: keyId
});
return ek.encrypt({ kid: keyId }, jws);
}
const resolveKeyId = async (kid: string): Promise<JsonWebKey> => {
const fragment = '#' + kid.split('#')[1];
@ -57,7 +60,7 @@ export async function generateDid({ signingPublicJwk, encryptionPublicJwk, recov
}, {
id: 'encryption-key-1',
purpose: ['general', 'auth'],
type: 'RSAEncryptionPublicKey',
type: 'JsonWebKey2020',
jwk: encryptionPublicJwk
}]
}]

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

@ -1,6 +1,7 @@
import base64url from 'base64url';
import { randomBytes, PublicKeyInput } from 'crypto';
import jose, { JWT } from 'jose';
import jjose, { JWT } from 'jose';
import jose from 'node-jose';
import secp256k1 from 'secp256k1';
import { EncryptionKey, SigningKey, KeyGenerators } from './KeyTypes';
@ -18,36 +19,58 @@ export const keyGenerators: KeyGenerators = {
export function encodeSection (data: any): string {
return base64url.encode(JSON.stringify(data));
}
export async function generateEncryptionKey (inputPublic?: JsonWebKey, inputPrivate?: JsonWebKey): Promise<EncryptionKey> {
let publicKey;
let privateKey = null;
let privateJwk;
const keystore = jose.JWK.createKeyStore();
const encryptionKeyPros = {
"kty": "EC",
"crv": "P-256",
"alg": 'ECDH-ES',
}
export async function generateEncryptionKey(inputPublic?: JsonWebKey, inputPrivate?: JsonWebKey): Promise<EncryptionKey> {
let publicKey: jose.JWK.Key | null = null;
let privateKey: jose.JWK.Key | null = null;
if (inputPublic) {
publicKey = jose.JWK.asKey(inputPublic as PublicKeyInput);
publicKey = await jose.JWK.asKey(inputPublic);
if (inputPrivate) {
privateKey = await jose.JWK.asKey(inputPrivate)
}
} else {
privateKey = jose.JWK.generateSync('RSA');
publicKey = privateKey;
privateJwk = privateKey.toJWK(true);
publicKey = privateKey = await keystore.generate("EC", "P-256", encryptionKeyPros);
}
const publicJwkFull = publicKey.toJWK(false);
const publicJwk = {
alg: publicJwkFull.alg,
e: publicJwkFull.e,
kty: publicJwkFull.kty,
n: publicJwkFull.n
};
const publicJwk = publicKey.toJSON(false);
const privateJwk = privateKey ? privateKey.toJSON(true) : null;
return {
decrypt: async (payload) => {
return (await jose.JWE.decrypt(payload, privateKey)).toString();
// TODO assertions about header
if (!privateKey) {
throw new Error('Cannot decrypt with a public key');
}
try {
const ret = await jose.JWE.createDecrypt(privateKey).decrypt(payload);
return ret.plaintext.toString();
} catch (e) {
console.log("Error decrypting", e)
}
},
encrypt: async (header, payload) => {
return jose.JWE.encrypt(payload, publicJwk, {
...header,
enc: "A256GCM"
});
const input = payload;
const jwe = await jose.JWE
.createEncrypt({
format: 'compact',
fields: {
...header,
enc: 'A256GCM',
}
}, publicKey)
.update(jose.util.asBuffer(input)).final()
console.log("Encrypt", header, (publicKey as any).kid,
jwe
)
return jwe;
},
publicJwk,
privateJwk
@ -59,9 +82,9 @@ export async function generateSigningKey (inputPublic?: JsonWebKey, inputPrivate
let privateKey = null;
let privateJwk;
if (inputPublic) {
publicKey = jose.JWK.asKey(inputPublic as PublicKeyInput);
publicKey = jjose.JWK.asKey(inputPublic as PublicKeyInput);
} else {
privateKey = jose.JWK.generateSync('EC', 'secp256k1');
privateKey = jjose.JWK.generateSync('EC', 'secp256k1');
publicKey = privateKey;
privateJwk = privateKey.toJWK(true);
}
@ -77,12 +100,12 @@ export async function generateSigningKey (inputPublic?: JsonWebKey, inputPrivate
return {
sign: async (header, payload) => {
return jose.JWS.sign(payload, privateKey, header);
return jjose.JWS.sign(payload, privateKey, header);
},
verify: async (jwt) => {
let result;
try {
result = jose.JWS.verify(jwt, publicJwk);
result = jjose.JWS.verify(jwt, publicJwk);
return {
payload: result,
valid: true

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

@ -1,5 +1,7 @@
import base64url from 'base64url';
import { Jose } from 'jose-jwe-jws';
import jose from 'node-jose';
import { EncryptionKey, SigningKey, KeyGenerators } from './KeyTypes';
import elliptic from 'elliptic'
@ -7,6 +9,7 @@ import sha256 from './sha256';
const EC = elliptic.ec;
const ec = new EC('secp256k1');
const keystore = jose.JWK.createKeyStore();
export function encodeSection(data: any): string {
return base64url.encode(JSON.stringify(data));
@ -17,57 +20,54 @@ export const keyGenerators: KeyGenerators = {
generateSigningKey
};
const encryptionKeyPros = {
"kty": "EC",
"crv": "P-256",
"alg": 'ECDH-ES',
}
export async function generateEncryptionKey(inputPublic?: JsonWebKey, inputPrivate?: JsonWebKey): Promise<EncryptionKey> {
let publicKey: CryptoKey | null = null;
let privateKey: CryptoKey | null = null;
let publicKey: jose.JWK.Key | null = null;
let privateKey: jose.JWK.Key | null = null;
if (inputPublic) {
publicKey = await window.crypto.subtle.importKey('jwk', inputPublic, {
name: 'RSA-OAEP',
hash: { name: 'SHA-1' }
}, true, ['encrypt', 'wrapKey']);
publicKey = await jose.JWK.asKey(inputPublic);
if (inputPrivate) {
privateKey = await window.crypto.subtle.importKey('jwk', inputPrivate, {
name: 'RSA-OAEP',
hash: { name: 'SHA-1' }
}, true, ['decrypt', 'unwrapKey']);
privateKey = await jose.JWK.asKey(inputPrivate)
}
} else {
const k = await window.crypto.subtle.generateKey({
name: 'RSA-OAEP',
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: { name: 'SHA-1' }
}, true, // extractable
['encrypt', 'wrapKey', 'decrypt', 'unwrapKey']);
publicKey = k.publicKey;
privateKey = k.privateKey;
publicKey = privateKey = await keystore.generate("EC", "P-256", encryptionKeyPros);
}
const publicJwk = await window.crypto.subtle.exportKey('jwk', publicKey);
const privateJwk = privateKey ? await window.crypto.subtle.exportKey('jwk', privateKey) : null;
const publicJwk = publicKey.toJSON(false);
const privateJwk = privateKey ? privateKey.toJSON(true) : null;
const cryptographer = new Jose.WebCryptographer();
cryptographer.setKeyEncryptionAlgorithm('RSA-OAEP');
cryptographer.setContentEncryptionAlgorithm('A256GCM');
return {
decrypt: async (payload) => {
// TODO assertions about header
if (!privateKey) {
throw new Error('Cannot decrypt with a public key');
}
const decrypter = new Jose.JoseJWE.Decrypter(cryptographer, Promise.resolve(privateKey));
try {
const ret = await decrypter.decrypt(payload);
return ret;
const ret = await jose.JWE.createDecrypt(privateKey).decrypt(payload);
return ret.plaintext.toString();
} catch (e) {
console.log("Error decrypting", e)
}
},
encrypt: async (header, payload) => {
const input = payload;
const encrypter = new Jose.JoseJWE.Encrypter(cryptographer, publicKey);
Object.entries(header).forEach(([k, v]) => encrypter.addHeader(k, v));
return encrypter.encrypt(input);
const jwe = await jose.JWE
.createEncrypt({
format: 'compact',
fields: {
...header,
enc: 'A256GCM',
}
}, publicKey)
.update(jose.util.asBuffer(input)).final()
return jwe;
},
publicJwk,
privateJwk