From 8b162293d5f042c6d0fd862b528357597e366966 Mon Sep 17 00:00:00 2001 From: Jonas Roussel Date: Sat, 26 Aug 2023 18:56:21 +0200 Subject: [PATCH] v2.11.0 --- CHANGELOG.md | 6 + example/example.dart | 47 +++++ example/rsa_cert_private.pem | 28 +++ example/rsa_cert_public.pem | 9 + example/rsa_certificate.pem | 20 ++ lib/src/algorithms.dart | 2 +- lib/src/{utils.dart => helpers.dart} | 20 +- lib/src/jwt.dart | 2 +- lib/src/key_parser.dart | 299 +++++++++++++++++++++++++++ lib/src/keys.dart | 61 +++--- pubspec.yaml | 3 +- 11 files changed, 441 insertions(+), 56 deletions(-) create mode 100644 example/rsa_cert_private.pem create mode 100644 example/rsa_cert_public.pem create mode 100644 example/rsa_certificate.pem rename lib/src/{utils.dart => helpers.dart} (84%) create mode 100644 lib/src/key_parser.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index fc9e461..c917306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.11.0 + +- Removing `basic_utils` package that was incompatible with flutter web +- Moving utils to `helpers.dart` and key parsing functions into `key_parser.dart` +- Adding some new examples in `example/example.dart` + ## 2.10.0 - New ECDSA algorithm (ES256K) diff --git a/example/example.dart b/example/example.dart index 88c91dd..2809eea 100644 --- a/example/example.dart +++ b/example/example.dart @@ -22,6 +22,10 @@ void main() { print('----- RSA-PSS SHA-256 -----'); ps256(); print('---------------------------\n'); + + print('----- RSA Certificate -----'); + rsaCert(); + print('---------------------------\n'); } // HMAC SHA-256 algorithm @@ -232,3 +236,46 @@ void ps256() { } } } + +// RSA Certificate +void rsaCert() { + String token; + + /* Sign */ { + // Create a json web token + final jwt = JWT( + { + 'id': 123, + 'server': { + 'id': '3e4fc296', + 'loc': 'euw-2', + } + }, + issuer: 'https://github.com/jonasroussel/dart_jsonwebtoken', + ); + + // Sign it + final pem = File('./example/rsa_cert_private.pem').readAsStringSync(); + final key = RSAPrivateKey(pem); + + token = jwt.sign(key, algorithm: JWTAlgorithm.RS256); + + print('Signed token: $token\n'); + } + + /* Verify */ { + try { + // Verify a token + final pem = File('./example/rsa_certificate.pem').readAsStringSync(); + final key = RSAPublicKey.cert(pem); + + final jwt = JWT.verify(token, key); + + print('Payload: ${jwt.payload}'); + } on JWTExpiredException { + print('jwt expired'); + } on JWTException catch (ex) { + print(ex.message); // ex: invalid signature + } + } +} diff --git a/example/rsa_cert_private.pem b/example/rsa_cert_private.pem new file mode 100644 index 0000000..ed082fe --- /dev/null +++ b/example/rsa_cert_private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHr1vavB+pZ0e/ +0vp7fzElDST0TffrAGvLvo6QUmn8AdzNGEiytW2GeYGBMEGni/I89/S6e0SWsVqa +XJjqDlXqefZUzPzbyfd9URwah0vcHFKk+bM7ZsvOxvlRhKYwx3waNzOlcfetlB5h +pFCiHotoUJL9sbVjoByMbs3BQP87gDSRAfkgdNhyt73Qe4mxlXBo7Ay+3dRLJZmV +IegJDQ2Ktxlsg25nYn84fpOQHlzoU4BMSKRZvVflKUblJllpwXETDvD52FlvITeo +2qdeTaerBUlOWVAYD31hJzfPUIynt4LZ7k6of73lkBH93ZOcVfYeyCGtt4fGMNJi +JxxjHkVnAgMBAAECggEAEPikc+i9QCaZAIKr60caJC4D0Ae4ZG66uqxNbZKzk0mN +PJktxJKK5nz5NsOlMNpCElB4qkjDwZL9QlVQYcQqJS2MdBSgAQYfVLVKEC8jkWeO +1pqqUwgflklu8zC09zxdHdVHPG59QHFwS5gtijnSW2FNvOYXpon7IyxfrGcbyjCq +vsEo4DkrTs2ZALkONSfYuRVMtMyWOZcOET8MKFYy6LtFzQaEB9TUPXwauF5oMxab ++S1iJLb4yPBos84Y1ooGQGl72neTueNF35qNHndkybSH0zcFFC48kdkc/PjkTfSt +ubgHDNmMEZDWgNdAK7igoLcwyS0lZiQgfVDfylbmcQKBgQDzAat/eZtBrdL4Gajk +oz2sFJQsftwX49T7xl3cKHBNKzc1J9SZHIqix8/Tajqtz5nHf8bwLkn9Z830BEjc +OM80hYh2vM1J9j1z7mo304+w3Y1tc+29rP+R65pJprMNiBEaTtPihtWBluL68hzi +f2Gl3pjJUA96KvCWL9LUiDDsswKBgQDSXLGV/eWm8tAB0NVzl8Mi35pAq63D107L +M+tqfg5+DiQhSkaL1N554uyyJ33+Q4YDctJB/9QrVppl5WysIjMBJtyYZAs8Qylz +n/JJcJzoIvxuNAEdKevxtLb934slRyGBWAGpCOkoxNhKKzN0ZovgC51OPNgCbMK7 +5oYND9eGfQKBgQDSijbZKjY+0GiBtlGjTl+nkOjUKFFujWHBhhtaHNs3sOKTNmA3 +DAh9glrolBgk8UIOHAHzpFdMqzxAV9n8m2fC3JLgNTI0C5kwsXbryWusVDgthYyM +kq+W8KbreveVGLegsH1ZvXKMZXtg6pXmE3E58cM0YB4Yvc1WgjbLFvg0iwKBgQCY +2TdD1/b9BmLzXs0pr3TfKv+Gy/d3XENpTLFacHuRRi8kbTazNZntkGAR1rYqgN+o +M87om72LO+L19Oywai0LQjR5GgA76kT9OZOvnv6zgXBPlsPUb/h7aKap8rrE/Fkx +BUQ3kTzxHY5W3esGQdiSk33aMkV2BZa9NvPiG8erdQKBgDlpbRVv3ypjDQxdgccT +4K0zyWgG0597PXF+wBCV3+1FryarzlBCaSN53daE/s+XzJh8pZEN5zV8SkjKdI+W +ccDgCZ/FL15OB/AuE6I6/T4g5NWXDqqWt5qMP+SU7oK+IpzpRZyq56SSLCXzvMzk ++PD5J7pSXLRdv9DJURCvZ2ny +-----END PRIVATE KEY----- diff --git a/example/rsa_cert_public.pem b/example/rsa_cert_public.pem new file mode 100644 index 0000000..bf3066c --- /dev/null +++ b/example/rsa_cert_public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx69b2rwfqWdHv9L6e38x +JQ0k9E336wBry76OkFJp/AHczRhIsrVthnmBgTBBp4vyPPf0untElrFamlyY6g5V +6nn2VMz828n3fVEcGodL3BxSpPmzO2bLzsb5UYSmMMd8GjczpXH3rZQeYaRQoh6L +aFCS/bG1Y6AcjG7NwUD/O4A0kQH5IHTYcre90HuJsZVwaOwMvt3USyWZlSHoCQ0N +ircZbINuZ2J/OH6TkB5c6FOATEikWb1X5SlG5SZZacFxEw7w+dhZbyE3qNqnXk2n +qwVJTllQGA99YSc3z1CMp7eC2e5OqH+95ZAR/d2TnFX2HsghrbeHxjDSYiccYx5F +ZwIDAQAB +-----END PUBLIC KEY----- diff --git a/example/rsa_certificate.pem b/example/rsa_certificate.pem new file mode 100644 index 0000000..b52bce0 --- /dev/null +++ b/example/rsa_certificate.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMjCCAhoCCQD3O5GD6JcncDANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJG +UjEWMBQGA1UECAwNSWxlLWRlLUZyYW5jZTEOMAwGA1UEBwwFUGFyaXMxJDAiBgkq +hkiG9w0BCQEWFWdvLmpyb3Vzc2VsQGdtYWlsLmNvbTAeFw0yMzA4MjMxODU3MTJa +Fw0yNDA4MjIxODU3MTJaMFsxCzAJBgNVBAYTAkZSMRYwFAYDVQQIDA1JbGUtZGUt +RnJhbmNlMQ4wDAYDVQQHDAVQYXJpczEkMCIGCSqGSIb3DQEJARYVZ28uanJvdXNz +ZWxAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx69b +2rwfqWdHv9L6e38xJQ0k9E336wBry76OkFJp/AHczRhIsrVthnmBgTBBp4vyPPf0 +untElrFamlyY6g5V6nn2VMz828n3fVEcGodL3BxSpPmzO2bLzsb5UYSmMMd8Gjcz +pXH3rZQeYaRQoh6LaFCS/bG1Y6AcjG7NwUD/O4A0kQH5IHTYcre90HuJsZVwaOwM +vt3USyWZlSHoCQ0NircZbINuZ2J/OH6TkB5c6FOATEikWb1X5SlG5SZZacFxEw7w ++dhZbyE3qNqnXk2nqwVJTllQGA99YSc3z1CMp7eC2e5OqH+95ZAR/d2TnFX2Hsgh +rbeHxjDSYiccYx5FZwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAAN08vxGGcZYKq +/5lPRUYr7+KzMr5wHQdEUluPVtMiYjpOeuP+vXtO+tgmabCJqlXGg/O0H/GhczGX +cGYEPye5ftjiRPYLjvAgHotJH2gnJ5w5y5bRYw3+r/JQkueEd617iHFNL2j0atxf +ULgfOfPXjLu+2Ch8c8z/J5u52H6xpXBNDRNCnfm2nqwMcsRXCitLMENxMv9IBmJD +y5hfMtlcFb7XOmlHr0eRuR1U4IVF4k9v2b1vCWYWJLhJHRWQFaLQPGm0Y/1KL1F3 +eXA81iNBFKIzqgiB0SSRxIt+mx4ZHg7QCwAYqzLFK8m59KRCz+eD1Ho/cO9NC/rZ +vwLdy6e2 +-----END CERTIFICATE----- diff --git a/lib/src/algorithms.dart b/lib/src/algorithms.dart index e364340..e830e49 100644 --- a/lib/src/algorithms.dart +++ b/lib/src/algorithms.dart @@ -8,7 +8,7 @@ import 'package:pointycastle/pointycastle.dart' as pc; import 'exceptions.dart'; import 'keys.dart'; -import 'utils.dart'; +import 'helpers.dart'; abstract class JWTAlgorithm { /// HMAC using SHA-256 hash algorithm diff --git a/lib/src/utils.dart b/lib/src/helpers.dart similarity index 84% rename from lib/src/utils.dart rename to lib/src/helpers.dart index c76e54e..72bb4ad 100644 --- a/lib/src/utils.dart +++ b/lib/src/helpers.dart @@ -1,5 +1,4 @@ import 'dart:convert'; - import 'dart:typed_data'; final jsonBase64 = json.fuse(utf8.fuse(base64Url)); @@ -90,18 +89,11 @@ BigInt bigIntFromBytes(Uint8List bytes) { return bytes.fold(BigInt.zero, (a, b) => a * _b256 + BigInt.from(b)); } -Uint8List hexToUint8List(String hex) { - if (hex.length % 2 != 0) { - throw 'Odd number of hex digits'; - } - var l = hex.length ~/ 2; - var result = Uint8List(l); - for (var i = 0; i < l; ++i) { - var x = int.parse(hex.substring(2 * i, 2 * (i + 1)), radix: 16); - if (x.isNaN) { - throw 'Expected hex string'; - } - result[i] = x; +List chunkString(String s, int chunkSize) { + var chunked = []; + for (var i = 0; i < s.length; i += chunkSize) { + var end = (i + chunkSize < s.length) ? i + chunkSize : s.length; + chunked.add(s.substring(i, end)); } - return result; + return chunked; } diff --git a/lib/src/jwt.dart b/lib/src/jwt.dart index ba29422..afc3df8 100644 --- a/lib/src/jwt.dart +++ b/lib/src/jwt.dart @@ -7,7 +7,7 @@ import 'package:collection/collection.dart'; import 'algorithms.dart'; import 'exceptions.dart'; import 'keys.dart'; -import 'utils.dart'; +import 'helpers.dart'; class JWT { /// Verify a token. diff --git a/lib/src/key_parser.dart b/lib/src/key_parser.dart new file mode 100644 index 0000000..6d1cb33 --- /dev/null +++ b/lib/src/key_parser.dart @@ -0,0 +1,299 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:pointycastle/asn1/object_identifiers.dart'; +import 'package:pointycastle/pointycastle.dart'; +import 'package:pointycastle/ecc/ecc_fp.dart' as ecc_fp; + +import 'helpers.dart'; + +abstract class KeyParser { + static const BEGIN_PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----'; + static const END_PRIVATE_KEY = '-----END PRIVATE KEY-----'; + + static const BEGIN_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----'; + static const END_PUBLIC_KEY = '-----END PUBLIC KEY-----'; + + static const BEGIN_EC_PRIVATE_KEY = '-----BEGIN EC PRIVATE KEY-----'; + static const END_EC_PRIVATE_KEY = '-----END EC PRIVATE KEY-----'; + + static const BEGIN_EC_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----'; + static const END_EC_PUBLIC_KEY = '-----END PUBLIC KEY-----'; + + static const BEGIN_RSA_PRIVATE_KEY = '-----BEGIN RSA PRIVATE KEY-----'; + static const END_RSA_PRIVATE_KEY = '-----END RSA PRIVATE KEY-----'; + + static const BEGIN_RSA_PUBLIC_KEY = '-----BEGIN RSA PUBLIC KEY-----'; + static const END_RSA_PUBLIC_KEY = '-----END RSA PUBLIC KEY-----'; + + //-------------// + // RSA Parsing // + //-------------// + + static RSAPrivateKey rsaPrivateKeyFromPEM(String pem, {bool pkcs1 = false}) { + var bytes = bytesFromPEM(pem); + + if (pkcs1) { + return rsaPrivateKeyPKCS1(bytes); + } else { + return rsaPrivateKey(bytes); + } + } + + static RSAPrivateKey rsaPrivateKey(Uint8List bytes) { + var asn1Parser = ASN1Parser(bytes); + final topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; + final privateKey = topLevelSeq.elements![2]; + + asn1Parser = ASN1Parser(privateKey.valueBytes); + final pkSeq = asn1Parser.nextObject() as ASN1Sequence; + + final modulus = pkSeq.elements![1] as ASN1Integer; + final privateExponent = pkSeq.elements![3] as ASN1Integer; + final p = pkSeq.elements![4] as ASN1Integer; + final q = pkSeq.elements![5] as ASN1Integer; + + return RSAPrivateKey( + modulus.integer!, + privateExponent.integer!, + p.integer, + q.integer, + ); + } + + static RSAPrivateKey rsaPrivateKeyPKCS1(Uint8List bytes) { + var asn1Parser = ASN1Parser(bytes); + var pkSeq = asn1Parser.nextObject() as ASN1Sequence; + + var modulus = pkSeq.elements![1] as ASN1Integer; + var privateExponent = pkSeq.elements![3] as ASN1Integer; + var p = pkSeq.elements![4] as ASN1Integer; + var q = pkSeq.elements![5] as ASN1Integer; + + return RSAPrivateKey( + modulus.integer!, + privateExponent.integer!, + p.integer, + q.integer, + ); + } + + static RSAPublicKey rsaPublicKeyFromPEM(String pem, {bool pkcs1 = false}) { + var bytes = bytesFromPEM(pem); + + if (pkcs1) { + return rsaPublicKeyPKCS1(bytes); + } else { + return rsaPublicKey(bytes); + } + } + + static RSAPublicKey rsaPublicKey(Uint8List bytes) { + final asn1Parser = ASN1Parser(bytes); + final topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; + + ASN1Sequence publicKeySeq; + if (topLevelSeq.elements![1].runtimeType == ASN1BitString) { + final publicKeyBitString = topLevelSeq.elements![1] as ASN1BitString; + + final publicKeyAsn = ASN1Parser( + publicKeyBitString.stringValues as Uint8List?, + ); + publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence; + } else { + publicKeySeq = topLevelSeq; + } + + final modulus = publicKeySeq.elements![0] as ASN1Integer; + final exponent = publicKeySeq.elements![1] as ASN1Integer; + + return RSAPublicKey(modulus.integer!, exponent.integer!); + } + + static RSAPublicKey rsaPublicKeyPKCS1(Uint8List bytes) { + final publicKeyAsn = ASN1Parser(bytes); + final publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence; + final modulus = publicKeySeq.elements![0] as ASN1Integer; + final exponent = publicKeySeq.elements![1] as ASN1Integer; + + return RSAPublicKey(modulus.integer!, exponent.integer!); + } + + //---------------// + // ECDSA Parsing // + //---------------// + + static ECPrivateKey ecPrivateKeyFromPEM(String pem, {bool pkcs1 = false}) { + final bytes = bytesFromPEM(pem); + + if (pkcs1) { + return ecPrivateKeyPKCS1(bytes); + } else { + return ecPrivateKey(bytes); + } + } + + static ECPrivateKey ecPrivateKey(Uint8List bytes) { + var asn1Parser = ASN1Parser(bytes); + var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; + + String? curveName; + + // Parse the PKCS8 format + var innerSeq = topLevelSeq.elements!.elementAt(1) as ASN1Sequence; + var b2 = innerSeq.elements!.elementAt(1) as ASN1ObjectIdentifier; + var b2Data = b2.objectIdentifierAsString; + var b2Curvedata = ObjectIdentifiers.getIdentifierByIdentifier(b2Data); + if (b2Curvedata != null) { + curveName = b2Curvedata['readableName']; + } + + var octetString = topLevelSeq.elements!.elementAt(2) as ASN1OctetString; + asn1Parser = ASN1Parser(octetString.valueBytes); + var octetStringSeq = asn1Parser.nextObject() as ASN1Sequence; + var octetStringKeyData = + octetStringSeq.elements!.elementAt(1) as ASN1OctetString; + + final x = octetStringKeyData.valueBytes!; + + return ECPrivateKey( + osp2i(x), + ECDomainParameters(curveName ?? 'prime256v1'), + ); + } + + static ECPrivateKey ecPrivateKeyPKCS1(Uint8List bytes) { + var asn1Parser = ASN1Parser(bytes); + var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; + + String? curveName; + + // Parse the SEC1 format + var privateKeyAsOctetString = + topLevelSeq.elements!.elementAt(1) as ASN1OctetString; + var choice = topLevelSeq.elements!.elementAt(2); + var s = ASN1Sequence(); + var parser = ASN1Parser(choice.valueBytes); + while (parser.hasNext()) { + s.add(parser.nextObject()); + } + var curveNameOi = s.elements!.elementAt(0) as ASN1ObjectIdentifier; + var data = ObjectIdentifiers.getIdentifierByIdentifier( + curveNameOi.objectIdentifierAsString, + ); + if (data != null) { + curveName = data['readableName']; + } + + final x = privateKeyAsOctetString.valueBytes!; + + return ECPrivateKey( + osp2i(x), + ECDomainParameters(curveName ?? 'prime256v1'), + ); + } + + static ECPublicKey ecPublicKeyFromPEM(String pem) { + final bytes = bytesFromPEM(pem); + + return ecPublicKey(bytes); + } + + static ECPublicKey ecPublicKey(Uint8List bytes) { + if (bytes.elementAt(0) == 0) { + bytes = bytes.sublist(1); + } + var asn1Parser = ASN1Parser(bytes); + var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; + + var algorithmIdentifierSequence = topLevelSeq.elements![0] as ASN1Sequence; + var curveNameOi = algorithmIdentifierSequence.elements!.elementAt(1) + as ASN1ObjectIdentifier; + String? curveName; + var data = ObjectIdentifiers.getIdentifierByIdentifier( + curveNameOi.objectIdentifierAsString); + if (data != null) { + curveName = data['readableName']; + } + + var subjectPublicKey = topLevelSeq.elements![1] as ASN1BitString; + var compressed = false; + var pubBytes = subjectPublicKey.valueBytes!; + if (pubBytes.elementAt(0) == 0) { + pubBytes = pubBytes.sublist(1); + } + + // Looks good so far! + var firstByte = pubBytes.elementAt(0); + if (firstByte != 4) { + compressed = true; + } + var x = pubBytes.sublist(1, (pubBytes.length / 2).round()); + var y = pubBytes.sublist(1 + x.length, pubBytes.length); + var params = ECDomainParameters(curveName ?? 'prime256v1'); + var bigX = decodeBigIntWithSign(1, x); + var bigY = decodeBigIntWithSign(1, y); + var pubKey = ECPublicKey( + ecc_fp.ECPoint( + params.curve as ecc_fp.ECCurve, + params.curve.fromBigInteger(bigX) as ecc_fp.ECFieldElement?, + params.curve.fromBigInteger(bigY) as ecc_fp.ECFieldElement?, + compressed, + ), + params, + ); + return pubKey; + } + + //--------------// + // PEM to Bytes // + //--------------// + + static Uint8List publicKeyBytesFromCertificate(String pem) { + var bytes = bytesFromPEM(pem); + var asn1Parser = ASN1Parser(bytes); + + var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; + var certSq = topLevelSeq.elements!.elementAt(0) as ASN1Sequence; + + int element; + if (certSq.elements!.elementAt(0) is ASN1Integer) { + element = -1; + } else { + element = 0; + } + + final pubKeySequence = certSq.elements!.elementAt(element + 6); + + return pubKeySequence.encodedBytes ?? Uint8List(0); + } + + static Uint8List bytesFromPEM(String pem) { + final lines = LineSplitter.split(pem) + .map((line) => line.trim()) + .where((line) => line.isNotEmpty) + .toList(); + + final base64Data = lines.sublist(1, lines.length - 1).join(''); + + return Uint8List.fromList(base64Decode(base64Data)); + } + + //------------------// + // Helper Functions // + //------------------// + + static BigInt osp2i(Iterable bytes, {Endian endian = Endian.big}) { + var result = BigInt.from(0); + if (endian == Endian.little) { + bytes = bytes.toList().reversed; + } + + for (var byte in bytes) { + result = result << 8; + result |= BigInt.from(byte); + } + + return result; + } +} diff --git a/lib/src/keys.dart b/lib/src/keys.dart index 26dd7bb..3993a93 100644 --- a/lib/src/keys.dart +++ b/lib/src/keys.dart @@ -1,11 +1,10 @@ import 'dart:typed_data'; -import 'package:basic_utils/basic_utils.dart'; -import 'package:dart_jsonwebtoken/src/utils.dart'; import 'package:ed25519_edwards/ed25519_edwards.dart' as ed; import 'package:pointycastle/pointycastle.dart' as pc; import 'exceptions.dart'; +import 'key_parser.dart'; abstract class JWTKey {} @@ -21,17 +20,15 @@ class RSAPrivateKey extends JWTKey { late pc.RSAPrivateKey key; RSAPrivateKey(String pem) { - if (pem.startsWith(CryptoUtils.BEGIN_RSA_PRIVATE_KEY)) { - key = CryptoUtils.rsaPrivateKeyFromPemPkcs1(pem); - } else { - key = CryptoUtils.rsaPrivateKeyFromPem(pem); - } + key = KeyParser.rsaPrivateKeyFromPEM( + pem, + pkcs1: pem.startsWith(KeyParser.BEGIN_RSA_PRIVATE_KEY), + ); } RSAPrivateKey.raw(pc.RSAPrivateKey _key) : key = _key; RSAPrivateKey.clone(RSAPrivateKey _key) : key = _key.key; - RSAPrivateKey.bytes(Uint8List bytes) - : key = CryptoUtils.rsaPrivateKeyFromDERBytes(bytes); + RSAPrivateKey.bytes(Uint8List bytes) : key = KeyParser.rsaPrivateKey(bytes); } /// For RSA algorithm, in verify method @@ -39,34 +36,25 @@ class RSAPublicKey extends JWTKey { late pc.RSAPublicKey key; RSAPublicKey(String pem) { - if (pem.startsWith(CryptoUtils.BEGIN_RSA_PUBLIC_KEY)) { - key = CryptoUtils.rsaPublicKeyFromPemPkcs1(pem); - } else { - key = CryptoUtils.rsaPublicKeyFromPem(pem); - } + key = KeyParser.rsaPublicKeyFromPEM( + pem, + pkcs1: pem.startsWith(KeyParser.BEGIN_RSA_PUBLIC_KEY), + ); } RSAPublicKey.raw(pc.RSAPublicKey _key) : key = _key; RSAPublicKey.clone(RSAPublicKey _key) : key = _key.key; RSAPublicKey.bytes(Uint8List bytes) { try { - key = CryptoUtils.rsaPublicKeyFromDERBytesPkcs1(bytes); + key = KeyParser.rsaPublicKey(bytes); } catch (_) { - key = CryptoUtils.rsaPublicKeyFromDERBytes(bytes); + key = KeyParser.rsaPublicKeyPKCS1(bytes); } } RSAPublicKey.cert(String pem) { - final x509 = X509Utils.x509CertificateFromPem(pem); - final bytes = x509.tbsCertificate?.subjectPublicKeyInfo.bytes; - if (bytes == null) { - throw JWTParseException('x509 Certificate parsing failed'); - } + final bytes = KeyParser.publicKeyBytesFromCertificate(pem); - try { - key = CryptoUtils.rsaPublicKeyFromDERBytesPkcs1(hexToUint8List(bytes)); - } catch (_) { - key = CryptoUtils.rsaPublicKeyFromDERBytes(hexToUint8List(bytes)); - } + key = RSAPublicKey.bytes(bytes).key; } } @@ -76,7 +64,10 @@ class ECPrivateKey extends JWTKey { late int size; ECPrivateKey(String pem) { - final _key = CryptoUtils.ecPrivateKeyFromPem(pem); + final _key = KeyParser.ecPrivateKeyFromPEM( + pem, + pkcs1: pem.startsWith(KeyParser.BEGIN_EC_PRIVATE_KEY), + ); final _params = _key.parameters; if (_params == null) { @@ -100,8 +91,7 @@ class ECPrivateKey extends JWTKey { ECPrivateKey.clone(ECPrivateKey _key) : key = _key.key, size = _key.size; - ECPrivateKey.bytes(Uint8List bytes) - : key = CryptoUtils.ecPrivateKeyFromDerBytes(bytes); + ECPrivateKey.bytes(Uint8List bytes) : key = KeyParser.ecPrivateKey(bytes); } /// For ECDSA algorithm, in verify method @@ -109,21 +99,16 @@ class ECPublicKey extends JWTKey { late pc.ECPublicKey key; ECPublicKey(String pem) { - key = CryptoUtils.ecPublicKeyFromPem(pem); + key = KeyParser.ecPublicKeyFromPEM(pem); } ECPublicKey.raw(pc.ECPublicKey _key) : key = _key; ECPublicKey.clone(ECPublicKey _key) : key = _key.key; - ECPublicKey.bytes(Uint8List bytes) - : key = CryptoUtils.ecPublicKeyFromDerBytes(bytes); + ECPublicKey.bytes(Uint8List bytes) : key = KeyParser.ecPublicKey(bytes); ECPublicKey.cert(String pem) { - final x509 = X509Utils.x509CertificateFromPem(pem); - final bytes = x509.tbsCertificate?.subjectPublicKeyInfo.bytes; - if (bytes == null) { - throw JWTParseException('x509 Certificate parsing failed'); - } + final bytes = KeyParser.publicKeyBytesFromCertificate(pem); - key = CryptoUtils.ecPublicKeyFromDerBytes(hexToUint8List(bytes)); + key = ECPublicKey.bytes(bytes).key; } } diff --git a/pubspec.yaml b/pubspec.yaml index 62ddfb6..53f408f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_jsonwebtoken description: A dart implementation of the famous javascript library 'jsonwebtoken' (JWT). -version: 2.10.0 +version: 2.11.0 repository: https://github.com/jonasroussel/dart_jsonwebtoken homepage: https://github.com/jonasroussel/dart_jsonwebtoken#readme @@ -16,7 +16,6 @@ dependencies: convert: ^3.1.1 collection: ^1.17.1 ed25519_edwards: ^0.3.1 - basic_utils: ^5.6.1 dev_dependencies: lints: ^2.1.1