From 6c828018df4a811335015ca3892915fe33a09434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Obiltschnig?= Date: Tue, 30 Jul 2019 11:19:49 +0200 Subject: [PATCH] added JWT library --- CMakeLists.txt | 21 +- .../include/Poco/Crypto/ECDSADigestEngine.h | 37 +- Crypto/src/ECDSADigestEngine.cpp | 108 +++- JWT/CMakeLists.txt | 37 ++ JWT/Makefile | 15 + JWT/cmake/PocoJWTConfig.cmake | 5 + JWT/include/Poco/JWT/JWT.h | 62 +++ JWT/include/Poco/JWT/JWTException.h | 41 ++ JWT/include/Poco/JWT/Serializer.h | 53 ++ JWT/include/Poco/JWT/Signer.h | 196 +++++++ JWT/include/Poco/JWT/Token.h | 336 ++++++++++++ JWT/src/JWTException.cpp | 35 ++ JWT/src/Serializer.cpp | 81 +++ JWT/src/Signer.cpp | 503 ++++++++++++++++++ JWT/src/Token.cpp | 180 +++++++ JWT/testsuite/CMakeLists.txt | 27 + JWT/testsuite/Makefile | 19 + JWT/testsuite/src/Driver.cpp | 17 + JWT/testsuite/src/JWTTestSuite.cpp | 26 + JWT/testsuite/src/JWTTestSuite.h | 27 + JWT/testsuite/src/SerializerTest.cpp | 131 +++++ JWT/testsuite/src/SerializerTest.h | 43 ++ JWT/testsuite/src/SignerTest.cpp | 466 ++++++++++++++++ JWT/testsuite/src/SignerTest.h | 57 ++ JWT/testsuite/src/TokenTest.cpp | 127 +++++ JWT/testsuite/src/TokenTest.h | 40 ++ JWT/testsuite/src/WinCEDriver.cpp | 30 ++ JWT/testsuite/src/WinDriver.cpp | 28 + components | 1 + 29 files changed, 2739 insertions(+), 10 deletions(-) create mode 100644 JWT/CMakeLists.txt create mode 100644 JWT/Makefile create mode 100644 JWT/cmake/PocoJWTConfig.cmake create mode 100644 JWT/include/Poco/JWT/JWT.h create mode 100644 JWT/include/Poco/JWT/JWTException.h create mode 100644 JWT/include/Poco/JWT/Serializer.h create mode 100644 JWT/include/Poco/JWT/Signer.h create mode 100644 JWT/include/Poco/JWT/Token.h create mode 100644 JWT/src/JWTException.cpp create mode 100644 JWT/src/Serializer.cpp create mode 100644 JWT/src/Signer.cpp create mode 100644 JWT/src/Token.cpp create mode 100644 JWT/testsuite/CMakeLists.txt create mode 100644 JWT/testsuite/Makefile create mode 100644 JWT/testsuite/src/Driver.cpp create mode 100644 JWT/testsuite/src/JWTTestSuite.cpp create mode 100644 JWT/testsuite/src/JWTTestSuite.h create mode 100644 JWT/testsuite/src/SerializerTest.cpp create mode 100644 JWT/testsuite/src/SerializerTest.h create mode 100644 JWT/testsuite/src/SignerTest.cpp create mode 100644 JWT/testsuite/src/SignerTest.h create mode 100644 JWT/testsuite/src/TokenTest.cpp create mode 100644 JWT/testsuite/src/TokenTest.h create mode 100644 JWT/testsuite/src/WinCEDriver.cpp create mode 100644 JWT/testsuite/src/WinDriver.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 412b8f0c3..d765ff0b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,7 +119,7 @@ if(ENABLE_DATA_POSTGRESQL) else() find_package(PostgreSQL) endif() - + if(PostgreSQL_FOUND) option(ENABLE_DATA "Enable SQL" ON) option(ENABLE_DATA_POSTGRESQL "Enable SQL PosgreSQL" ON) @@ -154,6 +154,7 @@ option(ENABLE_PDF "Enable PDF" OFF) option(ENABLE_UTIL "Enable Util" ON) option(ENABLE_NET "Enable Net" ON) option(ENABLE_NETSSL_WIN "Enable NetSSL Windows" OFF) +option(ENABLE_JWT "Enable JWT" OFF) option(ENABLE_SEVENZIP "Enable SevenZip" OFF) option(ENABLE_ZIP "Enable Zip" ON) option(ENABLE_CPPPARSER "Enable C++ parser" OFF) @@ -227,7 +228,7 @@ endif() if(ENABLE_PAGECOMPILER) set(ENABLE_NET ON CACHE BOOL "Enable Net" FORCE) - set(ENABLE_UTIL ON CACHE BOOL "Enable Util" FORCE) + set(ENABLE_UTIL ON CACHE BOOL "Enable Util" FORCE) endif() if(ENABLE_MONGODB OR ENABLE_REDIS) @@ -269,6 +270,11 @@ if(ENABLE_NET AND ENABLE_TESTS) set(ENABLE_UTIL ON CACHE BOOL "Enable Util" FORCE) endif() +if(ENABLE_JWT) + set(ENABLE_CRYPTO ON CACHE BOOL "Enable Crypto" FORCE) + set(ENABLE_JSON ON CACHE BOOL "Enable JSON" FORCE) +endif() + if(ENABLE_PDF) set(ENABLE_UTIL ON CACHE BOOL "Enable Util" FORCE) set(ENABLE_XML ON CACHE BOOL "Enable XML" FORCE) @@ -282,7 +288,7 @@ endif() if(ENABLE_SEVENZIP OR ENABLE_ZIP) set(ENABLE_UTIL ON CACHE BOOL "Enable Util" FORCE) - set(ENABLE_XML ON CACHE BOOL "Enable XML" FORCE) + set(ENABLE_XML ON CACHE BOOL "Enable XML" FORCE) endif() if(ENABLE_UTIL AND ENABLE_TESTS) @@ -316,6 +322,11 @@ if(ENABLE_NET) list(APPEND Poco_COMPONENTS "Net") endif() +if(EXISTS ${PROJECT_SOURCE_DIR}/JWT AND ENABLE_JWT) + add_subdirectory(JWT) + list(APPEND Poco_COMPONENTS "JWT") +endif() + if(EXISTS ${PROJECT_SOURCE_DIR}/MongoDB AND ENABLE_MONGODB) add_subdirectory(MongoDB) list(APPEND Poco_COMPONENTS "MongoDB") @@ -366,7 +377,7 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/Zip AND ENABLE_ZIP) list(APPEND Poco_COMPONENTS "Zip") endif() -if(APRUTIL_FOUND AND APACHE_FOUND AND +if(APRUTIL_FOUND AND APACHE_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/ApacheConnector AND ENABLE_APACHECONNECTOR) add_subdirectory(ApacheConnector) list(APPEND Poco_COMPONENTS "ApacheConnector") @@ -447,7 +458,7 @@ if(POCO_UNBUNDLED) DESTINATION "${PocoConfigPackageLocation}") install(FILES cmake/V39/FindEXPAT.cmake DESTINATION "${PocoConfigPackageLocation}/V39") - install(FILES cmake/V313/FindSQLite3.cmake + install(FILES cmake/V313/FindSQLite3.cmake DESTINATION "${PocoConfigPackageLocation}/V313") endif() diff --git a/Crypto/include/Poco/Crypto/ECDSADigestEngine.h b/Crypto/include/Poco/Crypto/ECDSADigestEngine.h index ed6fab442..d8021619e 100644 --- a/Crypto/include/Poco/Crypto/ECDSADigestEngine.h +++ b/Crypto/include/Poco/Crypto/ECDSADigestEngine.h @@ -40,7 +40,7 @@ class Crypto_API ECDSADigestEngine: public Poco::DigestEngine /// signed. Then, the hash value is encrypted, using /// the ECDSA private key. /// - /// To verify a signature, pass it to the verify() + /// To verify a signature, pass it to the verify() /// member function. It will decrypt the signature /// using the ECDSA public key and compare the resulting /// hash with the actual hash of the data. @@ -64,11 +64,11 @@ public: void reset(); /// Resets the engine so that a new /// digest can be computed. - + const DigestEngine::Digest& digest(); - /// Finishes the computation of the digest + /// Finishes the computation of the digest /// (the first time it's called) and - /// returns the message digest. + /// returns the message digest. /// /// Can be called multiple times. @@ -95,6 +95,35 @@ private: }; +class Crypto_API ECDSASignature + /// A helper class for dealing with ECDSA signatures. +{ +public: + typedef std::vector ByteVec; + + explicit ECDSASignature(const ByteVec& derSignature); + /// Creates the ECDSASignature from a DER-encoded signature. + + ECDSASignature(const ByteVec& rawR, const ByteVec& rawS); + /// Creates the ECDSASignature from raw r and s values. + + ~ECDSASignature(); + /// Destroys the ECDSASignature. + + ByteVec toDER() const; + /// Returns a buffer containing the DER-encoded signature. + + ByteVec rawR() const; + /// Returns a raw P value. + + ByteVec rawS() const; + /// Returns a raw Q value. + +private: + ECDSA_SIG* _pSig; +}; + + } } // namespace Poco::Crypto diff --git a/Crypto/src/ECDSADigestEngine.cpp b/Crypto/src/ECDSADigestEngine.cpp index 59512770c..f1fc3fa0e 100644 --- a/Crypto/src/ECDSADigestEngine.cpp +++ b/Crypto/src/ECDSADigestEngine.cpp @@ -14,6 +14,7 @@ #include "Poco/Crypto/ECDSADigestEngine.h" +#include "Poco/Crypto/CryptoException.h" #include @@ -21,6 +22,11 @@ namespace Poco { namespace Crypto { +// +// ECDSADigestEngine +// + + ECDSADigestEngine::ECDSADigestEngine(const ECKey& key, const std::string &name): _key(key), _engine(name) @@ -46,7 +52,7 @@ void ECDSADigestEngine::reset() _signature.clear(); } - + const DigestEngine::Digest& ECDSADigestEngine::digest() { if (_digest.empty()) @@ -97,4 +103,104 @@ void ECDSADigestEngine::updateImpl(const void* data, std::size_t length) } +// +// ECDSASignature +// + + +ECDSASignature::ECDSASignature(const ByteVec& derSignature) +{ + poco_assert (!derSignature.empty()); + + const unsigned char* p = &derSignature[0]; + _pSig = d2i_ECDSA_SIG(0, &p, derSignature.size()); + if (!_pSig) + throw OpenSSLException(); +} + + +ECDSASignature::ECDSASignature(const ByteVec& rawR, const ByteVec& rawS): + _pSig(ECDSA_SIG_new()) +{ + poco_assert (!rawR.empty() && !rawS.empty()); + + if (!_pSig) throw CryptoException("cannot allocate ECDSA signature"); + + try + { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + ECDSA_SIG_set0(_pSig, + BN_bin2bn(&rawR[0], rawR.size(), 0), + BN_bin2bn(&rawS[0], rawS.size(), 0)); + if (ECDSA_SIG_get0_r(_pSig) == 0 || ECDSA_SIG_get0_s(_pSig) == 0) + throw CryptoException("failed to decode R and S values"); +#else + if (!BN_bin2bn(&rawR[0], rawR.size(), _pSig->r)) + throw OpenSSLException(); + if (!BN_bin2bn(&rawS[0], rawS.size(), _pSig->s)) + throw OpenSSLException(); +#endif + } + catch (...) + { + ECDSA_SIG_free(_pSig); + throw; + } +} + + +ECDSASignature::~ECDSASignature() +{ + ECDSA_SIG_free(_pSig); +} + + +ECDSASignature::ByteVec ECDSASignature::toDER() const +{ + int size = i2d_ECDSA_SIG(_pSig, 0); + if (size > 0) + { + ByteVec buffer(size); + unsigned char* p = &buffer[0]; + i2d_ECDSA_SIG(_pSig, &p); + return buffer; + } + else throw OpenSSLException(); +} + + +ECDSASignature::ByteVec ECDSASignature::rawR() const +{ + ByteVec buffer; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const BIGNUM* pR = ECDSA_SIG_get0_r(_pSig); +#else + const BIGNUM* pR = _pSig->r; +#endif + if (pR) + { + buffer.resize(BN_num_bytes(pR)); + BN_bn2bin(pR, &buffer[0]); + } + return buffer; +} + + +ECDSASignature::ByteVec ECDSASignature::rawS() const +{ + ByteVec buffer; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const BIGNUM* pS = ECDSA_SIG_get0_s(_pSig); +#else + const BIGNUM* pS = _pSig->s; +#endif + if (pS) + { + buffer.resize(BN_num_bytes(pS)); + BN_bn2bin(pS, &buffer[0]); + } + return buffer; +} + + } } // namespace Poco::Crypto diff --git a/JWT/CMakeLists.txt b/JWT/CMakeLists.txt new file mode 100644 index 000000000..c0c7f5e85 --- /dev/null +++ b/JWT/CMakeLists.txt @@ -0,0 +1,37 @@ +# Sources +file(GLOB SRCS_G "src/*.cpp") +POCO_SOURCES_AUTO( SRCS ${SRCS_G}) + +# Headers +file(GLOB_RECURSE HDRS_G "include/*.h" ) +POCO_HEADERS_AUTO( SRCS ${HDRS_G}) + +# Version Resource +if(MSVC AND NOT POCO_STATIC) + source_group("Resources" FILES ${CMAKE_SOURCE_DIR}/DLLVersion.rc) + list(APPEND SRCS ${CMAKE_SOURCE_DIR}/DLLVersion.rc) +endif() + +add_library(JWT ${SRCS} ) +add_library(Poco::JWT ALIAS JWT) +set_target_properties( JWT + PROPERTIES + VERSION ${SHARED_LIBRARY_VERSION} SOVERSION ${SHARED_LIBRARY_VERSION} + OUTPUT_NAME PocoJWT + DEFINE_SYMBOL JWT_EXPORTS + ) + +target_link_libraries(JWT PUBLIC Poco::JSON Poco::Crypto) +target_include_directories(JWT + PUBLIC + $ + $ + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +POCO_INSTALL(JWT) +POCO_GENERATE_PACKAGE(JWT) + +if (ENABLE_TESTS) + add_subdirectory(testsuite) +endif () diff --git a/JWT/Makefile b/JWT/Makefile new file mode 100644 index 000000000..0e5a4b5c3 --- /dev/null +++ b/JWT/Makefile @@ -0,0 +1,15 @@ +# +# Makefile +# +# Makefile for Poco JWT +# + +include $(POCO_BASE)/build/rules/global + +objects = Token Signer Serializer JWTException + +target = PocoJWT +target_version = $(LIBVERSION) +target_libs = PocoCrypto PocoJSON PocoFoundation + +include $(POCO_BASE)/build/rules/lib diff --git a/JWT/cmake/PocoJWTConfig.cmake b/JWT/cmake/PocoJWTConfig.cmake new file mode 100644 index 000000000..ac7080eab --- /dev/null +++ b/JWT/cmake/PocoJWTConfig.cmake @@ -0,0 +1,5 @@ +include(CMakeFindDependencyMacro) +find_dependency(PocoFoundation) +find_dependency(PocoJSON) +find_dependency(PocoCrypto) +include("${CMAKE_CURRENT_LIST_DIR}/PocoJWTTargets.cmake") diff --git a/JWT/include/Poco/JWT/JWT.h b/JWT/include/Poco/JWT/JWT.h new file mode 100644 index 000000000..d105a1b1e --- /dev/null +++ b/JWT/include/Poco/JWT/JWT.h @@ -0,0 +1,62 @@ +// +// JWT.h +// +// Library: JWT +// Package: JWT +// Module: JWT +// +// Basic definitions for the Poco JWT library. +// This file must be the first file included by every other JWT +// header file. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef JWT_JWT_INCLUDED +#define JWT_JWT_INCLUDED + + +#include "Poco/Foundation.h" + + +// +// The following block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the JWT_EXPORTS +// symbol defined on the command line. this symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// JWT_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +// +#if defined(_WIN32) && defined(POCO_DLL) + #if defined(JWT_EXPORTS) + #define JWT_API __declspec(dllexport) + #else + #define JWT_API __declspec(dllimport) + #endif +#endif + + +#if !defined(JWT_API) + #if !defined(POCO_NO_GCC_API_ATTRIBUTE) && defined (__GNUC__) && (__GNUC__ >= 4) + #define JWT_API __attribute__ ((visibility ("default"))) + #else + #define JWT_API + #endif +#endif + + +// +// Automatically link JWT library. +// +#if defined(_MSC_VER) + #if !defined(POCO_NO_AUTOMATIC_LIBS) && !defined(JWT_EXPORTS) + #pragma comment(lib, "PocoJWT" POCO_LIB_SUFFIX) + #endif +#endif + + +#endif // JWT_JWT_INCLUDED diff --git a/JWT/include/Poco/JWT/JWTException.h b/JWT/include/Poco/JWT/JWTException.h new file mode 100644 index 000000000..ace75a8ea --- /dev/null +++ b/JWT/include/Poco/JWT/JWTException.h @@ -0,0 +1,41 @@ +// +// JWTException.h +// +// Library: JWT +// Package: JWT +// Module: JWTException +// +// Definition of the JWTException class. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef JWT_JWTException_INCLUDED +#define JWT_JWTException_INCLUDED + + +#include "Poco/JWT/JWT.h" +#include "Poco/Exception.h" + + +namespace Poco { +namespace JWT { + + +POCO_DECLARE_EXCEPTION(JWT_API, JWTException, Poco::Exception) +POCO_DECLARE_EXCEPTION(JWT_API, ParseException, JWTException) +POCO_DECLARE_EXCEPTION(JWT_API, UnsupportedAlgorithmException, JWTException) +POCO_DECLARE_EXCEPTION(JWT_API, UnallowedAlgorithmException, JWTException) +POCO_DECLARE_EXCEPTION(JWT_API, SignatureException, JWTException) +POCO_DECLARE_EXCEPTION(JWT_API, SignatureVerificationException, SignatureException) +POCO_DECLARE_EXCEPTION(JWT_API, SignatureGenerationException, SignatureException) + + +} } // namespace Poco::JWT + + +#endif // JWT_JWTException_INCLUDED diff --git a/JWT/include/Poco/JWT/Serializer.h b/JWT/include/Poco/JWT/Serializer.h new file mode 100644 index 000000000..f3b76204f --- /dev/null +++ b/JWT/include/Poco/JWT/Serializer.h @@ -0,0 +1,53 @@ +// +// Serializer.h +// +// Library: JWT +// Package: JWT +// Module: Serializer +// +// Definition of the Serializer class. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef JWT_Serializer_INCLUDED +#define JWT_Serializer_INCLUDED + + +#include "Poco/JWT/JWT.h" +#include "Poco/JSON/Object.h" + + +namespace Poco { +namespace JWT { + + +class JWT_API Serializer + /// A helper class for serializing and deserializing JWTs. +{ +public: + static std::string serialize(const Poco::JSON::Object& object); + /// Serializes and base64-encodes a JSON object. + + static void serialize(const Poco::JSON::Object& object, std::ostream& stream); + /// Serializes and base64-encodes a JSON object. + + static Poco::JSON::Object::Ptr deserialize(const std::string& serialized); + /// Attempts to deserialize a base64-encoded serialized JSON object. + + static Poco::JSON::Object::Ptr deserialize(std::istream& stream); + /// Attempts to deserialize a base64-encoded serialized JSON object. + + static std::vector split(const std::string& token); + /// Splits a serialized JWT into its components. +}; + + +} } // namespace Poco::JWT + + +#endif // JWT_Serializer_INCLUDED diff --git a/JWT/include/Poco/JWT/Signer.h b/JWT/include/Poco/JWT/Signer.h new file mode 100644 index 000000000..0de5967b5 --- /dev/null +++ b/JWT/include/Poco/JWT/Signer.h @@ -0,0 +1,196 @@ +// +// Signer.h +// +// Library: JWT +// Package: JWT +// Module: Signer +// +// Definition of the Signer class. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef JWT_Signer_INCLUDED +#define JWT_Signer_INCLUDED + + +#include "Poco/JWT/JWT.h" +#include "Poco/JWT/Token.h" +#include "Poco/Crypto/RSAKey.h" +#include "Poco/Crypto/ECKey.h" +#include "Poco/DigestEngine.h" +#include + + +namespace Poco { +namespace JWT { + + +class JWT_API Signer + /// This class signs and verifies the signature of JSON Web Tokens. + /// + /// The following signing algorithms are supported: + /// - HS256 (HMAC using SHA256) + /// - HS384 (HMAC using SHA384) + /// - HS512 (HMAC using SHA512) + /// - RS256 (RSA SSA PKCS1 v1.5 using SHA256) + /// - RS384 (RSA SSA PKCS1 v1.5 using SHA384) + /// - RS512 (RSA SSA PKCS1 v1.5 using SHA512) + /// - ES256 (ECDSA using P-256 and SHA-256) + /// - ES384 (ECDSA using P-256 and SHA-384) + /// - ES512 (ECDSA using P-256 and SHA-512) +{ +public: + Signer(); + /// Creates a Signer. + /// + /// For signing and verification, a key must be set using the + /// setHMACKey(), setRSAKey() or setECKey() methods. + /// + /// Sets HS256 as the only allowed algorithm. + /// Call setAlgorithms() or addAlgorithm() to allow additional + /// algorithms for verification. + + explicit Signer(const std::string& hmacKey); + /// Creates the Signer using the given secret/key for HMAC-based signing and verification. + /// + /// Sets HS256 as the only allowed algorithm. + /// Call setAlgorithms() or addAlgorithm() to allow additional + /// algorithms for verification. + + explicit Signer(const Poco::SharedPtr& pRSAKey); + /// Creates the Signer using the given secret/key for RSA-based signing and verification. + /// + /// Sets HS256 as the only allowed algorithm. + /// Call setAlgorithms() or addAlgorithm() to allow additional + /// algorithms for verification. + + explicit Signer(const Poco::SharedPtr& pECKey); + /// Creates the Signer using the given secret/key for EC-based signing and verification. + /// + /// Sets HS256 as the only allowed algorithm. + /// Call setAlgorithms() or addAlgorithm() to allow additional + /// algorithms for verification. + + ~Signer(); + /// Destroys the Signer. + + Signer& setAlgorithms(const std::set& algorithms); + /// Sets the allowed algorithms for signing. + /// + /// When verifying JWTs, the algorithm used for signing + /// must be one of the allowed algorithms. + + const std::set& getAlgorithms() const; + /// Returns the allowed algorithms for signing. + + Signer& addAlgorithm(const std::string& algorithm); + /// Adds an algorithm to the set of allowed algorithms. + + Signer& addAllAlgorithms(); + /// Adds all supported algorithm to the set of allowed algorithms. + + Signer& setHMACKey(const std::string& key); + /// Sets the key used for HMAC-based signing and verification. + + const std::string getHMACKey() const; + /// Returns the key used for HMAC-based signing and verification. + + Signer& setRSAKey(const Poco::SharedPtr& pKey); + /// Sets the key used for RSA-based signing and verification. + + Poco::SharedPtr getRSAKey() const; + /// Returns the key used for RSA-based signing and verification. + + Signer& setECKey(const Poco::SharedPtr& pKey); + /// Sets the key used for EC-based signing and verification. + + Poco::SharedPtr getECKey() const; + /// Returns the key used for EC-based signing and verification. + + std::string sign(Token& token, const std::string& algorithm) const; + /// Signs the given token using the given algorithm. + /// + /// An appropriate key must have been provided prior to calling sign(). + /// + /// Returns the serialized JWT including the signature. + + Token verify(const std::string& jwt) const; + /// Verifies the given serialized JSON Web Token. + /// + /// An appropriate key must have been provided prior to calling verify(). + /// + /// If successful, returns a Token object. + /// If not successful, throws a SignatureVerificationException. + + bool tryVerify(const std::string& jwt, Token& token) const; + /// Verifies the given serialized JSON Web Token and stores + /// it in the given Token object. + /// + /// An appropriate key must have been provided prior to calling verify(). + /// + /// If successful, returns true, otherwise false. + + static const std::string ALGO_NONE; + static const std::string ALGO_HS256; + static const std::string ALGO_HS384; + static const std::string ALGO_HS512; + static const std::string ALGO_RS256; + static const std::string ALGO_RS384; + static const std::string ALGO_RS512; + static const std::string ALGO_ES256; + static const std::string ALGO_ES384; + static const std::string ALGO_ES512; + +protected: + static std::string encode(const Poco::DigestEngine::Digest& digest); + static Poco::DigestEngine::Digest decode(const std::string& signature); + +private: + Signer(const Signer&); + Signer& operator = (const Signer&); + + std::set _algorithms; + std::string _hmacKey; + Poco::SharedPtr _pRSAKey; + Poco::SharedPtr _pECKey; +}; + + +// +// inlines +// + + +inline const std::set& Signer::getAlgorithms() const +{ + return _algorithms; +} + + +inline const std::string Signer::getHMACKey() const +{ + return _hmacKey; +} + + +inline Poco::SharedPtr Signer::getRSAKey() const +{ + return _pRSAKey; +} + + +inline Poco::SharedPtr Signer::getECKey() const +{ + return _pECKey; +} + + +} } // namespace Poco::JWT + + +#endif // JWT_Signer_INCLUDED diff --git a/JWT/include/Poco/JWT/Token.h b/JWT/include/Poco/JWT/Token.h new file mode 100644 index 000000000..86fb6531f --- /dev/null +++ b/JWT/include/Poco/JWT/Token.h @@ -0,0 +1,336 @@ +// +// Token.h +// +// Library: JWT +// Package: JWT +// Module: Token +// +// Definition of the Token class. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef JWT_Token_INCLUDED +#define JWT_Token_INCLUDED + + +#include "Poco/JWT/JWT.h" +#include "Poco/JSON/Object.h" +#include "Poco/Timestamp.h" + + +namespace Poco { +namespace JWT { + + +class JWT_API Token + /// This class represents a JSON Web Token (JWT) according to RFC 7519. + /// + /// To create and sign a JWT (using the Signer class): + /// + /// Token token; + /// token.setType("JWT"); + /// token.setSubject("1234567890"); + /// token.payload().set("name", std::string("John Doe")); + /// token.setIssuedAt(Poco::Timestamp())); + /// + /// Signer signer("0123456789ABCDEF0123456789ABCDEF"); + /// std::string jwt = signer.sign(token, Signer::ALGO_HS256); + /// + /// To verify a signed token: + /// + /// std::string jwt( + /// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + /// "eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ." + /// "qn9G7NwFEOjIh-7hfCUDZA1aJeQmf7I7YvzCBcdenGw"); + /// + /// Signer signer("0123456789ABCDEF0123456789ABCDEF"); + /// Token token = signer.verify(jwt); +{ +public: + Token(); + /// Creates an empty JSON Web Token. + + explicit Token(const std::string& token); + /// Creates a JSON Web Token from its serialized string representation. + + Token(const Token& token); + /// Creates a JSON Web Token by copying another one. + + ~Token(); + /// Destroys the Token. + + Token& operator = (const Token& token); + /// Assignment operator. + + Token& operator = (const std::string& token); + /// Parses and assigns serialized JWT. + + std::string toString() const; + /// Returns the serialized string representation of the JSON Web Token. + + const Poco::JSON::Object& header() const; + /// Returns the header JSON object. + + Poco::JSON::Object& header(); + /// Returns the header JSON object. + + const Poco::JSON::Object& payload() const; + /// Returns the payload JSON object. + + Poco::JSON::Object& payload(); + /// Returns the payload JSON object. + + const std::string& signature() const; + /// Returns the signature string. + + void setIssuer(const std::string& issuer); + /// Sets the issuer ("iss" claim in payload). + + std::string getIssuer() const; + /// Returns the issuer, or an empty string if no issuer has been specified. + + void setSubject(const std::string& subject); + /// Sets the subject ("sub" claim in paylod). + + std::string getSubject() const; + /// Returns the subject, or an empty string if no issuer has been specified. + + void setAudience(const std::string& audience); + /// Sets the audience ("aud" claim in payload) to a single value. + + void setAudience(const std::vector& audience); + /// Sets the audience ("aud" claim in payload). + + std::vector getAudience() const; + /// Returns the audience. + + void setExpiration(const Poco::Timestamp& expiration); + /// Sets the expiration ("exp" claim in payload). + + Poco::Timestamp getExpiration() const; + /// Returns the expiration, or a zero Poco::Timestamp if no expiration has been specified. + + void setNotBefore(const Poco::Timestamp& notBefore); + /// Sets the not-before time ("nbf" claim in payload). + + Poco::Timestamp getNotBefore() const; + /// Returns the not-before time, or a zero Poco::Timestamp if no not-before time has been specified. + + void setIssuedAt(const Poco::Timestamp& issuedAt); + /// Sets the issued-at time ("iat" claim in payload). + + Poco::Timestamp getIssuedAt() const; + /// Returns the issued-at time, or a zero Poco::Timestamp if no issued-at time has been specified. + + void setId(const std::string& id); + /// Sets the JWT ID ("jti" claim in payload). + + std::string getId() const; + /// Returns the JWT ID, or an empty string if no JWT ID has been specified. + + void setType(const std::string& type); + /// Sets the JWT type ("typ" claim in payload). + + std::string getType() const; + /// Returns the JWT type or an empty string if no type has been specified. + + void setAlgorithm(const std::string& algorithm); + /// Sets the JWT signature algorithm ("alg" claim in header). + + std::string getAlgorithm() const; + /// Returns the JWT signature algorithm, or an empty string if no algorithm has been specified. + + void setContentType(const std::string& contentType); + /// Sets the JWT content type ("cty" claim in header"). + + std::string getContentType() const; + /// Returns the JWT content type, or an empty string if no content type has been specified. + + static const std::string CLAIM_ISSUER; + static const std::string CLAIM_SUBJECT; + static const std::string CLAIM_AUDIENCE; + static const std::string CLAIM_EXPIRATION; + static const std::string CLAIM_NOT_BEFORE; + static const std::string CLAIM_ISSUED_AT; + static const std::string CLAIM_JWT_ID; + static const std::string CLAIM_TYPE; + static const std::string CLAIM_ALGORITHM; + static const std::string CLAIM_CONTENT_TYPE; + +protected: + Token(const std::string& header, const std::string& payload, const std::string& signature); + + void sign(const std::string& signature); + void setTimestamp(const std::string& claim, const Poco::Timestamp& ts); + Poco::Timestamp getTimestamp(const std::string& claim) const; + void assign(const std::string& header, const std::string& payload, const std::string& signature); + +private: + Poco::JSON::Object::Ptr _pHeader; + Poco::JSON::Object::Ptr _pPayload; + std::string _signature; + + static const std::string EMPTY; + + friend class Signer; +}; + + +// +// inlines +// + + +inline const Poco::JSON::Object& Token::header() const +{ + return *_pHeader; +} + + +inline Poco::JSON::Object& Token::header() +{ + return *_pHeader; +} + + +inline const Poco::JSON::Object& Token::payload() const +{ + return *_pPayload; +} + + +inline Poco::JSON::Object& Token::payload() +{ + return *_pPayload; +} + + +inline const std::string& Token::signature() const +{ + return _signature; +} + + +inline void Token::setIssuer(const std::string& issuer) +{ + _pPayload->set(CLAIM_ISSUER, issuer); +} + + +inline std::string Token::getIssuer() const +{ + return _pPayload->optValue(CLAIM_ISSUER, EMPTY); +} + + +inline void Token::setSubject(const std::string& subject) +{ + _pPayload->set(CLAIM_SUBJECT, subject); +} + + +inline std::string Token::getSubject() const +{ + return _pPayload->optValue(CLAIM_SUBJECT, EMPTY); +} + + +inline void Token::setAudience(const std::string& audience) +{ + _pPayload->set(CLAIM_AUDIENCE, audience); +} + + +inline void Token::setExpiration(const Poco::Timestamp& expiration) +{ + setTimestamp(CLAIM_EXPIRATION, expiration); +} + + +inline Poco::Timestamp Token::getExpiration() const +{ + return getTimestamp(CLAIM_EXPIRATION); +} + + +inline void Token::setNotBefore(const Poco::Timestamp& notBefore) +{ + setTimestamp(CLAIM_NOT_BEFORE, notBefore); +} + + +inline Poco::Timestamp Token::getNotBefore() const +{ + return getTimestamp(CLAIM_NOT_BEFORE); +} + + +inline void Token::setIssuedAt(const Poco::Timestamp& issuedAt) +{ + setTimestamp(CLAIM_ISSUED_AT, issuedAt); +} + + +inline Poco::Timestamp Token::getIssuedAt() const +{ + return getTimestamp(CLAIM_ISSUED_AT); +} + + +inline void Token::setId(const std::string& id) +{ + _pPayload->set(CLAIM_JWT_ID, id); +} + + +inline std::string Token::getId() const +{ + return _pPayload->optValue(CLAIM_JWT_ID, EMPTY); +} + + +inline void Token::setType(const std::string& type) +{ + _pHeader->set(CLAIM_TYPE, type); +} + + +inline std::string Token::getType() const +{ + return _pHeader->optValue(CLAIM_TYPE, EMPTY); +} + + +inline void Token::setAlgorithm(const std::string& algorithm) +{ + _pHeader->set(CLAIM_ALGORITHM, algorithm); +} + + +inline std::string Token::getAlgorithm() const +{ + return _pHeader->optValue(CLAIM_ALGORITHM, EMPTY); +} + + +inline void Token::setContentType(const std::string& contentType) +{ + _pHeader->set(CLAIM_CONTENT_TYPE, contentType); +} + + +inline std::string Token::getContentType() const +{ + return _pHeader->optValue(CLAIM_CONTENT_TYPE, EMPTY); +} + + +} } // namespace Poco::JWT + + +#endif // JWT_Token_INCLUDED diff --git a/JWT/src/JWTException.cpp b/JWT/src/JWTException.cpp new file mode 100644 index 000000000..9de0df73f --- /dev/null +++ b/JWT/src/JWTException.cpp @@ -0,0 +1,35 @@ +// +// JWTException.cpp +// +// Library: JWT +// Package: JWT +// Module: JWTException +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "Poco/JWT/JWTException.h" +#include + + +using Poco::Exception; + + +namespace Poco { +namespace JWT { + + +POCO_IMPLEMENT_EXCEPTION(JWTException, Exception, "JWT Exception") +POCO_IMPLEMENT_EXCEPTION(ParseException, JWTException, "JWT parsing failed") +POCO_IMPLEMENT_EXCEPTION(UnsupportedAlgorithmException, JWTException, "Unsupported signing algorithm") +POCO_IMPLEMENT_EXCEPTION(UnallowedAlgorithmException, JWTException, "Unallowed signing algorithm") +POCO_IMPLEMENT_EXCEPTION(SignatureException, JWTException, "JWT Signature Exception") +POCO_IMPLEMENT_EXCEPTION(SignatureVerificationException, SignatureException, "JWT signature verification failed") +POCO_IMPLEMENT_EXCEPTION(SignatureGenerationException, SignatureException, "JWT signature generation failed") + + +} } // namespace Poco::JWT diff --git a/JWT/src/Serializer.cpp b/JWT/src/Serializer.cpp new file mode 100644 index 000000000..cc2972184 --- /dev/null +++ b/JWT/src/Serializer.cpp @@ -0,0 +1,81 @@ +// +// Serializer.cpp +// +// Library: JWT +// Package: JWT +// Module: Serializer +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "Poco/JWT/Serializer.h" +#include "Poco/JWT/JWTException.h" +#include "Poco/JSON/Parser.h" +#include "Poco/Base64Encoder.h" +#include "Poco/Base64Decoder.h" +#include "Poco/MemoryStream.h" +#include "Poco/StringTokenizer.h" +#include + + +namespace Poco { +namespace JWT { + + +std::string Serializer::serialize(const Poco::JSON::Object& object) +{ + std::ostringstream stream; + serialize(object, stream); + return stream.str(); +} + + +void Serializer::serialize(const Poco::JSON::Object& object, std::ostream& stream) +{ + Poco::Base64Encoder encoder(stream, Poco::BASE64_URL_ENCODING | Poco::BASE64_NO_PADDING); + object.stringify(encoder); + encoder.close(); +} + + +Poco::JSON::Object::Ptr Serializer::deserialize(const std::string& serialized) +{ + Poco::MemoryInputStream stream(serialized.data(), serialized.size()); + return deserialize(stream); +} + + +Poco::JSON::Object::Ptr Serializer::deserialize(std::istream& stream) +{ + Poco::Base64Decoder decoder(stream, Poco::BASE64_URL_ENCODING | Poco::BASE64_NO_PADDING); + Poco::JSON::Parser parser; + try + { + Poco::Dynamic::Var json = parser.parse(decoder); + Poco::JSON::Object::Ptr pObject = json.extract(); + return pObject; + } + catch (Poco::BadCastException&) + { + throw ParseException("String does not deserialize to a JSON object"); + } + catch (Poco::Exception& exc) + { + throw ParseException("Failed to deserialize JWT component", exc.displayText()); + } +} + + +std::vector Serializer::split(const std::string& token) +{ + Poco::StringTokenizer tokenizer(token, "."); + std::vector result(tokenizer.begin(), tokenizer.end()); + return result; +} + + +} } // namespace Poco::JWT diff --git a/JWT/src/Signer.cpp b/JWT/src/Signer.cpp new file mode 100644 index 000000000..5c42d0812 --- /dev/null +++ b/JWT/src/Signer.cpp @@ -0,0 +1,503 @@ +// +// Signer.cpp +// +// Library: JWT +// Package: JWT +// Module: Signer +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "Poco/JWT/Signer.h" +#include "Poco/JWT/Serializer.h" +#include "Poco/JWT/JWTException.h" +#include "Poco/HMACEngine.h" +#include "Poco/RefCountedObject.h" +#include "Poco/AutoPtr.h" +#include "Poco/DynamicFactory.h" +#include "Poco/MemoryStream.h" +#include "Poco/Base64Encoder.h" +#include "Poco/Base64Decoder.h" +#include "Poco/Crypto/DigestEngine.h" +#include "Poco/Crypto/RSADigestEngine.h" +#include "Poco/Crypto/ECDSADigestEngine.h" +#include + + +namespace Poco { +namespace JWT { + + +// +// Algorithm +// + + +class Algorithm: public Poco::RefCountedObject + /// Abstraction of an algorithm for signing JWTs. +{ +public: + typedef Poco::AutoPtr Ptr; + + Algorithm(); + /// Creates the Algorithm. + + virtual ~Algorithm(); + /// Destroys the Algorithm. + + virtual Poco::DigestEngine::Digest sign(const Signer& signer, const std::string& header, const std::string& payload) = 0; + /// Signs the header and payload and returns the signature. + + virtual bool verify(const Signer& signer, const std::string& header, const std::string& payload, const Poco::DigestEngine::Digest& signature) = 0; + /// Verifies the header and payload against the signature. + /// + /// Returns true if the signature is correct, otherwise false. + + static const std::string SHA256; + static const std::string SHA384; + static const std::string SHA512; +}; + + +Algorithm::Algorithm() +{ +} + + +Algorithm::~Algorithm() +{ +} + + +const std::string Algorithm::SHA256("SHA256"); +const std::string Algorithm::SHA384("SHA384"); +const std::string Algorithm::SHA512("SHA512"); + + +class SHA256Engine: public Poco::Crypto::DigestEngine +{ +public: + enum + { + BLOCK_SIZE = 64, + DIGEST_SIZE = 32 + }; + + SHA256Engine(): + Poco::Crypto::DigestEngine(Algorithm::SHA256) + { + } +}; + + +class SHA384Engine: public Poco::Crypto::DigestEngine +{ +public: + enum + { + BLOCK_SIZE = 128, + DIGEST_SIZE = 48 + }; + + SHA384Engine(): + Poco::Crypto::DigestEngine(Algorithm::SHA384) + { + } +}; + + +class SHA512Engine: public Poco::Crypto::DigestEngine +{ +public: + enum + { + BLOCK_SIZE = 128, + DIGEST_SIZE = 64 + }; + + SHA512Engine(): + Poco::Crypto::DigestEngine(Algorithm::SHA512) + { + } +}; + + +template +class HMACAlgorithm: public Algorithm +{ +public: + Poco::DigestEngine::Digest sign(const Signer& signer, const std::string& header, const std::string& payload) + { + Poco::HMACEngine hmac(signer.getHMACKey()); + hmac.update(header); + hmac.update('.'); + hmac.update(payload); + return hmac.digest(); + } + + bool verify(const Signer& signer, const std::string& header, const std::string& payload, const Poco::DigestEngine::Digest& signature) + { + return sign(signer, header, payload) == signature; + } +}; + + +typedef HMACAlgorithm HS256; +typedef HMACAlgorithm HS384; +typedef HMACAlgorithm HS512; + + +class RSAAlgorithm: public Algorithm +{ +public: + RSAAlgorithm(const std::string& digestType): + _digestType(digestType) + { + } + + Poco::DigestEngine::Digest sign(const Signer& signer, const std::string& header, const std::string& payload) + { + if (!signer.getRSAKey()) throw SignatureGenerationException("No RSA key available"); + + Poco::Crypto::RSADigestEngine rsa(*signer.getRSAKey(), _digestType); + rsa.update(header); + rsa.update('.'); + rsa.update(payload); + return rsa.signature(); + + } + + bool verify(const Signer& signer, const std::string& header, const std::string& payload, const Poco::DigestEngine::Digest& signature) + { + if (!signer.getRSAKey()) throw SignatureVerificationException("No RSA key available"); + + Poco::Crypto::RSADigestEngine rsa(*signer.getRSAKey(), _digestType); + rsa.update(header); + rsa.update('.'); + rsa.update(payload); + return rsa.verify(signature); + } + +private: + std::string _digestType; +}; + + +class RS256: public RSAAlgorithm +{ +public: + RS256(): + RSAAlgorithm(Algorithm::SHA256) + { + } +}; + + +class RS384: public RSAAlgorithm +{ +public: + RS384(): + RSAAlgorithm(Algorithm::SHA384) + { + } +}; + + +class RS512: public RSAAlgorithm +{ +public: + RS512(): + RSAAlgorithm(Algorithm::SHA512) + { + } +}; + + +class ECDSAAlgorithm: public Algorithm +{ +public: + ECDSAAlgorithm(const std::string& digestType): + _digestType(digestType) + { + } + + Poco::DigestEngine::Digest sign(const Signer& signer, const std::string& header, const std::string& payload) + { + if (!signer.getECKey()) throw SignatureGenerationException("No EC key available"); + + Poco::Crypto::ECDSADigestEngine ecdsa(*signer.getECKey(), _digestType); + ecdsa.update(header); + ecdsa.update('.'); + ecdsa.update(payload); + + Poco::Crypto::ECDSASignature ecdsaSig(ecdsa.signature()); + Poco::DigestEngine::Digest jwtSig(ecdsaSig.rawR()); + Poco::DigestEngine::Digest rawS(ecdsaSig.rawS()); + jwtSig.insert(jwtSig.end(), rawS.begin(), rawS.end()); + return jwtSig; + + } + + bool verify(const Signer& signer, const std::string& header, const std::string& payload, const Poco::DigestEngine::Digest& signature) + { + if (!signer.getECKey()) throw SignatureVerificationException("No EC key available"); + + Poco::DigestEngine::Digest rawR(signature.begin(), signature.begin() + signature.size() / 2); + Poco::DigestEngine::Digest rawS(signature.begin() + signature.size() / 2, signature.end()); + + Poco::Crypto::ECDSASignature ecdsaSig(rawR, rawS); + Poco::DigestEngine::Digest derSig = ecdsaSig.toDER(); + + Poco::Crypto::ECDSADigestEngine ecdsa(*signer.getECKey(), _digestType); + ecdsa.update(header); + ecdsa.update('.'); + ecdsa.update(payload); + return ecdsa.verify(derSig); + } + +private: + std::string _digestType; +}; + + +class ES256: public ECDSAAlgorithm +{ +public: + ES256(): + ECDSAAlgorithm(Algorithm::SHA256) + { + } +}; + + +class ES384: public ECDSAAlgorithm +{ +public: + ES384(): + ECDSAAlgorithm(Algorithm::SHA384) + { + } +}; + + +class ES512: public ECDSAAlgorithm +{ +public: + ES512(): + ECDSAAlgorithm(Algorithm::SHA512) + { + } +}; + + +// +// AlgorithmFactory +// + + +class AlgorithmFactory: public Poco::DynamicFactory +{ +public: + AlgorithmFactory() + { + registerClass(Signer::ALGO_HS256); + registerClass(Signer::ALGO_HS384); + registerClass(Signer::ALGO_HS512); + + registerClass(Signer::ALGO_RS256); + registerClass(Signer::ALGO_RS384); + registerClass(Signer::ALGO_RS512); + + registerClass(Signer::ALGO_ES256); + registerClass(Signer::ALGO_ES384); + registerClass(Signer::ALGO_ES512); + } +}; + + +// +// Signer +// + + +const std::string Signer::ALGO_NONE("none"); +const std::string Signer::ALGO_HS256("HS256"); +const std::string Signer::ALGO_HS384("HS384"); +const std::string Signer::ALGO_HS512("HS512"); +const std::string Signer::ALGO_RS256("RS256"); +const std::string Signer::ALGO_RS384("RS384"); +const std::string Signer::ALGO_RS512("RS512"); +const std::string Signer::ALGO_ES256("ES256"); +const std::string Signer::ALGO_ES384("ES384"); +const std::string Signer::ALGO_ES512("ES512"); + + +Signer::Signer() +{ + _algorithms.insert(ALGO_HS256); +} + + +Signer::Signer(const std::string& hmacKey): + _hmacKey(hmacKey) +{ + _algorithms.insert(ALGO_HS256); +} + + +Signer::Signer(const Poco::SharedPtr& pRSAKey): + _pRSAKey(pRSAKey) +{ + _algorithms.insert(ALGO_HS256); +} + + +Signer::Signer(const Poco::SharedPtr& pECKey): + _pECKey(pECKey) +{ + _algorithms.insert(ALGO_HS256); +} + + +Signer::~Signer() +{ +} + + +Signer& Signer::setAlgorithms(const std::set& algorithms) +{ + _algorithms = algorithms; + return *this; +} + + +Signer& Signer::addAlgorithm(const std::string& algorithm) +{ + _algorithms.insert(algorithm); + return *this; +} + + +Signer& Signer::addAllAlgorithms() +{ + _algorithms.insert(ALGO_HS256); + _algorithms.insert(ALGO_HS384); + _algorithms.insert(ALGO_HS512); + + _algorithms.insert(ALGO_RS256); + _algorithms.insert(ALGO_RS384); + _algorithms.insert(ALGO_RS512); + + _algorithms.insert(ALGO_ES256); + _algorithms.insert(ALGO_ES384); + _algorithms.insert(ALGO_ES512); + + return *this; +} + + +Signer& Signer::setHMACKey(const std::string& key) +{ + _hmacKey = key; + return *this; +} + + +Signer& Signer::setRSAKey(const Poco::SharedPtr& pKey) +{ + _pRSAKey = pKey; + return *this; +} + + +Signer& Signer::setECKey(const Poco::SharedPtr& pKey) +{ + _pECKey = pKey; + return *this; +} + + +std::string Signer::sign(Token& token, const std::string& algorithm) const +{ + AlgorithmFactory factory; + if (!factory.isClass(algorithm)) throw UnsupportedAlgorithmException(algorithm); + + token.setAlgorithm(algorithm); + + std::string header = Serializer::serialize(token.header()); + std::string payload = Serializer::serialize(token.payload()); + + Algorithm::Ptr pAlgorithm = factory.createInstance(algorithm); + Poco::DigestEngine::Digest digest = pAlgorithm->sign(*this, header, payload); + + std::string signature = encode(digest); + token.sign(signature); + std::string jwt = header; + jwt += '.'; + jwt += payload; + jwt += '.'; + jwt += signature; + return jwt; +} + + +Token Signer::verify(const std::string& jwt) const +{ + Token token; + if (tryVerify(jwt, token)) + return token; + else + throw SignatureVerificationException(); +} + + +bool Signer::tryVerify(const std::string& jwt, Token& token) const +{ + std::vector parts = Serializer::split(jwt); + if (parts.size() < 3) throw ParseException("Not a valid JWT", jwt); + + token.assign(parts[0], parts[1], parts[2]); + std::string algorithm = token.getAlgorithm(); + + if (_algorithms.find(algorithm) == _algorithms.end()) throw UnallowedAlgorithmException(algorithm); + + AlgorithmFactory factory; + if (!factory.isClass(algorithm)) throw UnsupportedAlgorithmException(algorithm); + + Algorithm::Ptr pAlgorithm = factory.createInstance(algorithm); + + return pAlgorithm->verify(*this, parts[0], parts[1], decode(parts[2])); +} + + +std::string Signer::encode(const Poco::DigestEngine::Digest& digest) +{ + std::ostringstream stream; + Poco::Base64Encoder encoder(stream, Poco::BASE64_URL_ENCODING | Poco::BASE64_NO_PADDING); + encoder.write(reinterpret_cast(&digest[0]), digest.size()); + encoder.close(); + return stream.str(); +} + + +Poco::DigestEngine::Digest Signer::decode(const std::string& signature) +{ + Poco::DigestEngine::Digest digest; + digest.reserve(64); + Poco::MemoryInputStream stream(signature.data(), signature.size()); + Poco::Base64Decoder decoder(stream, Poco::BASE64_URL_ENCODING | Poco::BASE64_NO_PADDING); + int ch = decoder.get(); + while (ch != -1) + { + digest.push_back(static_cast(static_cast(ch))); + ch = decoder.get(); + } + return digest; +} + + +} } // namespace Poco::JWT diff --git a/JWT/src/Token.cpp b/JWT/src/Token.cpp new file mode 100644 index 000000000..58c40a688 --- /dev/null +++ b/JWT/src/Token.cpp @@ -0,0 +1,180 @@ +// +// Token.cpp +// +// Library: JWT +// Package: JWT +// Module: Token +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "Poco/JWT/Token.h" +#include "Poco/JWT/Serializer.h" +#include "Poco/JWT/JWTException.h" + + +namespace Poco { +namespace JWT { + + +const std::string Token::CLAIM_ISSUER("iss"); +const std::string Token::CLAIM_SUBJECT("sub"); +const std::string Token::CLAIM_AUDIENCE("aud"); +const std::string Token::CLAIM_EXPIRATION("exp"); +const std::string Token::CLAIM_NOT_BEFORE("nbf"); +const std::string Token::CLAIM_ISSUED_AT("iat"); +const std::string Token::CLAIM_JWT_ID("jti"); +const std::string Token::CLAIM_TYPE("typ"); +const std::string Token::CLAIM_ALGORITHM("alg"); +const std::string Token::CLAIM_CONTENT_TYPE("cty"); +const std::string Token::EMPTY; + + +Token::Token(): + _pHeader(new Poco::JSON::Object), + _pPayload(new Poco::JSON::Object) +{ +} + + +Token::Token(const std::string& token) +{ + std::vector parts = Serializer::split(token); + if (parts.size() < 3) throw ParseException("Not a valid JWT", token); + _pHeader = Serializer::deserialize(parts[0]); + _pPayload = Serializer::deserialize(parts[1]); + _signature = parts[2]; +} + + +Token::Token(const Token& token): + _pHeader(new Poco::JSON::Object(*token._pHeader)), + _pPayload(new Poco::JSON::Object(*token._pPayload)), + _signature(token._signature) +{ +} + + +Token::Token(const std::string& header, const std::string& payload, const std::string& signature) +{ + assign(header, payload, signature); +} + + +Token::~Token() +{ +} + + +Token& Token::operator = (const Token& token) +{ + if (&token != this) + { + Poco::JSON::Object::Ptr pHeader = new Poco::JSON::Object(*token._pHeader); + Poco::JSON::Object::Ptr pPayload = new Poco::JSON::Object(*token._pPayload); + std::string signature = token._signature; + + std::swap(_pHeader, pHeader); + std::swap(_pPayload, pPayload); + std::swap(_signature, signature); + } + return *this; +} + + +Token& Token::operator = (const std::string& token) +{ + std::vector parts = Serializer::split(token); + if (parts.size() < 3) throw ParseException("Not a valid JWT", token); + assign(parts[0], parts[1], parts[2]); + + return *this; +} + + +void Token::assign(const std::string& header, const std::string& payload, const std::string& signature) +{ + Poco::JSON::Object::Ptr pHeader = Serializer::deserialize(header); + Poco::JSON::Object::Ptr pPayload = Serializer::deserialize(payload); + std::string aSignature = signature; + + std::swap(_pHeader, pHeader); + std::swap(_pPayload, pPayload); + std::swap(_signature, aSignature); +} + + +std::string Token::toString() const +{ + std::ostringstream stream; + Serializer::serialize(*_pHeader, stream); + stream << '.'; + Serializer::serialize(*_pPayload, stream); + stream << '.'; + stream << _signature; + return stream.str(); +} + + +void Token::setAudience(const std::vector& audience) +{ + Poco::JSON::Array::Ptr pArray = new Poco::JSON::Array; + for (std::vector::const_iterator it = audience.begin(); it != audience.end(); ++it) + { + pArray->add(*it); + } + _pPayload->set(CLAIM_AUDIENCE, pArray); +} + + +std::vector Token::getAudience() const +{ + std::vector result; + if (_pPayload->has(CLAIM_AUDIENCE)) + { + if (_pPayload->isArray(CLAIM_AUDIENCE)) + { + Poco::JSON::Array::Ptr pArray = _pPayload->getArray(CLAIM_AUDIENCE); + if (pArray) + { + for (unsigned i = 0; i < pArray->size(); i++) + { + result.push_back(pArray->getElement(i)); + } + } + } + else + { + result.push_back(_pPayload->getValue(CLAIM_AUDIENCE)); + } + } + return result; +} + + +void Token::setTimestamp(const std::string& claim, const Poco::Timestamp& ts) +{ + double epochSeconds = static_cast(ts.epochMicroseconds())/Poco::Timestamp::resolution(); + _pPayload->set(claim, epochSeconds); +} + + +Poco::Timestamp Token::getTimestamp(const std::string& claim) const +{ + double epochSeconds = _pPayload->optValue(claim, 0.0); + Poco::Timestamp::TimeVal tv = epochSeconds*Poco::Timestamp::resolution(); + return Poco::Timestamp(tv); +} + + +void Token::sign(const std::string& signature) +{ + _signature = signature; +} + + +} } // namespace Poco::JWT diff --git a/JWT/testsuite/CMakeLists.txt b/JWT/testsuite/CMakeLists.txt new file mode 100644 index 000000000..b671d8318 --- /dev/null +++ b/JWT/testsuite/CMakeLists.txt @@ -0,0 +1,27 @@ +# Sources +file(GLOB SRCS_G "src/*.cpp") +POCO_SOURCES_AUTO( TEST_SRCS ${SRCS_G}) + +# Headers +file(GLOB_RECURSE HDRS_G "src/*.h" ) +POCO_HEADERS_AUTO( TEST_SRCS ${HDRS_G}) + +POCO_SOURCES_AUTO_PLAT( TEST_SRCS OFF + src/WinDriver.cpp +) + +POCO_SOURCES_AUTO_PLAT( TEST_SRCS WINCE + src/WinCEDriver.cpp +) + +add_executable(JWT-testrunner ${TEST_SRCS} ) +if(ANDROID) + add_test(NAME JWT WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -DANDROID_NDK=${ANDROID_NDK} -DLIBRARY_DIR=${CMAKE_BINARY_DIR}/lib -DUNITTEST=${CMAKE_BINARY_DIR}/bin/JWT-testrunner -DTEST_PARAMETER=-all -P ${CMAKE_SOURCE_DIR}/cmake/ExecuteOnAndroid.cmake) +else() + add_test(NAME JWT WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND JWT-testrunner -all) +endif() +target_link_libraries(JWT-testrunner PUBLIC Poco::JWT Poco::Crypto CppUnit) +if(UNIX AND NOT ANDROID) + target_link_libraries(JWT-testrunner PUBLIC pthread) +endif(UNIX AND NOT ANDROID) diff --git a/JWT/testsuite/Makefile b/JWT/testsuite/Makefile new file mode 100644 index 000000000..d2a55da6e --- /dev/null +++ b/JWT/testsuite/Makefile @@ -0,0 +1,19 @@ +# +# Makefile +# +# Makefile for Poco JWT testsuite +# + +include $(POCO_BASE)/build/rules/global + +objects = SerializerTest TokenTest SignerTest JWTTestSuite Driver + +target = testrunner +target_version = 1 +target_libs = PocoJWT PocoJSON PocoCrypto PocoFoundation CppUnit + +include $(POCO_BASE)/build/rules/exec + +ifdef POCO_UNBUNDLED + SYSLIBS += -lpcre +endif diff --git a/JWT/testsuite/src/Driver.cpp b/JWT/testsuite/src/Driver.cpp new file mode 100644 index 000000000..db4335bfd --- /dev/null +++ b/JWT/testsuite/src/Driver.cpp @@ -0,0 +1,17 @@ +// +// Driver.cpp +// +// Console-based test driver for Poco JWT. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "CppUnit/TestRunner.h" +#include "JWTTestSuite.h" + + +CppUnitMain(JWTTestSuite) diff --git a/JWT/testsuite/src/JWTTestSuite.cpp b/JWT/testsuite/src/JWTTestSuite.cpp new file mode 100644 index 000000000..d890b47f7 --- /dev/null +++ b/JWT/testsuite/src/JWTTestSuite.cpp @@ -0,0 +1,26 @@ +// +// JWTTestSuite.cpp +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "JWTTestSuite.h" +#include "SerializerTest.h" +#include "TokenTest.h" +#include "SignerTest.h" + + +CppUnit::Test* JWTTestSuite::suite() +{ + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("JWTTestSuite"); + + pSuite->addTest(SerializerTest::suite()); + pSuite->addTest(TokenTest::suite()); + pSuite->addTest(SignerTest::suite()); + + return pSuite; +} diff --git a/JWT/testsuite/src/JWTTestSuite.h b/JWT/testsuite/src/JWTTestSuite.h new file mode 100644 index 000000000..c1e32d8a1 --- /dev/null +++ b/JWT/testsuite/src/JWTTestSuite.h @@ -0,0 +1,27 @@ +// +// JWTTestSuite.h +// +// Definition of the JWTTestSuite class. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef JWTTestSuite_INCLUDED +#define JWTTestSuite_INCLUDED + + +#include "CppUnit/TestSuite.h" + + +class JWTTestSuite +{ +public: + static CppUnit::Test* suite(); +}; + + +#endif // JWTTestSuite_INCLUDED diff --git a/JWT/testsuite/src/SerializerTest.cpp b/JWT/testsuite/src/SerializerTest.cpp new file mode 100644 index 000000000..5c5769f56 --- /dev/null +++ b/JWT/testsuite/src/SerializerTest.cpp @@ -0,0 +1,131 @@ +// +// SerializerTest.cpp +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "SerializerTest.h" +#include "CppUnit/TestCaller.h" +#include "CppUnit/TestSuite.h" +#include "Poco/JWT/Serializer.h" + + +using namespace Poco::JWT; + + +SerializerTest::SerializerTest(const std::string& name): + CppUnit::TestCase("SerializerTest") +{ +} + + +SerializerTest::~SerializerTest() +{ +} + + +void SerializerTest::setUp() +{ +} + + +void SerializerTest::tearDown() +{ +} + + +void SerializerTest::testSerializeEmpty() +{ + Poco::JSON::Object::Ptr pObject = new Poco::JSON::Object; + std::string str = Serializer::serialize(*pObject); + assert (str == "e30"); +} + + +void SerializerTest::testSerializeAlgNone() +{ + Poco::JSON::Object::Ptr pObject = new Poco::JSON::Object; + pObject->set("alg", std::string("none")); + std::string str = Serializer::serialize(*pObject); + assert (str == "eyJhbGciOiJub25lIn0"); +} + + +void SerializerTest::testSerializeAlgHS256() +{ + Poco::JSON::Object::Ptr pObject = new Poco::JSON::Object; + pObject->set("alg", std::string("HS256")); + pObject->set("typ", std::string("JWT")); + std::string str = Serializer::serialize(*pObject); + assert (str == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"); +} + + +void SerializerTest::testDeserializeEmpty() +{ + std::string serialized("e30"); + Poco::JSON::Object::Ptr pObject = Serializer::deserialize(serialized); + assert (pObject->size() == 0); +} + + +void SerializerTest::testDeserializeAlgNone() +{ + std::string serialized("eyJhbGciOiJub25lIn0"); + Poco::JSON::Object::Ptr pObject = Serializer::deserialize(serialized); + assert (pObject->size() == 1); + assert (pObject->getValue("alg") == "none"); +} + + +void SerializerTest::testDeserializeAlgHS256() +{ + std::string serialized("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"); + Poco::JSON::Object::Ptr pObject = Serializer::deserialize(serialized); + assert (pObject->size() == 2); + assert (pObject->getValue("alg") == "HS256"); + assert (pObject->getValue("typ") == "JWT"); +} + + +void SerializerTest::testSplit() +{ + std::string jwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); + std::vector parts = Serializer::split(jwt); + assert (parts.size() == 3); + assert (parts[0] == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"); + assert (parts[1] == "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"); + assert (parts[2] == "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); +} + + +void SerializerTest::testSplitEmptySig() +{ + std::string jwt("eyJhbGciOiJub25lIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ."); + std::vector parts = Serializer::split(jwt); + assert (parts.size() == 3); + assert (parts[0] == "eyJhbGciOiJub25lIn0"); + assert (parts[1] == "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"); + assert (parts[2] == ""); +} + + +CppUnit::Test* SerializerTest::suite() +{ + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SerializerTest"); + + CppUnit_addTest(pSuite, SerializerTest, testSerializeEmpty); + CppUnit_addTest(pSuite, SerializerTest, testSerializeAlgNone); + CppUnit_addTest(pSuite, SerializerTest, testSerializeAlgHS256); + CppUnit_addTest(pSuite, SerializerTest, testDeserializeEmpty); + CppUnit_addTest(pSuite, SerializerTest, testDeserializeAlgNone); + CppUnit_addTest(pSuite, SerializerTest, testDeserializeAlgHS256); + CppUnit_addTest(pSuite, SerializerTest, testSplit); + CppUnit_addTest(pSuite, SerializerTest, testSplitEmptySig); + + return pSuite; +} diff --git a/JWT/testsuite/src/SerializerTest.h b/JWT/testsuite/src/SerializerTest.h new file mode 100644 index 000000000..16701e23d --- /dev/null +++ b/JWT/testsuite/src/SerializerTest.h @@ -0,0 +1,43 @@ +// +// SerializerTest.h +// +// Definition of the SerializerTest class. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef SerializerTest_INCLUDED +#define SerializerTest_INCLUDED + + +#include "Poco/JWT/JWT.h" +#include "CppUnit/TestCase.h" + + +class SerializerTest: public CppUnit::TestCase +{ +public: + SerializerTest(const std::string& name); + ~SerializerTest(); + + void setUp(); + void tearDown(); + + void testSerializeEmpty(); + void testSerializeAlgNone(); + void testSerializeAlgHS256(); + void testDeserializeEmpty(); + void testDeserializeAlgNone(); + void testDeserializeAlgHS256(); + void testSplit(); + void testSplitEmptySig(); + + static CppUnit::Test* suite(); +}; + + +#endif // SerializerTest_INCLUDED diff --git a/JWT/testsuite/src/SignerTest.cpp b/JWT/testsuite/src/SignerTest.cpp new file mode 100644 index 000000000..d8bac3148 --- /dev/null +++ b/JWT/testsuite/src/SignerTest.cpp @@ -0,0 +1,466 @@ +// +// SignerTest.cpp +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "SignerTest.h" +#include "CppUnit/TestCaller.h" +#include "CppUnit/TestSuite.h" +#include "Poco/JWT/Signer.h" +#include "Poco/JWT/JWTException.h" + + +using namespace Poco::JWT; + + +const std::string SignerTest::RSA_PRIVATE_KEY( + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpQIBAAKCAQEAyVsWBblTS6iuhZiScYwX3Q8O0q2rvbC9cw9fO9NE3XMjzSGf\n" + "NMMFqRRgnivduUvpXwQxw711Va6Y/7o8xrRJ+LBflKBN7EEMdBbNyj7HvZrrkonW\n" + "X/mO1uFo9Tf4Lt0hfomE1tqcXw5IGi4X1wXzvbUvDhFqmFcL3F7alIeSPPoZe5Kq\n" + "4PFoVxhH8z+2u4Q3qME2NbYvJqYaaO+cCxX1JtdY230LL/rHXtH+EbhJpHJNGdLz\n" + "zCJMdKZF3Aqz4J5lb1z6FYIekzJ20FvJ6it1tCQplQisEJyo2YNYCgzBUeAcJoXw\n" + "o4OqsIgfpLWEoTBxm5WbarIwt6yVLRamHGXsZQIDAQABAoIBAQCF5F65gamExu7+\n" + "AR1oCcsYgNnPeBKaeXsQlqkyA+NoChdFYVUXZZNPAjDZHLw8aeEOAKEC0unCBu7Q\n" + "JcwiKWKCzu1PzGTkc8DPNRa0pJh3WRvKfoOhoDW+Z3c+kHAk4YBTPyipIgcXIk0J\n" + "s5rArcGNZXybszMZh9BoQl7Ao4G3rzssIAn4lKhoHPkm4YhJqhboPb0esjTtQXZU\n" + "QIbME3Psir2BgtL+dAW15DofmmvSwVFlsW9tM2QMrS7RtEDoDbEgdteAYGJe+BKg\n" + "6+wH9fGR7Csacqfg5YWBYBC6zwMtOxYIYLuSA3MeWH529M7V26hog/cntl4CA8C4\n" + "Q/vdfjrhAoGBAPzoP7rjkZkwiE49IHLjth7wMEpJJzb9O33jAtRRqz46gXRMO7y2\n" + "lADzHkHmMaJThxtVSQleNHWHW/QZosEaVqw8ibfIeUNVnwLZBL02Rp0XyVnLp2zq\n" + "bG8kQM5CQO9yrckQ4U7hE6Nb9goXR1DzojdllXjS0G2LuGNeZAjL3Pc5AoGBAMvR\n" + "c0UAESCwQX93WyvJRQUsx/3qLWvm84o847W3Ouik4CPxWX6K77lHu8trwl0+sI4r\n" + "+1l4IMli1GBYnp+h64kcIHRcdh8cdB2Q0SSovpbIfBQDmLLxH2eZcPsCkimrRvWw\n" + "1wdPw3e56vn/uhOil1BBqjTdUel4cJkaQX8bDdKNAoGBALxjfGWIcsJ7xm0RV4R9\n" + "XwI4xJ/xUgbCJ3iki00A8OBP0HKC/tSZ5DG458cK49oZAkE4DEmwJL+Pbs2r8vKI\n" + "3hs6lROTf9DKjMIgSklvjrYiK9h5vWOCU/eON7/s1lYHRLDLpCmPu4MqU2I86ODC\n" + "Owms3+S6lIulUlqiyz1KLnTxAoGBAKYrOjY8Krqi2JOKhNs6+bmRW+/o12VMYqwG\n" + "noPNQgrgORk0sQkZTv6YvEPJtCn/bURMGV1FMj3eBFYUiiaNhZFATDlyFJ+ivAxK\n" + "wCiKJvCZvYc3s2vYaAk3sUQEQZQVXwH6TiVY430eJ7PwyQ0vFvxIvPLiqeCV3/8C\n" + "x/lP3kIVAoGAZbiwceB12RTDCL2jsczN4ZE16VyXvatI44NWtakbuxZoDJGhg5Uu\n" + "N2xb6w2mkmaJ0FUi2+B7Vpv8vmH98Aylfev03J4qnJgaNVVEK2nlprkOyflHr7Bj\n" + "fIG0bvyvwUNUy8f5idTqcucDi02zu3Sfr2+LocNUJgrbvswMIXUE3v8=\n" + "-----END RSA PRIVATE KEY-----\n" +); + + +const std::string SignerTest::RSA_PUBLIC_KEY( + "-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyVsWBblTS6iuhZiScYwX\n" + "3Q8O0q2rvbC9cw9fO9NE3XMjzSGfNMMFqRRgnivduUvpXwQxw711Va6Y/7o8xrRJ\n" + "+LBflKBN7EEMdBbNyj7HvZrrkonWX/mO1uFo9Tf4Lt0hfomE1tqcXw5IGi4X1wXz\n" + "vbUvDhFqmFcL3F7alIeSPPoZe5Kq4PFoVxhH8z+2u4Q3qME2NbYvJqYaaO+cCxX1\n" + "JtdY230LL/rHXtH+EbhJpHJNGdLzzCJMdKZF3Aqz4J5lb1z6FYIekzJ20FvJ6it1\n" + "tCQplQisEJyo2YNYCgzBUeAcJoXwo4OqsIgfpLWEoTBxm5WbarIwt6yVLRamHGXs\n" + "ZQIDAQAB\n" + "-----END PUBLIC KEY-----\n" +); + + +const std::string SignerTest::ECDSA_PRIVATE_KEY( + "-----BEGIN PRIVATE KEY-----\n" + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2\n" + "OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r\n" + "1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G\n" + "-----END PRIVATE KEY-----\n" +); + + +const std::string SignerTest::ECDSA_PUBLIC_KEY( + "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9\n" + "q9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n" + "-----END PUBLIC KEY-----\n" +); + + +SignerTest::SignerTest(const std::string& name): + CppUnit::TestCase("SignerTest") +{ +} + + +SignerTest::~SignerTest() +{ +} + + +void SignerTest::setUp() +{ +} + + +void SignerTest::tearDown() +{ +} + + +void SignerTest::testSignHS256() +{ + Token token; + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + Signer signer("0123456789ABCDEF0123456789ABCDEF"); + std::string jwt = signer.sign(token, Signer::ALGO_HS256); + + assert (jwt == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.qn9G7NwFEOjIh-7hfCUDZA1aJeQmf7I7YvzCBcdenGw"); +} + + +void SignerTest::testSignHS384() +{ + Token token; + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + Signer signer("0123456789ABCDEF0123456789ABCDEF"); + std::string jwt = signer.sign(token, Signer::ALGO_HS384); + + assert (jwt == "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.9NsI7ahPhCd3itTewXb0GNZi08fuUHXLx0qeBscteMXJiug1PyQ_teA9v7zLgg1W"); +} + + +void SignerTest::testSignHS512() +{ + Token token; + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + Signer signer("0123456789ABCDEF0123456789ABCDEF"); + std::string jwt = signer.sign(token, Signer::ALGO_HS512); + + assert (jwt == "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.WG4y8U_bDN4T3Vu3L5Q5C4pqssrH4wqBtdrFLVuS8k-BLycCq8_bjYGgo7BCzVt4DFXs3BFUIJQdWBzuJwXHtg"); +} + + +void SignerTest::testVerifyHS256() +{ + std::string jwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.qn9G7NwFEOjIh-7hfCUDZA1aJeQmf7I7YvzCBcdenGw"); + + Signer signer("0123456789ABCDEF0123456789ABCDEF"); + try + { + Token token = signer.verify(jwt); + assert (token.getAlgorithm() == "HS256"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "qn9G7NwFEOjIh-7hfCUDZA1aJeQmf7I7YvzCBcdenGw"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testVerifyHS384() +{ + std::string jwt("eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.9NsI7ahPhCd3itTewXb0GNZi08fuUHXLx0qeBscteMXJiug1PyQ_teA9v7zLgg1W"); + + Signer signer("0123456789ABCDEF0123456789ABCDEF"); + signer.addAlgorithm(Signer::ALGO_HS384); + try + { + Token token = signer.verify(jwt); + assert (token.getAlgorithm() == "HS384"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "9NsI7ahPhCd3itTewXb0GNZi08fuUHXLx0qeBscteMXJiug1PyQ_teA9v7zLgg1W"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testVerifyHS512() +{ + std::string jwt("eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.WG4y8U_bDN4T3Vu3L5Q5C4pqssrH4wqBtdrFLVuS8k-BLycCq8_bjYGgo7BCzVt4DFXs3BFUIJQdWBzuJwXHtg"); + + Signer signer("0123456789ABCDEF0123456789ABCDEF"); + signer.addAlgorithm(Signer::ALGO_HS512); + try + { + Token token = signer.verify(jwt); + assert (token.getAlgorithm() == "HS512"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "WG4y8U_bDN4T3Vu3L5Q5C4pqssrH4wqBtdrFLVuS8k-BLycCq8_bjYGgo7BCzVt4DFXs3BFUIJQdWBzuJwXHtg"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testVerifyFailSignature() +{ + std::string jwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.wn9G7NwFEOjIh-7hfCFDZA1aJeQmf7I7YvzCBcdenGw"); + + Signer signer("0123456789ABCDEF0123456789ABCDEF"); + try + { + Token token = signer.verify(jwt); + fail("Verification must fail"); + } + catch (SignatureVerificationException&) + { + } +} + + +void SignerTest::testVerifyFailKey() +{ + std::string jwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.qn9G7NwFEOjIh-7hfCUDZA1aJeQmf7I7YvzCBcdenGw"); + + Signer signer("0123456789ABCDEF0123456789ABFAIL"); + try + { + Token token = signer.verify(jwt); + fail("Verification must fail"); + } + catch (SignatureVerificationException&) + { + } +} + + +void SignerTest::testSignRS256() +{ + Token token; + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + std::istringstream privateKeyStream(RSA_PRIVATE_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::RSAKey(0, &privateKeyStream); + + Signer signer(pKey); + std::string jwt = signer.sign(token, Signer::ALGO_RS256); + + assert (jwt == "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.a27BSSEBTaQZFA1tVX4IZHgyG5HIXcJVZpbpB5LQ_rPTalJjvhDDuWC1dM0G0tUACrzPtUN4BhSd-dygJsX4b35DnWm_gPUNDI3HMm7Ck52mM_2Y6445B6aa_pPPuFk6AWql8WWLzQqo9kjQh8AmbMw2A9bciA1smEEsHVw4-VX1tEtupbhJsXO2FnwkQNhJF_Pp4nuX282UV_4DtZ9LW3jLoEYFytKrM4fhkNKVMY52Cn0DJA89fQYe7098gduCjzqoGtaoKKDngbADn2h_1P8VLZrZEd4UROEHviVLm_qxHrWY8-tB0L7i_JMXxw1qMKAavWA-WbnNDdXpOn_o2Q"); +} + + +void SignerTest::testSignRS384() +{ + Token token; + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + std::istringstream privateKeyStream(RSA_PRIVATE_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::RSAKey(0, &privateKeyStream); + + Signer signer(pKey); + std::string jwt = signer.sign(token, Signer::ALGO_RS384); + + assert (jwt == "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.L-34N4v5kLa94Llz-XakGIwL9M00ERciAzZSqxgGIJ2dw9VrIodfK-U00wZZwSA2UEZWIm-LJ7wQBiuUw8oMl_fYsufT8W6dWiGZQ2c24AjGKwpXmypPKjh5yRnylkK-8ZRC1AJuZDsY8DJE7vse1w2eAE_Jw0XRJ-u_lq9Hgxz58ZonV1YzUdyVPtD3gWdhyjnlzPCH7lQM4copVUFN6mFTZzt4WQ2i1O1qW1cD_F4Jul9_5z5BYe7-bK3DoV79AgfbEewdnc4yatLQWMIAkrc2LM_tFe83ABhFYhM0qIH8nOuk3WKyKwtjh15f3h3Fn-JnriSfcC79v-M5UpEsZg"); +} + + +void SignerTest::testSignRS512() +{ + Token token; + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + std::istringstream privateKeyStream(RSA_PRIVATE_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::RSAKey(0, &privateKeyStream); + + Signer signer(pKey); + std::string jwt = signer.sign(token, Signer::ALGO_RS512); + + assert (jwt == "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.XQTBYo2zqxcyUGs0H-74tfBY6l8PxBQK7-IAJ1NgEVIeoMDX3zQJu5BQX2_VhjOESOPqGNN-FtiNLD1G-LCvSV1fxJwIVEilT7CTBs5iNii6Jrpha5YPnzETqBiz1zdnyNh_QVbtdRIv2ORlp_OIYNZJrxiRfOGvm2_Z3htDoqgv_Lm8SZqelOntox96GrV6GaXhpKBbLjBSU-XPkSOcm5VuXDCz8tltJ_d5cKxbFDUtS6FBYNMaLEqIL4-_aJU_Ld5TcPQT7MqWlHHZZufA5zzmfKEEgddco6uzCBLOz3B6E4Z5VZDoweCM5R7hnLiZOlK0kYsFoaDCVcK_TZhDNw"); +} + + +void SignerTest::testVerifyRS256() +{ + std::string jwt("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.a27BSSEBTaQZFA1tVX4IZHgyG5HIXcJVZpbpB5LQ_rPTalJjvhDDuWC1dM0G0tUACrzPtUN4BhSd-dygJsX4b35DnWm_gPUNDI3HMm7Ck52mM_2Y6445B6aa_pPPuFk6AWql8WWLzQqo9kjQh8AmbMw2A9bciA1smEEsHVw4-VX1tEtupbhJsXO2FnwkQNhJF_Pp4nuX282UV_4DtZ9LW3jLoEYFytKrM4fhkNKVMY52Cn0DJA89fQYe7098gduCjzqoGtaoKKDngbADn2h_1P8VLZrZEd4UROEHviVLm_qxHrWY8-tB0L7i_JMXxw1qMKAavWA-WbnNDdXpOn_o2Q"); + + std::istringstream publicKeyStream(RSA_PUBLIC_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::RSAKey(&publicKeyStream); + + Signer signer(pKey); + signer.addAlgorithm(Signer::ALGO_RS256); + try + { + Token token = signer.verify(jwt); + assert (token.getAlgorithm() == "RS256"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "a27BSSEBTaQZFA1tVX4IZHgyG5HIXcJVZpbpB5LQ_rPTalJjvhDDuWC1dM0G0tUACrzPtUN4BhSd-dygJsX4b35DnWm_gPUNDI3HMm7Ck52mM_2Y6445B6aa_pPPuFk6AWql8WWLzQqo9kjQh8AmbMw2A9bciA1smEEsHVw4-VX1tEtupbhJsXO2FnwkQNhJF_Pp4nuX282UV_4DtZ9LW3jLoEYFytKrM4fhkNKVMY52Cn0DJA89fQYe7098gduCjzqoGtaoKKDngbADn2h_1P8VLZrZEd4UROEHviVLm_qxHrWY8-tB0L7i_JMXxw1qMKAavWA-WbnNDdXpOn_o2Q"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testVerifyRS384() +{ + std::string jwt("eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.L-34N4v5kLa94Llz-XakGIwL9M00ERciAzZSqxgGIJ2dw9VrIodfK-U00wZZwSA2UEZWIm-LJ7wQBiuUw8oMl_fYsufT8W6dWiGZQ2c24AjGKwpXmypPKjh5yRnylkK-8ZRC1AJuZDsY8DJE7vse1w2eAE_Jw0XRJ-u_lq9Hgxz58ZonV1YzUdyVPtD3gWdhyjnlzPCH7lQM4copVUFN6mFTZzt4WQ2i1O1qW1cD_F4Jul9_5z5BYe7-bK3DoV79AgfbEewdnc4yatLQWMIAkrc2LM_tFe83ABhFYhM0qIH8nOuk3WKyKwtjh15f3h3Fn-JnriSfcC79v-M5UpEsZg"); + + std::istringstream publicKeyStream(RSA_PUBLIC_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::RSAKey(&publicKeyStream); + + Signer signer(pKey); + signer.addAlgorithm(Signer::ALGO_RS384); + try + { + Token token = signer.verify(jwt); + assert (token.getAlgorithm() == "RS384"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "L-34N4v5kLa94Llz-XakGIwL9M00ERciAzZSqxgGIJ2dw9VrIodfK-U00wZZwSA2UEZWIm-LJ7wQBiuUw8oMl_fYsufT8W6dWiGZQ2c24AjGKwpXmypPKjh5yRnylkK-8ZRC1AJuZDsY8DJE7vse1w2eAE_Jw0XRJ-u_lq9Hgxz58ZonV1YzUdyVPtD3gWdhyjnlzPCH7lQM4copVUFN6mFTZzt4WQ2i1O1qW1cD_F4Jul9_5z5BYe7-bK3DoV79AgfbEewdnc4yatLQWMIAkrc2LM_tFe83ABhFYhM0qIH8nOuk3WKyKwtjh15f3h3Fn-JnriSfcC79v-M5UpEsZg"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testVerifyRS512() +{ + std::string jwt("eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.XQTBYo2zqxcyUGs0H-74tfBY6l8PxBQK7-IAJ1NgEVIeoMDX3zQJu5BQX2_VhjOESOPqGNN-FtiNLD1G-LCvSV1fxJwIVEilT7CTBs5iNii6Jrpha5YPnzETqBiz1zdnyNh_QVbtdRIv2ORlp_OIYNZJrxiRfOGvm2_Z3htDoqgv_Lm8SZqelOntox96GrV6GaXhpKBbLjBSU-XPkSOcm5VuXDCz8tltJ_d5cKxbFDUtS6FBYNMaLEqIL4-_aJU_Ld5TcPQT7MqWlHHZZufA5zzmfKEEgddco6uzCBLOz3B6E4Z5VZDoweCM5R7hnLiZOlK0kYsFoaDCVcK_TZhDNw"); + + std::istringstream publicKeyStream(RSA_PUBLIC_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::RSAKey(&publicKeyStream); + + Signer signer(pKey); + signer.addAlgorithm(Signer::ALGO_RS512); + try + { + Token token = signer.verify(jwt); + assert (token.getAlgorithm() == "RS512"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "XQTBYo2zqxcyUGs0H-74tfBY6l8PxBQK7-IAJ1NgEVIeoMDX3zQJu5BQX2_VhjOESOPqGNN-FtiNLD1G-LCvSV1fxJwIVEilT7CTBs5iNii6Jrpha5YPnzETqBiz1zdnyNh_QVbtdRIv2ORlp_OIYNZJrxiRfOGvm2_Z3htDoqgv_Lm8SZqelOntox96GrV6GaXhpKBbLjBSU-XPkSOcm5VuXDCz8tltJ_d5cKxbFDUtS6FBYNMaLEqIL4-_aJU_Ld5TcPQT7MqWlHHZZufA5zzmfKEEgddco6uzCBLOz3B6E4Z5VZDoweCM5R7hnLiZOlK0kYsFoaDCVcK_TZhDNw"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testSignVerifyES256() +{ + // Note: ECDSA is a strange beast and does not return a "known" signature. + // That's why we do the signing and verification in a single test. + + Token token; + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + std::istringstream privateKeyStream(ECDSA_PRIVATE_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::ECKey(0, &privateKeyStream); + + Signer signer(pKey); + std::string jwt = signer.sign(token, Signer::ALGO_ES256); + + std::istringstream publicKeyStream(ECDSA_PUBLIC_KEY); + pKey = new Poco::Crypto::ECKey(&publicKeyStream); + + Signer verifier(pKey); + verifier.addAlgorithm(Signer::ALGO_ES256); + try + { + Token token2 = verifier.verify(jwt); + assert (token2.getAlgorithm() == "ES256"); + assert (token2.getType() == "JWT"); + assert (token2.getSubject() == "1234567890"); + assert (token2.getIssuedAt().epochTime() == 1516239022); + assert (token2.payload().getValue("name") == "John Doe"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testVerifyES256() +{ + std::string jwt("eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.kLfRdCmR-qewMgzhCtqJrXVoagoh7es0yWsn3VunuS51FMBBcxLTKRDfdgHih0os4gvBdLMYkJu61_IQqoIYZw"); + + std::istringstream publicKeyStream(ECDSA_PUBLIC_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::ECKey(&publicKeyStream); + + Signer signer(pKey); + signer.addAlgorithm(Signer::ALGO_ES256); + try + { + Token token = signer.verify(jwt); + assert (token.getAlgorithm() == "ES256"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "kLfRdCmR-qewMgzhCtqJrXVoagoh7es0yWsn3VunuS51FMBBcxLTKRDfdgHih0os4gvBdLMYkJu61_IQqoIYZw"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +CppUnit::Test* SignerTest::suite() +{ + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SignerTest"); + + CppUnit_addTest(pSuite, SignerTest, testSignHS256); + CppUnit_addTest(pSuite, SignerTest, testSignHS384); + CppUnit_addTest(pSuite, SignerTest, testSignHS512); + CppUnit_addTest(pSuite, SignerTest, testVerifyHS256); + CppUnit_addTest(pSuite, SignerTest, testVerifyHS384); + CppUnit_addTest(pSuite, SignerTest, testVerifyHS512); + CppUnit_addTest(pSuite, SignerTest, testVerifyFailSignature); + CppUnit_addTest(pSuite, SignerTest, testVerifyFailKey); + CppUnit_addTest(pSuite, SignerTest, testSignRS256); + CppUnit_addTest(pSuite, SignerTest, testSignRS384); + CppUnit_addTest(pSuite, SignerTest, testSignRS512); + CppUnit_addTest(pSuite, SignerTest, testVerifyRS256); + CppUnit_addTest(pSuite, SignerTest, testVerifyRS384); + CppUnit_addTest(pSuite, SignerTest, testVerifyRS512); + CppUnit_addTest(pSuite, SignerTest, testSignVerifyES256); + CppUnit_addTest(pSuite, SignerTest, testVerifyES256); + + return pSuite; +} diff --git a/JWT/testsuite/src/SignerTest.h b/JWT/testsuite/src/SignerTest.h new file mode 100644 index 000000000..87dfc6d6f --- /dev/null +++ b/JWT/testsuite/src/SignerTest.h @@ -0,0 +1,57 @@ +// +// SignerTest.h +// +// Definition of the SignerTest class. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef SignerTest_INCLUDED +#define SignerTest_INCLUDED + + +#include "Poco/JWT/JWT.h" +#include "CppUnit/TestCase.h" + + +class SignerTest: public CppUnit::TestCase +{ +public: + SignerTest(const std::string& name); + ~SignerTest(); + + void setUp(); + void tearDown(); + + void testSignHS256(); + void testSignHS384(); + void testSignHS512(); + void testVerifyHS256(); + void testVerifyHS384(); + void testVerifyHS512(); + void testVerifyFailSignature(); + void testVerifyFailKey(); + void testSignRS256(); + void testSignRS384(); + void testSignRS512(); + void testVerifyRS256(); + void testVerifyRS384(); + void testVerifyRS512(); + void testSignVerifyES256(); + void testVerifyES256(); + + static CppUnit::Test* suite(); + + static const std::string RSA_PRIVATE_KEY; + static const std::string RSA_PUBLIC_KEY; + + static const std::string ECDSA_PRIVATE_KEY; + static const std::string ECDSA_PUBLIC_KEY; +}; + + +#endif // SignerTest_INCLUDED diff --git a/JWT/testsuite/src/TokenTest.cpp b/JWT/testsuite/src/TokenTest.cpp new file mode 100644 index 000000000..a1da2f73f --- /dev/null +++ b/JWT/testsuite/src/TokenTest.cpp @@ -0,0 +1,127 @@ +// +// TokenTest.cpp +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "TokenTest.h" +#include "CppUnit/TestCaller.h" +#include "CppUnit/TestSuite.h" +#include "Poco/JWT/Token.h" +#include + +using namespace Poco::JWT; + + +TokenTest::TokenTest(const std::string& name): + CppUnit::TestCase("TokenTest") +{ +} + + +TokenTest::~TokenTest() +{ +} + + +void TokenTest::setUp() +{ +} + + +void TokenTest::tearDown() +{ +} + + +void TokenTest::testParse() +{ + std::string jwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); + + Token token(jwt); + assert (token.getAlgorithm() == "HS256"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); +} + + +void TokenTest::testParseNoSig() +{ + std::string jwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ."); + + Token token(jwt); + assert (token.getAlgorithm() == "HS256"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == ""); +} + + +void TokenTest::testSerialize() +{ + Token token; + token.setAlgorithm("HS256"); + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + std::string jwt = token.toString(); + assert (jwt == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ."); +} + + +void TokenTest::testAssign() +{ + std::string jwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); + + Token token(jwt); + Token token2(token); + + assert (token.toString() == token2.toString()); +} + + +void TokenTest::testAudience() +{ + Token token; + + token.setAudience("11111"); + assert (token.payload().getValue(Token::CLAIM_AUDIENCE) == "11111"); + + std::vector audience = token.getAudience(); + assert (audience.size() == 1); + assert (audience[0] == "11111"); + + audience.push_back("22222"); + token.setAudience(audience); + + assert (token.payload().isArray(Token::CLAIM_AUDIENCE)); + audience = token.getAudience(); + assert (audience.size() == 2); + assert (audience[0] == "11111"); + assert (audience[1] == "22222"); +} + + +CppUnit::Test* TokenTest::suite() +{ + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("TokenTest"); + + CppUnit_addTest(pSuite, TokenTest, testParse); + CppUnit_addTest(pSuite, TokenTest, testParseNoSig); + CppUnit_addTest(pSuite, TokenTest, testSerialize); + CppUnit_addTest(pSuite, TokenTest, testAssign); + CppUnit_addTest(pSuite, TokenTest, testAudience); + + return pSuite; +} diff --git a/JWT/testsuite/src/TokenTest.h b/JWT/testsuite/src/TokenTest.h new file mode 100644 index 000000000..e68c042d4 --- /dev/null +++ b/JWT/testsuite/src/TokenTest.h @@ -0,0 +1,40 @@ +// +// TokenTest.h +// +// Definition of the TokenTest class. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef TokenTest_INCLUDED +#define TokenTest_INCLUDED + + +#include "Poco/JWT/JWT.h" +#include "CppUnit/TestCase.h" + + +class TokenTest: public CppUnit::TestCase +{ +public: + TokenTest(const std::string& name); + ~TokenTest(); + + void setUp(); + void tearDown(); + + void testParse(); + void testParseNoSig(); + void testSerialize(); + void testAssign(); + void testAudience(); + + static CppUnit::Test* suite(); +}; + + +#endif // TokenTest_INCLUDED diff --git a/JWT/testsuite/src/WinCEDriver.cpp b/JWT/testsuite/src/WinCEDriver.cpp new file mode 100644 index 000000000..9961f2b8f --- /dev/null +++ b/JWT/testsuite/src/WinCEDriver.cpp @@ -0,0 +1,30 @@ +// +// WinCEDriver.cpp +// +// Console-based test driver for Windows CE. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "CppUnit/TestRunner.h" +#include "JWTTestSuite.h" +#include + + +int wmain(int argc, wchar_t* argv[]) +{ + std::vector args; + for (int i = 0; i < argc; ++i) + { + char buffer[1024]; + std::wcstombs(buffer, argv[i], sizeof(buffer)); + args.push_back(std::string(buffer)); + } + CppUnit::TestRunner runner; + runner.addTest("JWTTestSuite", JWTTestSuite::suite()); + return runner.run(args) ? 0 : 1; +} diff --git a/JWT/testsuite/src/WinDriver.cpp b/JWT/testsuite/src/WinDriver.cpp new file mode 100644 index 000000000..c6430ee74 --- /dev/null +++ b/JWT/testsuite/src/WinDriver.cpp @@ -0,0 +1,28 @@ +// +// WinDriver.cpp +// +// Windows test driver for Poco JWT. +// +// Copyright (c) 2019, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "WinTestRunner/WinTestRunner.h" +#include "JWTTestSuite.h" + + +class TestDriver: public CppUnit::WinTestRunnerApp +{ + void TestMain() + { + CppUnit::WinTestRunner runner; + runner.addTest(JWTTestSuite::suite()); + runner.run(); + } +}; + + +TestDriver theDriver; diff --git a/components b/components index 0bd9c91ab..aba7ecea7 100644 --- a/components +++ b/components @@ -16,6 +16,7 @@ Data/PostgreSQL Zip PageCompiler PageCompiler/File2Page +JWT PDF CppParser MongoDB