mirror of
https://github.com/jonasroussel/dart_jsonwebtoken.git
synced 2025-08-26 06:10:22 +08:00
v1.6.0
This commit is contained in:
@ -1,3 +1,12 @@
|
||||
## 1.6.0
|
||||
|
||||
- New ECDSA Algorithm (EC256, EC384, EC512)
|
||||
- ECPrivateKey and ECPublicKey, two new keys for ECDSA algorithm
|
||||
- PrivateKey is renamed in RSAPrivateKey
|
||||
- PublicKey is renamed in RSAPublicKey
|
||||
- Optimization of private & public keys parsing
|
||||
- `rsa_pkcs` & `cryptography` have been removed
|
||||
|
||||
## 1.5.0
|
||||
|
||||
- Debuging `_TypeError issue on sign method` (#4)
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Jonas Roussel
|
||||
Copyright (c) 2021 Jonas Roussel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
20
README.md
20
README.md
@ -4,6 +4,7 @@
|
||||
A dart implementation of the famous javascript library `jsonwebtoken`.
|
||||
|
||||
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
|
||||
|
||||
https://jwt.io allows you to decode, verify and generate JWT.
|
||||
|
||||
## Usage
|
||||
@ -49,13 +50,16 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Supported Algorithms
|
||||
|
||||
- HS256 (HMAC / SHA256)
|
||||
- HS384 (HMAC / SHA384)
|
||||
- HS512 (HMAC / SHA512)
|
||||
|
||||
- RS256 (RSA / SHA256)
|
||||
- RS384 (RSA / SHA384)
|
||||
- RS512 (RSA / SHA512)
|
||||
JWTAlgorithm | Digital Signature or MAC Algorithm
|
||||
-------------|-----------------------------------
|
||||
HS256 | HMAC using SHA-256 hash algorithm
|
||||
HS384 | HMAC using SHA-384 hash algorithm
|
||||
HS512 | HMAC using SHA-512 hash algorithm
|
||||
RS256 | RSASSA-PKCS1-v1_5 using SHA-256 hash algorithm
|
||||
RS384 | RSASSA-PKCS1-v1_5 using SHA-384 hash algorithm
|
||||
RS512 | RSASSA-PKCS1-v1_5 using SHA-512 hash algorithm
|
||||
ES256 | ECDSA using P-256 curve and SHA-256 hash algorithm
|
||||
ES384 | ECDSA using P-384 curve and SHA-384 hash algorithm
|
||||
ES512 | ECDSA using P-521 curve and SHA-512 hash algorithm
|
||||
|
5
example/ec_private.pem
Normal file
5
example/ec_private.pem
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2
|
||||
OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r
|
||||
1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G
|
||||
-----END PRIVATE KEY-----
|
4
example/ec_public.pem
Normal file
4
example/ec_public.pem
Normal file
@ -0,0 +1,4 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9
|
||||
q9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==
|
||||
-----END PUBLIC KEY-----
|
@ -1,7 +1,23 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
|
||||
|
||||
// HMAC SHA-256 algorithm
|
||||
void main() {
|
||||
print('----- HMAC SHA-256 ------');
|
||||
hs256();
|
||||
print('-------------------------\n');
|
||||
|
||||
print('----- RSA SHA-256 -----');
|
||||
rs256();
|
||||
print('-----------------------\n');
|
||||
|
||||
print('----- ECDSA SHA-256 -----');
|
||||
es256();
|
||||
print('-------------------------');
|
||||
}
|
||||
|
||||
// HMAC SHA-256 algorithm
|
||||
void hs256() {
|
||||
String token;
|
||||
|
||||
/* Sign */ {
|
||||
@ -55,40 +71,10 @@ void rs256() {
|
||||
);
|
||||
|
||||
// Sign it
|
||||
token = jwt.sign(
|
||||
PrivateKey(
|
||||
'''
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWw
|
||||
kWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mr
|
||||
m/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEi
|
||||
NQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV
|
||||
3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2
|
||||
QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQABAoIBACiARq2wkltjtcjs
|
||||
kFvZ7w1JAORHbEufEO1Eu27zOIlqbgyAcAl7q+/1bip4Z/x1IVES84/yTaM8p0go
|
||||
amMhvgry/mS8vNi1BN2SAZEnb/7xSxbflb70bX9RHLJqKnp5GZe2jexw+wyXlwaM
|
||||
+bclUCrh9e1ltH7IvUrRrQnFJfh+is1fRon9Co9Li0GwoN0x0byrrngU8Ak3Y6D9
|
||||
D8GjQA4Elm94ST3izJv8iCOLSDBmzsPsXfcCUZfmTfZ5DbUDMbMxRnSo3nQeoKGC
|
||||
0Lj9FkWcfmLcpGlSXTO+Ww1L7EGq+PT3NtRae1FZPwjddQ1/4V905kyQFLamAA5Y
|
||||
lSpE2wkCgYEAy1OPLQcZt4NQnQzPz2SBJqQN2P5u3vXl+zNVKP8w4eBv0vWuJJF+
|
||||
hkGNnSxXQrTkvDOIUddSKOzHHgSg4nY6K02ecyT0PPm/UZvtRpWrnBjcEVtHEJNp
|
||||
bU9pLD5iZ0J9sbzPU/LxPmuAP2Bs8JmTn6aFRspFrP7W0s1Nmk2jsm0CgYEAyH0X
|
||||
+jpoqxj4efZfkUrg5GbSEhf+dZglf0tTOA5bVg8IYwtmNk/pniLG/zI7c+GlTc9B
|
||||
BwfMr59EzBq/eFMI7+LgXaVUsM/sS4Ry+yeK6SJx/otIMWtDfqxsLD8CPMCRvecC
|
||||
2Pip4uSgrl0MOebl9XKp57GoaUWRWRHqwV4Y6h8CgYAZhI4mh4qZtnhKjY4TKDjx
|
||||
QYufXSdLAi9v3FxmvchDwOgn4L+PRVdMwDNms2bsL0m5uPn104EzM6w1vzz1zwKz
|
||||
5pTpPI0OjgWN13Tq8+PKvm/4Ga2MjgOgPWQkslulO/oMcXbPwWC3hcRdr9tcQtn9
|
||||
Imf9n2spL/6EDFId+Hp/7QKBgAqlWdiXsWckdE1Fn91/NGHsc8syKvjjk1onDcw0
|
||||
NvVi5vcba9oGdElJX3e9mxqUKMrw7msJJv1MX8LWyMQC5L6YNYHDfbPF1q5L4i8j
|
||||
8mRex97UVokJQRRA452V2vCO6S5ETgpnad36de3MUxHgCOX3qL382Qx9/THVmbma
|
||||
3YfRAoGAUxL/Eu5yvMK8SAt/dJK6FedngcM3JEFNplmtLYVLWhkIlNRGDwkg3I5K
|
||||
y18Ae9n7dHVueyslrb6weq7dTkYDi3iOYRW8HRkIQh06wEdbxt0shTzAJvvCQfrB
|
||||
jg/3747WSsf/zBTcHihTRBdAv6OmdhV4/dD5YBfLAkLrd+mX7iE=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
''',
|
||||
),
|
||||
algorithm: JWTAlgorithm.RS256,
|
||||
);
|
||||
final pem = File('rsa_private.pem').readAsStringSync();
|
||||
final key = RSAPrivateKey(pem);
|
||||
|
||||
token = jwt.sign(key, algorithm: JWTAlgorithm.RS256);
|
||||
|
||||
print('Signed token: $token\n');
|
||||
}
|
||||
@ -96,22 +82,53 @@ void rs256() {
|
||||
/* Verify */ {
|
||||
try {
|
||||
// Verify a token
|
||||
final jwt = JWT.verify(
|
||||
token,
|
||||
PublicKey(
|
||||
'''
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv
|
||||
vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc
|
||||
aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy
|
||||
tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0
|
||||
e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb
|
||||
V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9
|
||||
MwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
''',
|
||||
),
|
||||
);
|
||||
final pem = File('rsa_public.pem').readAsStringSync();
|
||||
final key = RSAPublicKey(pem);
|
||||
|
||||
final jwt = JWT.verify(token, key);
|
||||
|
||||
print('Payload: ${jwt.payload}');
|
||||
} on JWTExpiredError {
|
||||
print('jwt expired');
|
||||
} on JWTError catch (ex) {
|
||||
print(ex.message); // ex: invalid signature
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ECDSA SHA-256 algorithm
|
||||
void es256() {
|
||||
String token;
|
||||
|
||||
/* Sign */ {
|
||||
// Create a json web token
|
||||
final jwt = JWT(
|
||||
{
|
||||
'id': 123,
|
||||
'server': {
|
||||
'id': '3e4fc296',
|
||||
'loc': 'euw-2',
|
||||
}
|
||||
},
|
||||
issuer: 'https://github.com/jonasroussel/jsonwebtoken',
|
||||
);
|
||||
|
||||
// Sign it
|
||||
final pem = File('ec_private.pem').readAsStringSync();
|
||||
final key = ECPrivateKey(pem);
|
||||
|
||||
token = jwt.sign(key, algorithm: JWTAlgorithm.ES256);
|
||||
|
||||
print('Signed token: $token\n');
|
||||
}
|
||||
|
||||
/* Verify */ {
|
||||
try {
|
||||
// Verify a token
|
||||
final pem = File('ec_public.pem').readAsStringSync();
|
||||
final key = ECPublicKey(pem);
|
||||
|
||||
final jwt = JWT.verify(token, key);
|
||||
|
||||
print('Payload: ${jwt.payload}');
|
||||
} on JWTExpiredError {
|
||||
|
27
example/rsa_private.pem
Normal file
27
example/rsa_private.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAq5QLAv9kYTgelglIhC17KdfUoinkwvQ4F0TZAp7qgmu19dCx
|
||||
PPC5lhPTK8aAdfJNGrnlXw94t4BCmZbWysdUbYHg8qo7sxQMlwixGiCij8/yC3JT
|
||||
fMxLgNhv7s1NMkG5JPQU1suDsyU3TLI9LcXbST5Ges480+GTZ419KnNcnBVMLmFl
|
||||
nBP6J0bn2xRs5DufZ2tp8nmzZuLKjVGA4DW3b3VCZmLSQHL9Y0pTQjwkqbhjDfiu
|
||||
woXGdNJsILuFqpxeVIcsEitbLPzrGEbjE/8afar38gkkBhF5Dv2bt5XD5MpeTvUH
|
||||
BbL49weOoUdlCh4nrNl5qz0QqplCIMuTdiCGDwIDAQABAoIBADEDz8mBcAmd4I74
|
||||
A/FOlNq1P5c75JAU+FkdX+kxwMiVQJwCjD9efYX0D+V3hYcsOv6hkHcBknjJeT0r
|
||||
LB0tqF7INZzdKMTVu2sCHqXaINZ4jyrZ9CO4sPET45YSrDTpNZzq5XEfHnZ08UFF
|
||||
wdhrp2NbGQNIG3RkApoYf2M7JX+o1ci/4t0Q74ZyzmbHHiKEr5VJvGsd/wajwF9r
|
||||
waCdbFf7Y7atf27s4ue8pKLo8vVOkDro+gOZYXy/9jOINP9E/NmL1Ggda0mXkNe6
|
||||
2DX3Riu7WDD8YqxCAWCQuuR+JdsDoAujSppgBeb4JC60Pfsk8QmjXTPqAqaBR2FS
|
||||
QHh0J3ECgYEA4pWGQIgK+hb0naXCffK+l2KO7Rgj5JBLkrarIU/blpYPeIwbRUGz
|
||||
qIPcDLdNzwi3CWCwlX3jh3SCeQgI3VCS+BHTJ0B0mZROoMi2jpFsbQUGHethPKj5
|
||||
/iSqYrHvKfcgFowlrUGrg40rgJ3RD2H3UqT03FhzeQzg9R8yKNmVWq0CgYEAwdpp
|
||||
mnDT9yEpuM6qAdhtRxwrsKorqdsQp5B5dFDIN11FW8cso6kQM08Rx/wLwx/1fOFc
|
||||
tMIVxkKcNXLNZXCEPilswHqBTM8JNvxZf8pCACgHtsVClf76zC8vRKfHqN8fli0l
|
||||
aPK66MBw8jeGrdVmMZ8ehmqCLiE1K8xU1O3P1ysCgYBSTxzgzkqmhLZBmlhnpkmj
|
||||
9pKTAETxn5VEa4UI6diMAGEIWmUBqJdZ7OR/3gqu3ayYBH7BH2UK7MEEtG177+wj
|
||||
n14EET8KFzzk3WSrr5oilEOflMTS41NFBg67IYYOyY3nguug340tkZeu9iVeoCjW
|
||||
5cNpU9xGqe4CPV3cRwYhVQKBgE+AwhrC90u9YB0ZNQQ4vOiTFyAHSo0I+RWf3Q/l
|
||||
KZf7OrzfPOGKLsi8PvCj4DcQXl/vFVRU3RVIsEQDZ1WZBcNwvdda5riXmJHigNo2
|
||||
G0dxI2jEEgDuZ/Fh0KL5pJsPlybCMTuPgTR/6FKOjz+v64+iUKST/fvpRqNF0+zX
|
||||
84utAoGARB+Oun9oJveeFL48Qbh5bxYvYfi3kEZtpqa5kh/2b0/DcaOQprfI5ERE
|
||||
EAEXPTSykGI60hmlbEMWh6GcfMwHGrrZy0eWw/oQXZclx+HDIjrl869NRLt+WoWp
|
||||
3vTAqqvIPiBL2BjgTVSLdwwaXKDJb53MutLozOmAliIhRxO5ywI=
|
||||
-----END RSA PRIVATE KEY-----
|
9
example/rsa_public.pem
Normal file
9
example/rsa_public.pem
Normal file
@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq5QLAv9kYTgelglIhC17
|
||||
KdfUoinkwvQ4F0TZAp7qgmu19dCxPPC5lhPTK8aAdfJNGrnlXw94t4BCmZbWysdU
|
||||
bYHg8qo7sxQMlwixGiCij8/yC3JTfMxLgNhv7s1NMkG5JPQU1suDsyU3TLI9LcXb
|
||||
ST5Ges480+GTZ419KnNcnBVMLmFlnBP6J0bn2xRs5DufZ2tp8nmzZuLKjVGA4DW3
|
||||
b3VCZmLSQHL9Y0pTQjwkqbhjDfiuwoXGdNJsILuFqpxeVIcsEitbLPzrGEbjE/8a
|
||||
far38gkkBhF5Dv2bt5XD5MpeTvUHBbL49weOoUdlCh4nrNl5qz0QqplCIMuTdiCG
|
||||
DwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
@ -4,3 +4,4 @@ export 'src/jwt.dart';
|
||||
export 'src/errors.dart';
|
||||
export 'src/algorithms.dart';
|
||||
export 'src/keys.dart';
|
||||
export 'src/parser.dart';
|
||||
|
@ -2,18 +2,41 @@ 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;
|
||||
import 'package:pointycastle/pointycastle.dart' as pc;
|
||||
|
||||
import 'errors.dart';
|
||||
import 'keys.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
abstract class JWTAlgorithm {
|
||||
static const HS256 = HMACAlgorithm('HS256');
|
||||
static const HS384 = HMACAlgorithm('HS384');
|
||||
static const HS512 = HMACAlgorithm('HS512');
|
||||
static const RS256 = RSAAlgorithm('RS256');
|
||||
static const RS384 = RSAAlgorithm('RS384');
|
||||
static const RS512 = RSAAlgorithm('RS512');
|
||||
/// HMAC using SHA-256 hash algorithm
|
||||
static const HS256 = _HMACAlgorithm('HS256');
|
||||
|
||||
/// HMAC using SHA-384 hash algorithm
|
||||
static const HS384 = _HMACAlgorithm('HS384');
|
||||
|
||||
/// HMAC using SHA-512 hash algorithm
|
||||
static const HS512 = _HMACAlgorithm('HS512');
|
||||
|
||||
/// RSASSA-PKCS1-v1_5 using SHA-256 hash algorithm
|
||||
static const RS256 = _RSAAlgorithm('RS256');
|
||||
|
||||
/// RSASSA-PKCS1-v1_5 using SHA-384 hash algorithm
|
||||
static const RS384 = _RSAAlgorithm('RS384');
|
||||
|
||||
/// RSASSA-PKCS1-v1_5 using SHA-512 hash algorithm
|
||||
static const RS512 = _RSAAlgorithm('RS512');
|
||||
|
||||
/// ECDSA using P-256 curve and SHA-256 hash algorithm
|
||||
static const ES256 = _ECDSAAlgorithm('ES256');
|
||||
|
||||
/// ECDSA using P-384 curve and SHA-384 hash algorithm
|
||||
static const ES384 = _ECDSAAlgorithm('ES384');
|
||||
|
||||
/// ECDSA using P-512 curve and SHA-512 hash algorithm
|
||||
static const ES512 = _ECDSAAlgorithm('ES512');
|
||||
|
||||
/// Return the `JWTAlgorithm` from his string name
|
||||
static JWTAlgorithm fromName(String name) {
|
||||
switch (name) {
|
||||
case 'HS256':
|
||||
@ -28,6 +51,12 @@ abstract class JWTAlgorithm {
|
||||
return JWTAlgorithm.RS384;
|
||||
case 'RS512':
|
||||
return JWTAlgorithm.RS512;
|
||||
case 'ES256':
|
||||
return JWTAlgorithm.ES256;
|
||||
case 'ES384':
|
||||
return JWTAlgorithm.ES384;
|
||||
case 'ES512':
|
||||
return JWTAlgorithm.ES512;
|
||||
default:
|
||||
throw JWTInvalidError('unknown algorithm');
|
||||
}
|
||||
@ -35,30 +64,40 @@ abstract class JWTAlgorithm {
|
||||
|
||||
const JWTAlgorithm();
|
||||
|
||||
/// `JWTAlgorithm` name
|
||||
String get name;
|
||||
List<int> sign(Key key, List<int> body);
|
||||
bool verify(Key key, List<int> body, List<int> signature);
|
||||
|
||||
/// Create a signature of the `body` with `key`
|
||||
///
|
||||
/// return the signature as bytes
|
||||
Uint8List sign(Key key, Uint8List body);
|
||||
|
||||
/// Verify the `signature` of `body` with `key`
|
||||
///
|
||||
/// return `true` if the signature is correct `false` otherwise
|
||||
bool verify(Key key, Uint8List body, Uint8List signature);
|
||||
}
|
||||
|
||||
class HMACAlgorithm extends JWTAlgorithm {
|
||||
class _HMACAlgorithm extends JWTAlgorithm {
|
||||
final String _name;
|
||||
|
||||
const HMACAlgorithm(this._name);
|
||||
const _HMACAlgorithm(this._name);
|
||||
|
||||
@override
|
||||
String get name => _name;
|
||||
|
||||
@override
|
||||
List<int> sign(Key key, List<int> body) {
|
||||
Uint8List sign(Key key, Uint8List body) {
|
||||
assert(key is SecretKey, 'key must be a SecretKey');
|
||||
final secretKey = key as SecretKey;
|
||||
|
||||
final hmac = Hmac(_getHash(name), utf8.encode(secretKey.key));
|
||||
|
||||
return hmac.convert(body).bytes;
|
||||
}
|
||||
|
||||
@override
|
||||
bool verify(Key key, List<int> body, List<int> signature) {
|
||||
bool verify(Key key, Uint8List body, Uint8List signature) {
|
||||
assert(key is SecretKey, 'key must be a SecretKey');
|
||||
|
||||
final actual = sign(key, body);
|
||||
@ -86,78 +125,44 @@ class HMACAlgorithm extends JWTAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
class RSAAlgorithm extends JWTAlgorithm {
|
||||
class _RSAAlgorithm extends JWTAlgorithm {
|
||||
final String _name;
|
||||
|
||||
const RSAAlgorithm(this._name);
|
||||
const _RSAAlgorithm(this._name);
|
||||
|
||||
@override
|
||||
String get name => _name;
|
||||
|
||||
@override
|
||||
List<int> sign(Key key, List<int> body) {
|
||||
assert(key is PrivateKey, 'key must be a PrivateKey');
|
||||
final privateKey = key as PrivateKey;
|
||||
Uint8List sign(Key key, Uint8List body) {
|
||||
assert(key is RSAPrivateKey, 'key must be a RSAPrivateKey');
|
||||
final privateKey = key as RSAPrivateKey;
|
||||
|
||||
final parser = RSAPKCSParser();
|
||||
RSAKeyPair pair;
|
||||
|
||||
pair = parser.parsePEM(privateKey.key, password: privateKey.passphrase);
|
||||
if (pair.private == null) {
|
||||
throw JWTInvalidError('invalid private RSA key');
|
||||
}
|
||||
|
||||
final signer = Signer('${_getHash(name)}/RSA');
|
||||
final params = ParametersWithRandom(
|
||||
PrivateKeyParameter<RSAPrivateKey>(
|
||||
RSAPrivateKey(
|
||||
pair.private.modulus,
|
||||
pair.private.privateExponent,
|
||||
pair.private.prime1,
|
||||
pair.private.prime2,
|
||||
),
|
||||
),
|
||||
SecureRandom('AES/CTR/PRNG'),
|
||||
);
|
||||
final signer = pc.Signer('${_getHash(name)}/RSA');
|
||||
final params = pc.PrivateKeyParameter<pc.RSAPrivateKey>(privateKey.key);
|
||||
|
||||
signer.init(true, params);
|
||||
|
||||
RSASignature signature = signer.generateSignature(Uint8List.fromList(body));
|
||||
pc.RSASignature signature = signer.generateSignature(
|
||||
Uint8List.fromList(body),
|
||||
);
|
||||
|
||||
return signature.bytes.toList(growable: false);
|
||||
return signature.bytes;
|
||||
}
|
||||
|
||||
@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;
|
||||
bool verify(Key key, Uint8List body, Uint8List signature) {
|
||||
assert(key is RSAPublicKey, 'key must be a RSAPublicKey');
|
||||
final publicKey = key as RSAPublicKey;
|
||||
|
||||
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('${_getHash(name)}/RSA');
|
||||
final params = ParametersWithRandom(
|
||||
PublicKeyParameter<RSAPublicKey>(
|
||||
RSAPublicKey(
|
||||
pair.public.modulus,
|
||||
BigInt.from(pair.public.publicExponent),
|
||||
),
|
||||
),
|
||||
SecureRandom('AES/CTR/PRNG'),
|
||||
);
|
||||
final signer = pc.Signer('${_getHash(name)}/RSA');
|
||||
final params = pc.PublicKeyParameter<pc.RSAPublicKey>(publicKey.key);
|
||||
|
||||
signer.init(false, params);
|
||||
|
||||
final msg = Uint8List.fromList(body);
|
||||
final sign = RSASignature(Uint8List.fromList(signature));
|
||||
final sign = pc.RSASignature(Uint8List.fromList(signature));
|
||||
|
||||
return signer.verifySignature(msg, sign);
|
||||
} catch (ex) {
|
||||
@ -178,3 +183,66 @@ class RSAAlgorithm extends JWTAlgorithm {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _ECDSAAlgorithm extends JWTAlgorithm {
|
||||
final String _name;
|
||||
|
||||
const _ECDSAAlgorithm(this._name);
|
||||
|
||||
@override
|
||||
String get name => _name;
|
||||
|
||||
@override
|
||||
Uint8List sign(Key key, Uint8List body) {
|
||||
assert(key is ECPrivateKey, 'key must be a ECPublicKey');
|
||||
final privateKey = key as ECPrivateKey;
|
||||
|
||||
final signer = pc.Signer('${_getHash(name)}/DET-ECDSA');
|
||||
final params = pc.PrivateKeyParameter<pc.ECPrivateKey>(privateKey.key);
|
||||
|
||||
signer.init(true, params);
|
||||
|
||||
pc.ECSignature signature = signer.generateSignature(
|
||||
Uint8List.fromList(body),
|
||||
);
|
||||
|
||||
final len = privateKey.size;
|
||||
final bytes = Uint8List(len * 2);
|
||||
bytes.setRange(0, len, bigIntToBytes(signature.r).toList().reversed);
|
||||
bytes.setRange(len, len * 2, bigIntToBytes(signature.s).toList().reversed);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@override
|
||||
bool verify(Key key, Uint8List body, Uint8List signature) {
|
||||
assert(key is ECPublicKey, 'key must be a ECPublicKey');
|
||||
final publicKey = key as ECPublicKey;
|
||||
|
||||
final signer = pc.Signer('${_getHash(name)}/DET-ECDSA');
|
||||
final params = pc.PublicKeyParameter<pc.ECPublicKey>(publicKey.key);
|
||||
|
||||
signer.init(false, params);
|
||||
|
||||
final len = signature.length ~/ 2;
|
||||
final sign = pc.ECSignature(
|
||||
bigIntFromBytes(signature.sublist(0, len)),
|
||||
bigIntFromBytes(signature.sublist(len)),
|
||||
);
|
||||
|
||||
return signer.verifySignature(body, sign);
|
||||
}
|
||||
|
||||
String _getHash(String name) {
|
||||
switch (name) {
|
||||
case 'ES256':
|
||||
return 'SHA-256';
|
||||
case 'ES384':
|
||||
return 'SHA-384';
|
||||
case 'ES512':
|
||||
return 'SHA-512';
|
||||
default:
|
||||
throw ArgumentError.value(name, 'name', 'unknown hash name');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,25 @@
|
||||
/// JWTError objects thrown in the case of a jwt sign or verify failure.
|
||||
class JWTError extends Error {
|
||||
JWTError(this.message);
|
||||
|
||||
/// Describes the error thrown
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => 'JWTError: $message';
|
||||
}
|
||||
|
||||
/// An error thrown when token is invalid
|
||||
/// An error thrown when jwt is invalid
|
||||
class JWTInvalidError extends JWTError {
|
||||
JWTInvalidError(String message) : super(message);
|
||||
}
|
||||
|
||||
/// An error thrown when token is expired
|
||||
/// An error thrown when jwt is expired
|
||||
class JWTExpiredError extends JWTError {
|
||||
JWTExpiredError() : super('jwt expired');
|
||||
}
|
||||
|
||||
/// An error thrown when token is not active
|
||||
/// An error thrown when jwt is not active
|
||||
class JWTNotActiveError extends JWTError {
|
||||
JWTNotActiveError() : super('jwt not active');
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
|
||||
|
||||
import './utils.dart';
|
||||
import 'algorithms.dart';
|
||||
import 'errors.dart';
|
||||
import 'keys.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
class JWT {
|
||||
/// Verify a token.
|
||||
///
|
||||
/// `key` must be
|
||||
/// - SecretKey with HS256 algorithm
|
||||
/// - PublicKey with RS256 algorithm
|
||||
/// - SecretKey with HMAC algorithm
|
||||
/// - RSAPublicKey with RSA algorithm
|
||||
/// - ECPublicKey with ECDSA algorithm
|
||||
static JWT verify(
|
||||
String token,
|
||||
Key key, {
|
||||
@ -52,8 +54,9 @@ class JWT {
|
||||
if (payload is Map) {
|
||||
// exp
|
||||
if (checkExpiresIn && payload.containsKey('exp')) {
|
||||
final exp =
|
||||
DateTime.fromMillisecondsSinceEpoch(payload['exp'] * 1000);
|
||||
final exp = DateTime.fromMillisecondsSinceEpoch(
|
||||
payload['exp'] * 1000,
|
||||
);
|
||||
if (exp.isBefore(DateTime.now())) {
|
||||
throw JWTExpiredError();
|
||||
}
|
||||
@ -61,8 +64,9 @@ class JWT {
|
||||
|
||||
// nbf
|
||||
if (checkNotBefore && payload.containsKey('nbf')) {
|
||||
final nbf =
|
||||
DateTime.fromMillisecondsSinceEpoch(payload['nbf'] * 1000);
|
||||
final nbf = DateTime.fromMillisecondsSinceEpoch(
|
||||
payload['nbf'] * 1000,
|
||||
);
|
||||
if (nbf.isAfter(DateTime.now())) {
|
||||
throw JWTNotActiveError();
|
||||
}
|
||||
@ -73,8 +77,9 @@ class JWT {
|
||||
if (!payload.containsKey('iat')) {
|
||||
throw JWTInvalidError('invalid issue at');
|
||||
}
|
||||
final iat =
|
||||
DateTime.fromMillisecondsSinceEpoch(payload['iat'] * 1000);
|
||||
final iat = DateTime.fromMillisecondsSinceEpoch(
|
||||
payload['iat'] * 1000,
|
||||
);
|
||||
if (!iat.isAtSameMomentAs(DateTime.now())) {
|
||||
throw JWTInvalidError('invalid issue at');
|
||||
}
|
||||
@ -154,8 +159,9 @@ class JWT {
|
||||
/// Sign and generate a new token.
|
||||
///
|
||||
/// `key` must be
|
||||
/// - SecretKey with HS256 algorithm
|
||||
/// - PrivateKey with RS256 algorithm
|
||||
/// - SecretKey with HMAC algorithm
|
||||
/// - RSAPrivateKey with RSA algorithm
|
||||
/// - ECPrivateKey with ECDSA algorithm
|
||||
String sign(
|
||||
Key key, {
|
||||
JWTAlgorithm algorithm = JWTAlgorithm.HS256,
|
||||
@ -194,7 +200,8 @@ class JWT {
|
||||
);
|
||||
} catch (ex) {
|
||||
throw JWTError(
|
||||
'invalid payload json format (Map keys must be String type)');
|
||||
'invalid payload json format (Map keys must be String type)',
|
||||
);
|
||||
}
|
||||
|
||||
final body = '${b64Header}.${b64Payload}';
|
||||
|
@ -1,24 +1,50 @@
|
||||
import 'package:pointycastle/pointycastle.dart' as pc;
|
||||
|
||||
import 'parser.dart';
|
||||
|
||||
abstract class Key {}
|
||||
|
||||
/// For HS256 algorithm
|
||||
/// For HMAC algorithms
|
||||
class SecretKey extends Key {
|
||||
String key;
|
||||
|
||||
SecretKey(this.key);
|
||||
}
|
||||
|
||||
/// For RS256 algorithm, in sign method
|
||||
class PrivateKey extends Key {
|
||||
String key;
|
||||
String passphrase;
|
||||
/// For RSA algorithm, in sign method
|
||||
class RSAPrivateKey extends Key {
|
||||
pc.RSAPrivateKey key;
|
||||
|
||||
PrivateKey(this.key, [this.passphrase = '']);
|
||||
RSAPrivateKey(String pem) {
|
||||
key = parseRSAPrivateKeyPEM(pem);
|
||||
}
|
||||
}
|
||||
|
||||
/// For RS256 algorithm, in verify method
|
||||
class PublicKey extends Key {
|
||||
String key;
|
||||
String passphrase;
|
||||
/// For RSA algorithm, in verify method
|
||||
class RSAPublicKey extends Key {
|
||||
pc.RSAPublicKey key;
|
||||
|
||||
PublicKey(this.key, [this.passphrase = '']);
|
||||
RSAPublicKey(String pem) {
|
||||
key = parseRSAPublicKeyPEM(pem);
|
||||
}
|
||||
}
|
||||
|
||||
/// For ECDSA algorithm, in sign method
|
||||
class ECPrivateKey extends Key {
|
||||
pc.ECPrivateKey key;
|
||||
int size;
|
||||
|
||||
ECPrivateKey(String pem) {
|
||||
key = parseECPrivateKeyPEM(pem);
|
||||
size = (key.parameters.curve.fieldSize / 8).round();
|
||||
}
|
||||
}
|
||||
|
||||
/// For ECDSA algorithm, in verify method
|
||||
class ECPublicKey extends Key {
|
||||
pc.ECPublicKey key;
|
||||
|
||||
ECPublicKey(String pem) {
|
||||
key = parseECPublicKeyPEM(pem);
|
||||
}
|
||||
}
|
||||
|
275
lib/src/parser.dart
Normal file
275
lib/src/parser.dart
Normal file
@ -0,0 +1,275 @@
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:pointycastle/pointycastle.dart' hide ECPoint;
|
||||
import 'package:pointycastle/ecc/ecc_fp.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
// RSA Private Key -> PKCS#1 format
|
||||
const String _pkcs1RSAPrivateHeader = '-----BEGIN RSA PRIVATE KEY-----';
|
||||
const String _pkcs1RSAPrivateFooter = '-----END RSA PRIVATE KEY-----';
|
||||
|
||||
// RSA Private Key -> PKCS#8 format
|
||||
const String _pkcs8RSAPrivateHeader = '-----BEGIN PRIVATE KEY-----';
|
||||
const String _pkcs8RSAPrivateFooter = '-----END PRIVATE KEY-----';
|
||||
|
||||
// RSA Public Key -> PKCS#1 format
|
||||
const String _pkcs1RSAPublicHeader = '-----BEGIN RSA PUBLIC KEY-----';
|
||||
const String _pkcs1RSAPublicFooter = '-----END RSA PUBLIC KEY-----';
|
||||
|
||||
// RSA Public Key -> PKCS#1 format
|
||||
const String _pkcs8RSAPublicHeader = '-----BEGIN PUBLIC KEY-----';
|
||||
const String _pkcs8RSAPublicFooter = '-----END PUBLIC KEY-----';
|
||||
|
||||
// ECDSA Private Key -> SEC 1 format
|
||||
const String _sec1ECPrivateHeader = '-----BEGIN EC PRIVATE KEY-----';
|
||||
const String _sec1ECPrivateFooter = '-----END EC PRIVATE KEY-----';
|
||||
|
||||
// ECDSA Private Key -> PKCS#8 format
|
||||
const String _pkcs8ECPrivateHeader = '-----BEGIN PRIVATE KEY-----';
|
||||
const String _pkcs8ECPrivateFooter = '-----END PRIVATE KEY-----';
|
||||
|
||||
// ECDSA Public Key -> PKCS#8 format
|
||||
const String _pkcs8ECPublicHeader = '-----BEGIN PUBLIC KEY-----';
|
||||
const String _pkcs8ECPublicFooter = '-----END PUBLIC KEY-----';
|
||||
|
||||
// ECDSA Curves OID to DomainName
|
||||
const Map<String, String> _ecCurves = const {
|
||||
'1.2.840.10045.3.1.7': 'prime256v1', // P-256
|
||||
'1.3.132.0.10': 'secp256k1', // P-256
|
||||
'1.3.132.0.34': 'secp384r1', // P-384
|
||||
'1.3.132.0.35': 'secp521r1', // P-512
|
||||
};
|
||||
|
||||
/// RSA Private Key -> PKCS#1 parser
|
||||
RSAPrivateKey _pkcs1RSAPrivateKey(Uint8List bytes) {
|
||||
final parser = ASN1Parser(bytes);
|
||||
final seq = parser.nextObject() as ASN1Sequence;
|
||||
final values = seq.elements.cast<ASN1Integer>();
|
||||
|
||||
return RSAPrivateKey(
|
||||
values[1].integer, // modulus
|
||||
values[3].integer, // privateExponent
|
||||
values[4].integer, // prime1
|
||||
values[5].integer, // prime2
|
||||
);
|
||||
}
|
||||
|
||||
/// RSA Private Key -> PKCS#8 parser
|
||||
RSAPrivateKey _pkcs8RSAPrivateKey(Uint8List bytes) {
|
||||
final parser = ASN1Parser(bytes);
|
||||
final seq = parser.nextObject() as ASN1Sequence;
|
||||
|
||||
final keySeq = seq.elements[2] as ASN1OctetString;
|
||||
final keyParser = ASN1Parser(keySeq.octets);
|
||||
|
||||
final valuesSeq = keyParser.nextObject() as ASN1Sequence;
|
||||
final values = valuesSeq.elements.cast<ASN1Integer>();
|
||||
|
||||
return RSAPrivateKey(
|
||||
values[1].integer, // modulus
|
||||
values[3].integer, // privateExponent
|
||||
values[4].integer, // prime1
|
||||
values[5].integer, // prime2
|
||||
);
|
||||
}
|
||||
|
||||
/// RSA Public Key -> PKCS#1 parser
|
||||
RSAPublicKey _pkcs1RSAPublicKey(Uint8List bytes) {
|
||||
final parser = ASN1Parser(bytes);
|
||||
final seq = parser.nextObject() as ASN1Sequence;
|
||||
final values = seq.elements.cast<ASN1Integer>();
|
||||
|
||||
return RSAPublicKey(
|
||||
values[0].integer, // modulus
|
||||
values[1].integer, // publicExponent
|
||||
);
|
||||
}
|
||||
|
||||
/// RSA Public Key -> PKCS#8 parser
|
||||
RSAPublicKey _pkcs8RSAPublicKey(Uint8List bytes) {
|
||||
final parser = ASN1Parser(bytes);
|
||||
final seq = parser.nextObject() as ASN1Sequence;
|
||||
|
||||
final keySeq = seq.elements[1] as ASN1BitString;
|
||||
final keyParser = ASN1Parser(keySeq.stringValues);
|
||||
|
||||
final valuesSeq = keyParser.nextObject() as ASN1Sequence;
|
||||
final values = valuesSeq.elements.cast<ASN1Integer>();
|
||||
|
||||
return RSAPublicKey(
|
||||
values[0].integer, // modulus
|
||||
values[1].integer, // publicExponent
|
||||
);
|
||||
}
|
||||
|
||||
/// ECDSA Private Key -> SEC 1 parser
|
||||
ECPrivateKey _sec1ECPrivateKey(Uint8List bytes) {
|
||||
final parser = ASN1Parser(bytes);
|
||||
final seq = parser.nextObject() as ASN1Sequence;
|
||||
|
||||
final privateKey = seq.elements[1] as ASN1OctetString;
|
||||
|
||||
final params = seq.elements[2];
|
||||
final paramsParser = ASN1Parser(params.valueBytes);
|
||||
final oid = (paramsParser.nextObject() as ASN1ObjectIdentifier)
|
||||
.objectIdentifierAsString;
|
||||
final curve = _ecCurves[oid];
|
||||
|
||||
return ECPrivateKey(
|
||||
decodeBigInt(privateKey.valueBytes),
|
||||
ECDomainParameters(curve),
|
||||
);
|
||||
}
|
||||
|
||||
/// ECDSA Private Key -> PKCS#8 parser
|
||||
ECPrivateKey _pkcs8ECPrivateKey(Uint8List bytes) {
|
||||
final parser = ASN1Parser(bytes);
|
||||
final seq = parser.nextObject() as ASN1Sequence;
|
||||
|
||||
final oidSeq = seq.elements[1] as ASN1Sequence;
|
||||
final oid =
|
||||
(oidSeq.elements[1] as ASN1ObjectIdentifier).objectIdentifierAsString;
|
||||
final curve = _ecCurves[oid];
|
||||
|
||||
final privateKeyParser = ASN1Parser(seq.elements[2].valueBytes);
|
||||
final privateKeySeq = privateKeyParser.nextObject() as ASN1Sequence;
|
||||
final privateKey = (privateKeySeq.elements[1] as ASN1OctetString);
|
||||
|
||||
return ECPrivateKey(
|
||||
decodeBigInt(privateKey.valueBytes),
|
||||
ECDomainParameters(curve),
|
||||
);
|
||||
}
|
||||
|
||||
/// ECDSA Public Key -> PKCS#8 parser
|
||||
ECPublicKey _pkcs8ECPublicKey(Uint8List bytes) {
|
||||
final parser = ASN1Parser(bytes);
|
||||
final seq = parser.nextObject() as ASN1Sequence;
|
||||
|
||||
final oidSeq = seq.elements[0] as ASN1Sequence;
|
||||
final oid =
|
||||
(oidSeq.elements[1] as ASN1ObjectIdentifier).objectIdentifierAsString;
|
||||
final curve = _ecCurves[oid];
|
||||
|
||||
var publicKeyBytes = seq.elements[1].valueBytes;
|
||||
if (publicKeyBytes[0] == 0) {
|
||||
publicKeyBytes = publicKeyBytes.sublist(1);
|
||||
}
|
||||
|
||||
final compressed = publicKeyBytes[0] != 4;
|
||||
final x = publicKeyBytes.sublist(1, (publicKeyBytes.length / 2).round());
|
||||
final y = publicKeyBytes.sublist(1 + x.length, publicKeyBytes.length);
|
||||
final bigX = decodeBigIntWithSign(1, x);
|
||||
final bigY = decodeBigIntWithSign(1, y);
|
||||
final params = ECDomainParameters(curve);
|
||||
|
||||
return ECPublicKey(
|
||||
ECPoint(
|
||||
params.curve,
|
||||
params.curve.fromBigInteger(bigX),
|
||||
params.curve.fromBigInteger(bigY),
|
||||
compressed,
|
||||
),
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse RSA private key from pem string
|
||||
RSAPrivateKey parseRSAPrivateKeyPEM(String pem) {
|
||||
if (pem.contains(_pkcs1RSAPrivateHeader) &&
|
||||
pem.contains(_pkcs1RSAPrivateFooter)) {
|
||||
final data = pem
|
||||
.substring(
|
||||
pem.indexOf(_pkcs1RSAPrivateHeader) + _pkcs1RSAPrivateHeader.length,
|
||||
pem.indexOf(_pkcs1RSAPrivateFooter),
|
||||
)
|
||||
.replaceAll(RegExp(r'[\n\r ]'), '');
|
||||
|
||||
return _pkcs1RSAPrivateKey(base64.decode(data));
|
||||
} else if (pem.contains(_pkcs8RSAPrivateHeader) &&
|
||||
pem.contains(_pkcs8RSAPrivateFooter)) {
|
||||
final data = pem
|
||||
.substring(
|
||||
pem.indexOf(_pkcs8RSAPrivateHeader) + _pkcs8RSAPrivateHeader.length,
|
||||
pem.indexOf(_pkcs8RSAPrivateFooter),
|
||||
)
|
||||
.replaceAll(RegExp(r'[\n\r ]'), '');
|
||||
|
||||
return _pkcs8RSAPrivateKey(base64.decode(data));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse RSA public key from pem string
|
||||
RSAPublicKey parseRSAPublicKeyPEM(String pem) {
|
||||
if (pem.contains(_pkcs1RSAPublicHeader) &&
|
||||
pem.contains(_pkcs1RSAPublicFooter)) {
|
||||
final data = pem
|
||||
.substring(
|
||||
pem.indexOf(_pkcs1RSAPublicHeader) + _pkcs1RSAPublicHeader.length,
|
||||
pem.indexOf(_pkcs1RSAPublicFooter),
|
||||
)
|
||||
.replaceAll(RegExp(r'[\n\r ]'), '');
|
||||
|
||||
return _pkcs1RSAPublicKey(base64.decode(data));
|
||||
} else if (pem.contains(_pkcs8RSAPublicHeader) &&
|
||||
pem.contains(_pkcs8RSAPublicFooter)) {
|
||||
final data = pem
|
||||
.substring(
|
||||
pem.indexOf(_pkcs8RSAPublicHeader) + _pkcs8RSAPublicHeader.length,
|
||||
pem.indexOf(_pkcs8RSAPublicFooter),
|
||||
)
|
||||
.replaceAll(RegExp(r'[\n\r ]'), '');
|
||||
|
||||
return _pkcs8RSAPublicKey(base64.decode(data));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse ECDSA private key from pem string
|
||||
ECPrivateKey parseECPrivateKeyPEM(String pem) {
|
||||
if (pem.contains(_sec1ECPrivateHeader) &&
|
||||
pem.contains(_sec1ECPrivateFooter)) {
|
||||
final data = pem
|
||||
.substring(
|
||||
pem.indexOf(_sec1ECPrivateHeader) + _sec1ECPrivateHeader.length,
|
||||
pem.indexOf(_sec1ECPrivateFooter),
|
||||
)
|
||||
.replaceAll(RegExp(r'[\n\r ]'), '');
|
||||
|
||||
return _sec1ECPrivateKey(base64.decode(data));
|
||||
} else if (pem.contains(_pkcs8ECPrivateHeader) &&
|
||||
pem.contains(_pkcs8ECPrivateFooter)) {
|
||||
final data = pem
|
||||
.substring(
|
||||
pem.indexOf(_pkcs8ECPrivateHeader) + _pkcs8ECPrivateHeader.length,
|
||||
pem.indexOf(_pkcs8ECPrivateFooter),
|
||||
)
|
||||
.replaceAll(RegExp(r'[\n\r ]'), '');
|
||||
|
||||
return _pkcs8ECPrivateKey(base64.decode(data));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse ECDSA public key from pem string
|
||||
ECPublicKey parseECPublicKeyPEM(String pem) {
|
||||
if (pem.contains(_pkcs8ECPublicHeader) &&
|
||||
pem.contains(_pkcs8ECPublicFooter)) {
|
||||
final data = pem
|
||||
.substring(
|
||||
pem.indexOf(_pkcs8ECPublicHeader) + _pkcs8ECPublicHeader.length,
|
||||
pem.indexOf(_pkcs8ECPublicFooter),
|
||||
)
|
||||
.replaceAll(RegExp(r'[\n\r ]'), '');
|
||||
|
||||
return _pkcs8ECPublicKey(base64.decode(data));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
final jsonBase64 = json.fuse(utf8.fuse(base64Url));
|
||||
|
||||
String base64Unpadded(String value) {
|
||||
@ -24,3 +26,66 @@ String base64Padded(String value) {
|
||||
int secondsSinceEpoch(DateTime time) {
|
||||
return time.millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
|
||||
BigInt decodeBigInt(List<int> bytes) {
|
||||
var negative = bytes.isNotEmpty && bytes[0] & 0x80 == 0x80;
|
||||
|
||||
BigInt result;
|
||||
|
||||
if (bytes.length == 1) {
|
||||
result = BigInt.from(bytes[0]);
|
||||
} else {
|
||||
result = BigInt.zero;
|
||||
|
||||
for (var i = 0; i < bytes.length; i++) {
|
||||
var item = bytes[bytes.length - i - 1];
|
||||
result |= (BigInt.from(item) << (8 * i));
|
||||
}
|
||||
}
|
||||
|
||||
if (result == BigInt.zero) return BigInt.zero;
|
||||
|
||||
return negative ? result.toSigned(result.bitLength) : result;
|
||||
}
|
||||
|
||||
BigInt decodeBigIntWithSign(int sign, List<int> bytes) {
|
||||
if (sign == 0) return BigInt.zero;
|
||||
|
||||
BigInt result;
|
||||
|
||||
if (bytes.length == 1) {
|
||||
result = BigInt.from(bytes[0]);
|
||||
} else {
|
||||
result = BigInt.zero;
|
||||
|
||||
for (var i = 0; i < bytes.length; i++) {
|
||||
var item = bytes[bytes.length - i - 1];
|
||||
result |= (BigInt.from(item) << (8 * i));
|
||||
}
|
||||
}
|
||||
|
||||
if (result == BigInt.zero) return BigInt.zero;
|
||||
|
||||
return sign < 0
|
||||
? result.toSigned(result.bitLength)
|
||||
: result.toUnsigned(result.bitLength);
|
||||
}
|
||||
|
||||
Uint8List bigIntToBytes(BigInt v) {
|
||||
final _b256 = BigInt.from(256);
|
||||
|
||||
List<int> bytes = List();
|
||||
|
||||
while (v.sign != 0) {
|
||||
bytes.add((v % _b256).toInt());
|
||||
v = v ~/ _b256;
|
||||
}
|
||||
|
||||
return Uint8List.fromList(bytes);
|
||||
}
|
||||
|
||||
BigInt bigIntFromBytes(Uint8List bytes) {
|
||||
final _b256 = BigInt.from(256);
|
||||
|
||||
return bytes.fold(BigInt.zero, (a, b) => a * _b256 + BigInt.from(b));
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: dart_jsonwebtoken
|
||||
description: A dart implementation of the famous javascript library 'jsonwebtoken' (JWT).
|
||||
version: 1.5.0
|
||||
version: 1.6.0
|
||||
repository: https://github.com/jonasroussel/jsonwebtoken
|
||||
homepage: https://github.com/jonasroussel/jsonwebtoken#readme
|
||||
|
||||
@ -9,9 +9,7 @@ environment:
|
||||
|
||||
dependencies:
|
||||
crypto: ^2.1.5
|
||||
rsa_pkcs: ^1.1.4
|
||||
pointycastle: ^2.0.0
|
||||
cryptography: ^1.4.1
|
||||
pointycastle: ^2.0.1
|
||||
|
||||
dev_dependencies:
|
||||
pedantic: ^1.9.2
|
||||
|
Reference in New Issue
Block a user