This commit is contained in:
Jonas Roussel
2020-06-12 13:35:07 +02:00
parent 3377ae8b0b
commit 997fe84f7f
8 changed files with 168 additions and 46 deletions

View File

@ -1,15 +1,21 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
import 'package:pointycastle/pointycastle.dart' hide PrivateKey, PublicKey;
import 'package:rsa_pkcs/rsa_pkcs.dart' hide RSAPrivateKey, RSAPublicKey;
abstract class JWTAlgorithm {
static const HS256 = HS256Algorithm();
static const RS256 = RS256Algorithm();
static JWTAlgorithm fromName(String name) {
switch (name) {
case 'HS256':
return JWTAlgorithm.HS256;
case 'RS256':
return JWTAlgorithm.RS256;
default:
throw JWTInvalidError('unknown algorithm');
}
@ -18,8 +24,8 @@ abstract class JWTAlgorithm {
const JWTAlgorithm();
String get name;
List<int> sign(String key, List<int> body);
bool verify(String key, List<int> body, List<int> signature);
List<int> sign(Key key, List<int> body);
bool verify(Key key, List<int> body, List<int> signature);
}
class HS256Algorithm extends JWTAlgorithm {
@ -29,13 +35,18 @@ class HS256Algorithm extends JWTAlgorithm {
String get name => 'HS256';
@override
List<int> sign(String key, List<int> body) {
final hmac = Hmac(sha256, utf8.encode(key));
List<int> sign(Key key, List<int> body) {
assert(key is SecretKey, 'key must be a SecretKey');
final secretKey = key as SecretKey;
final hmac = Hmac(sha256, utf8.encode(secretKey.key));
return hmac.convert(body).bytes;
}
@override
bool verify(String key, List<int> body, List<int> signature) {
bool verify(Key key, List<int> body, List<int> signature) {
assert(key is SecretKey, 'key must be a SecretKey');
final actual = sign(key, body);
if (actual.length != signature.length) return false;
@ -47,3 +58,80 @@ class HS256Algorithm extends JWTAlgorithm {
return true;
}
}
class RS256Algorithm extends JWTAlgorithm {
const RS256Algorithm();
@override
String get name => 'RS256';
@override
List<int> sign(Key key, List<int> body) {
assert(key is PrivateKey, 'key must be a PrivateKey');
final privateKey = key as PrivateKey;
final parser = RSAPKCSParser();
RSAKeyPair pair;
try {
pair = parser.parsePEM(privateKey.key, password: privateKey.passphrase);
assert(pair.private != null);
} catch (ex) {
throw JWTInvalidError('invalid private RSA key');
}
final signer = Signer('SHA-256/RSA');
final params = ParametersWithRandom(
PrivateKeyParameter<RSAPrivateKey>(
RSAPrivateKey(
pair.private.modulus,
pair.private.privateExponent,
pair.private.prime1,
pair.private.prime2,
),
),
SecureRandom('AES/CTR/PRNG'),
);
signer.init(true, params);
RSASignature signature = signer.generateSignature(Uint8List.fromList(body));
return signature.bytes.toList(growable: false);
}
@override
bool verify(Key key, List<int> body, List<int> signature) {
assert(key is PublicKey, 'key must be a PublicKey');
final publicKey = key as PublicKey;
final parser = RSAPKCSParser();
RSAKeyPair pair;
try {
pair = parser.parsePEM(publicKey.key, password: publicKey.passphrase);
assert(pair.public != null);
} catch (ex) {
throw JWTInvalidError('invalid public RSA key');
}
try {
final signer = Signer('SHA-256/RSA');
final params = ParametersWithRandom(
PublicKeyParameter<RSAPublicKey>(
RSAPublicKey(
pair.public.modulus,
BigInt.from(pair.public.publicExponent),
),
),
SecureRandom('AES/CTR/PRNG'),
);
signer.init(false, params);
return signer.verifySignature(Uint8List.fromList(body), RSASignature(Uint8List.fromList(signature)));
} catch (ex) {
return false;
}
}
}