Minimal implementation which parses tokens and simulates TLS subject from it.

parent 1ec8662e
......@@ -57,29 +57,6 @@ namespace Arc {
Logger EMIESClient::logger(Logger::rootLogger, "EMI ES Client");
#ifdef USE_SCITOKENS
static std::string create_scitoken(const MCCConfig& cfg, const URL& url) {
bool is_file = cfg.credential.empty();
Arc::Credential cert(is_file ? ((!cfg.proxy.empty())?cfg.proxy:cfg.cert): cfg.credential, "", "", "", "", is_file);
if(cert.GetIdentityName().empty())
return "";
Arc::JWSE scitoken;
std::string certificateStr;
cert.OutputCertificate(certificateStr);
cert.OutputPrivatekey(certificateStr);
cert.OutputCertificateChain(certificateStr);
scitoken.Certificate(certificateStr.c_str());
scitoken.HeaderParameter(Arc::JWSE::HeaderNameIssuer, cert.GetCAName().c_str());
scitoken.HeaderParameter(Arc::JWSE::HeaderNameSubject, cert.GetIdentityName().c_str());
scitoken.HeaderParameter(Arc::JWSE::HeaderNameAudience, url.str().c_str());
std::string scitokenStr;
if(!scitoken.Output(scitokenStr))
return "";
std::cerr<<"SCITOKEN: "<<scitokenStr<<std::endl;
return scitokenStr;
}
#endif
static void set_namespaces(NS& ns) {
ns[ES_TYPES_NPREFIX] = ES_TYPES_NAMESPACE;
ns[ES_CREATE_NPREFIX] = ES_CREATE_NAMESPACE;
......@@ -106,17 +83,23 @@ std::cerr<<"SCITOKEN: "<<scitokenStr<<std::endl;
logger.msg(DEBUG, "Creating an EMI ES client");
#ifdef USE_SCITOKENS
// TEST: removing credentials from HTTPS layer
MCCConfig temp_cfg(cfg);
temp_cfg.proxy.clear();
temp_cfg.cert.clear();
temp_cfg.key.clear();
temp_cfg.credential.clear();
scitoken = Arc::GetEnv("SCITOKEN");
std::cerr<<"SCITOKEN: "<<scitoken<<std::endl;
if(!scitoken.empty()) {
// removing credentials from HTTPS layer
MCCConfig temp_cfg(cfg);
temp_cfg.proxy.clear();
temp_cfg.cert.clear();
temp_cfg.key.clear();
temp_cfg.credential.clear();
client = new ClientSOAP(temp_cfg, url, timeout);
} else {
client = new ClientSOAP(cfg, url, timeout);
}
#else
MCCConfig const& temp_cfg(cfg);
client = new ClientSOAP(cfg, url, timeout);
#endif
client = new ClientSOAP(temp_cfg, url, timeout);
if (!client)
logger.msg(VERBOSE, "Unable to create SOAP client used by EMIESClient.");
set_namespaces(ns);
......@@ -170,13 +153,8 @@ std::cerr<<"SCITOKEN: "<<scitokenStr<<std::endl;
MessageAttributes attrout;
MessageAttributes attrin;
attrout.set("SOAP:ENDPOINT",rurl.str());
#ifdef USE_SCITOKENS
// Turn user credentials into scitoken and add it to request
std::string scitokenStr = create_scitoken(cfg, rurl);
if(!scitokenStr.empty())
attrout.set("HTTP:authorization", "bearer "+scitokenStr);
#endif
if(!scitoken.empty())
attrout.set("HTTP:authorization", "bearer "+scitoken);
if (!deleg->DelegateCredentialsInit(*entry,&attrout,&attrin,&(client->GetContext()),
(renew_id.empty()?DelegationProviderSOAP::EMIDS:DelegationProviderSOAP::EMIDSRENEW))) {
......@@ -225,12 +203,8 @@ std::cerr<<"SCITOKEN: "<<scitokenStr<<std::endl;
PayloadSOAP* resp = NULL;
std::multimap<std::string,std::string> http_attr;
#ifdef USE_SCITOKENS
// Turn user credentials into scitoken and add it to request
std::string scitokenStr = create_scitoken(cfg, rurl);
if(!scitokenStr.empty())
http_attr.insert(std::pair<std::string,std::string>("authorization","bearer "+scitokenStr));
#endif
if(!scitoken.empty())
http_attr.insert(std::pair<std::string,std::string>("authorization","bearer "+scitoken));
if (!client->process(http_attr, &req, &resp)) {
logger.msg(VERBOSE, "%s request failed", req.Child(0).FullName());
......
......@@ -329,6 +329,8 @@ namespace Arc {
const MCCConfig cfg;
std::string scitoken;
int timeout;
std::string lfailure;
......
......@@ -95,9 +95,9 @@ namespace Arc {
private:
T *object;
void (*deleter)(T*);
void operator=(const AutoPointer<T>&);
AutoPointer(const AutoPointer&);
static void DefaultDeleter(T* o) { delete o; }
void operator=(const AutoPointer<T>&);
AutoPointer(AutoPointer<T> const&);
public:
/// NULL pointer constructor
AutoPointer(void (*d)(T*) = &DefaultDeleter)
......@@ -105,13 +105,24 @@ namespace Arc {
/// Constructor which wraps pointer and optionally defines deletion function
AutoPointer(T *o, void (*d)(T*) = &DefaultDeleter)
: object(o), deleter(d) {}
/// Moving constructor
AutoPointer(AutoPointer<T>& o)
: object(o.Release()), deleter(o.deleter) {}
/// Destructor destroys wrapped object using assigned deleter
~AutoPointer(void) {
if (object) if(deleter) (*deleter)(object);
object = NULL;
}
void operator=(T* o) {
AutoPointer<T>& operator=(T* o) {
if (object) if(deleter) (*deleter)(object);
object = o;
return *this;
}
AutoPointer<T>& operator=(AutoPointer<T>& o) {
if (object) if(deleter) (*deleter)(object);
object = o.object;
o.object = NULL;
return *this;
}
/// For referring wrapped object
T& operator*(void) const {
......
......@@ -77,8 +77,8 @@ namespace Arc {
const char *id) {
XMLNode comp = chain["Component"];
for (; (bool)comp; ++comp)
if ((comp.Attribute("name") == name) &&
(comp.Attribute("id") == id))
if ((!name || (comp.Attribute("name") == name)) &&
(!id || (comp.Attribute("id") == id)))
return comp;
return XMLNode();
}
......@@ -397,6 +397,12 @@ namespace Arc {
comp.NewAttribute("entry") = "http";
comp.NewChild("Method") = "POST"; // Override using attributes if needed
comp.NewChild("Endpoint") = url.str(true); // Override using attributes if needed
// Pass information about protocol and hostname to TLS level
XMLNode compTLS = ConfigFindComponent(xmlcfg["Chain"], "tls.client", NULL);
if(compTLS) {
compTLS.NewChild("Hostname") = url.Host();
compTLS.NewChild("Protocol") = "http/1.1"; // educated guess
}
}
ClientHTTP::~ClientHTTP() {}
......
......@@ -114,7 +114,7 @@ namespace Arc {
return tls_entry ? tls_entry : tcp_entry;
}
virtual MCC_Status Load();
void AddSecHandler(XMLNode handlercfg, TCPSec sec, const std::string& libanme = "", const std::string& libpath = "");
void AddSecHandler(XMLNode handlercfg, TCPSec sec, const std::string& libname = "", const std::string& libpath = "");
protected:
MCC *tcp_entry;
MCC *tls_entry;
......@@ -215,7 +215,7 @@ namespace Arc {
MCC* GetEntry() {
return http_entry;
}
void AddSecHandler(XMLNode handlercfg, const std::string& libanme = "", const std::string& libpath = "");
void AddSecHandler(XMLNode handlercfg, const std::string& libname = "", const std::string& libpath = "");
virtual MCC_Status Load();
void RelativeURI(bool val) { relative_uri=val; };
const URL& GetURL() const { return default_url; };
......@@ -260,7 +260,7 @@ namespace Arc {
return soap_entry;
}
/** Adds security handler to configuration of SOAP MCC */
void AddSecHandler(XMLNode handlercfg, const std::string& libanme = "", const std::string& libpath = "");
void AddSecHandler(XMLNode handlercfg, const std::string& libname = "", const std::string& libpath = "");
/** Instantiates pluggable elements according to generated configuration */
virtual MCC_Status Load();
protected:
......
......@@ -7,11 +7,12 @@ pgmpkglibdir = $(pkglibdir)
pgmpkglib_PROGRAMS =
libarcscitokens_ladir = $(pkgincludedir)
libarcscitokens_la_HEADERS = jwse.h
libarcscitokens_la_SOURCES = jwse.cpp jwse_hmac.cpp jwse_ecdsa.cpp jwse_keys.cpp
libarcscitokens_la_CXXFLAGS = -I$(top_srcdir)/include $(OPENSSL_CFLAGS) $(GLIBMM_CFLAGS) $(AM_CXXFLAGS)
libarcscitokens_la_HEADERS = jwse.h openid_metadata.h
libarcscitokens_la_SOURCES = jwse.cpp jwse_hmac.cpp jwse_ecdsa.cpp jwse_rsassapkcs1.cpp jwse_rsassapss.cpp jwse_keys.cpp openid_metadata.cpp jwse_private.h
libarcscitokens_la_CXXFLAGS = -I$(top_srcdir)/include $(OPENSSL_CFLAGS) $(LIBXML2_CFLAGS) $(GLIBMM_CFLAGS) $(AM_CXXFLAGS)
libarcscitokens_la_LIBADD = \
$(top_builddir)/src/external/cJSON/libcjson.la \
$(top_builddir)/src/hed/libs/common/libarccommon.la \
$(top_builddir)/src/hed/libs/communication/libarccommunication.la \
$(OPENSSL_LIBS) $(GLIBMM_LIBS) $(LIBINTL)
libarcscitokens_la_LDFLAGS = -version-info 3:0:0
......@@ -5,6 +5,7 @@
#include <openssl/evp.h>
#include <arc/Base64.h>
#include <arc/Logger.h>
#include <arc/external/cJSON/cJSON.h>
#include "jwse.h"
......@@ -13,6 +14,8 @@
namespace Arc {
Logger JWSE::logger_(Logger::getRootLogger(), "JWSE");
char const * const JWSE::ClaimNameSubject = "sub";
char const * const JWSE::ClaimNameIssuer = "iss";
char const * const JWSE::ClaimNameAudience = "aud";
......@@ -44,6 +47,7 @@ namespace Arc {
bool JWSE::Input(std::string const& jwseCompact) {
Cleanup();
logger_.msg(DEBUG, "JWSE::Input: token: %s", jwseCompact);
char const* pos = jwseCompact.c_str();
while(std::isspace(*pos) != 0) {
if(*pos == '\0') return false;
......@@ -61,6 +65,11 @@ namespace Arc {
std::string joseStr = Base64::decodeURLSafe(joseStart, joseEnd-joseStart);
header_ = cJSON_Parse(joseStr.c_str());
if(!header_) return false;
{
char* headerStr = cJSON_Print(header_.Ptr());
logger_.msg(DEBUG, "JWSE::Input: header: %s", headerStr);
std::free(headerStr);
}
cJSON* algObject = cJSON_GetObjectItem(header_.Ptr(), HeaderNameAlgorithm);
if(algObject == NULL) return false; // Neither JWS nor JWE
if(algObject->type != cJSON_String) return false;
......@@ -82,26 +91,39 @@ namespace Arc {
content_ = cJSON_CreateObject();
}
if(!content_) return false;
{
char* contentStr = cJSON_Print(content_.Ptr());
logger_.msg(DEBUG, "JWSE::Input: JWS content: %s", contentStr);
std::free(contentStr);
}
// Time
cJSON* notBefore = cJSON_GetObjectItem(content_.Ptr(), ClaimNameNotBefore);
if(notBefore) {
if(notBefore->type != cJSON_Number) return false;
time_t notBeforeTime = static_cast<time_t>(notBefore->valueint);
if(static_cast<int>(time(NULL)-notBeforeTime) < 0) return false;
if(static_cast<int>(time(NULL)-notBeforeTime) < 0) {
logger_.msg(DEBUG, "JWSE::Input: JWS: token too young");
return false;
}
}
cJSON* notAfter = cJSON_GetObjectItem(content_.Ptr(), ClaimNameNotAfter);
if(notAfter) {
if(notAfter->type != cJSON_Number) return false;
time_t notAfterTime = static_cast<time_t>(notAfter->valueint);
if(static_cast<int>(notAfterTime - time(NULL)) < 0) return false;
if(static_cast<int>(notAfterTime - time(NULL)) < 0) {
logger_.msg(DEBUG, "JWSE::Input: JWS: token too old");
return false;
}
}
// Signature
if(!ExtractPublicKey()) return false;
char const* signatureStart = pos;
char const* signatureEnd = jwseCompact.c_str() + jwseCompact.length();
std::string signature = Base64::decodeURLSafe(signatureStart, signatureEnd-signatureStart);
bool verifyResult = false;
logger_.msg(DEBUG, "JWSE::Input: JWS: signature algorthm: %s", algObject->valuestring);
if(strcmp(algObject->valuestring, "none") == 0) {
verifyResult = signature.empty(); // expecting empty signature if no protection is requested
} else if(strcmp(algObject->valuestring, "HS256") == 0) {
......@@ -122,6 +144,24 @@ namespace Arc {
} else if(strcmp(algObject->valuestring, "ES512") == 0) {
verifyResult = VerifyECDSA("SHA512", joseStart, payloadEnd-joseStart,
reinterpret_cast<unsigned char const*>(signature.c_str()), signature.length());
} else if(strcmp(algObject->valuestring, "RS256") == 0) {
verifyResult = VerifyRSASSAPKCS1("SHA256", joseStart, payloadEnd-joseStart,
reinterpret_cast<unsigned char const*>(signature.c_str()), signature.length());
} else if(strcmp(algObject->valuestring, "RS384") == 0) {
verifyResult = VerifyRSASSAPKCS1("SHA384", joseStart, payloadEnd-joseStart,
reinterpret_cast<unsigned char const*>(signature.c_str()), signature.length());
} else if(strcmp(algObject->valuestring, "RS512") == 0) {
verifyResult = VerifyRSASSAPKCS1("SHA512", joseStart, payloadEnd-joseStart,
reinterpret_cast<unsigned char const*>(signature.c_str()), signature.length());
} else if(strcmp(algObject->valuestring, "PS256") == 0) {
verifyResult = VerifyRSASSAPSS("SHA256", joseStart, payloadEnd-joseStart,
reinterpret_cast<unsigned char const*>(signature.c_str()), signature.length());
} else if(strcmp(algObject->valuestring, "PS384") == 0) {
verifyResult = VerifyRSASSAPSS("SHA384", joseStart, payloadEnd-joseStart,
reinterpret_cast<unsigned char const*>(signature.c_str()), signature.length());
} else if(strcmp(algObject->valuestring, "PS512") == 0) {
verifyResult = VerifyRSASSAPSS("SHA512", joseStart, payloadEnd-joseStart,
reinterpret_cast<unsigned char const*>(signature.c_str()), signature.length());
}
/*
| RS256 | RSASSA-PKCS1-v1_5 using | Recommended |
......@@ -138,10 +178,14 @@ namespace Arc {
| | MGF1 with SHA-512 | |
*/
if(!verifyResult) return false;
if(!verifyResult) {
logger_.msg(DEBUG, "JWSE::Input: JWS: signature verification failed");
return false;
}
} else {
// JWE - not yet
header_ = NULL;
logger_.msg(DEBUG, "JWSE::Input: JWE: not supported yet");
return false;
}
valid_ = true;
......
#include <string>
#include <arc/Utils.h>
#include <arc/Logger.h>
struct cJSON;
......@@ -20,10 +21,10 @@ namespace Arc {
static char const * const ClaimNameActivities;
static char const * const HeaderNameX509CertChain;
static char const * const HeaderNameJSONWebKey;
static char const * const HeaderNameJSONWebKeyId;
static char const * const HeaderNameAlgorithm;
static char const * const HeaderNameEncryption;
//! Parse scitoken available as simple string.
//! Mostly to be used for scitokens embedded into something
//! like HTTP header.
......@@ -86,6 +87,8 @@ namespace Arc {
private:
static Logger logger_;
bool valid_;
mutable AutoPointer<cJSON> header_;
......@@ -108,10 +111,20 @@ namespace Arc {
bool VerifyECDSA(char const* digestName, void const* message, unsigned int messageSize,
void const* signature, unsigned int signatureSize);
bool VerifyRSASSAPKCS1(char const* digestName, void const* message, unsigned int messageSize,
void const* signature, unsigned int signatureSize);
bool VerifyRSASSAPSS(char const* digestName, void const* message, unsigned int messageSize,
void const* signature, unsigned int signatureSize);
bool SignHMAC(char const* digestName, void const* message, unsigned int messageSize, std::string& signature) const;
bool SignECDSA(char const* digestName, void const* message, unsigned int messageSize, std::string& signature) const;
bool SignRSASSAPKCS1(char const* digestName, void const* message, unsigned int messageSize, std::string& signature) const;
bool SignRSASSAPSS(char const* digestName, void const* message, unsigned int messageSize, std::string& signature) const;
}; // class JWSE
} // namespace Arc
......
......@@ -25,7 +25,7 @@ namespace Arc {
if(!ctx) return false;
const EVP_MD* md = EVP_get_digestbyname(digestName);
if(!md) return false;
int rc = EVP_DigestVerifyInit(ctx.Ptr(), NULL, md, NULL, key_->PublicKey());
int rc = EVP_DigestVerifyInit(ctx.Ptr(), NULL, md, NULL, const_cast<EVP_PKEY*>(key_->PublicKey()));
if(rc != 1) return false;
rc = EVP_DigestVerifyUpdate(ctx.Ptr(), message, messageSize);
if(rc != 1) return false;
......@@ -41,7 +41,7 @@ namespace Arc {
if(!ctx) return false;
const EVP_MD* md = EVP_get_digestbyname(digestName);
if(!md) return false;
int rc = EVP_DigestSignInit(ctx.Ptr(), NULL, md, NULL, key_->PrivateKey());
int rc = EVP_DigestSignInit(ctx.Ptr(), NULL, md, NULL, const_cast<EVP_PKEY*>(key_->PrivateKey()));
if(rc != 1) return false;
rc = EVP_DigestSignUpdate(ctx.Ptr(), message, messageSize);
if(rc != 1) return false;
......
......@@ -6,6 +6,7 @@
#include <arc/Base64.h>
#include <arc/StringConv.h>
#include <arc/external/cJSON/cJSON.h>
#include <arc/message/MCC.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
......@@ -14,6 +15,7 @@
#include "jwse.h"
#include "jwse_private.h"
#include "openid_metadata.h"
static EVP_PKEY* X509_get_privkey(X509*) {
......@@ -72,6 +74,7 @@ namespace Arc {
char const * const JWSE::HeaderNameX509CertChain = "x5c";
char const * const JWSE::HeaderNameJSONWebKey = "jwk";
char const * const JWSE::HeaderNameJSONWebKeyId = "kid";
static void sk_x509_deallocate(STACK_OF(X509)* o) {
sk_X509_pop_free(o, X509_free);
......@@ -94,14 +97,17 @@ namespace Arc {
std::string exponent = Base64::decodeURLSafe(exponentObject->valuestring);
AutoPointer<RSA> rsaKey(RSA_new(),&RSA_free);
if(!rsaKey) return NULL;
BIGNUM* n(NULL);
BIGNUM* e(NULL);
if((n = BN_bin2bn(reinterpret_cast<unsigned char const *>(modulus.c_str()), modulus.length(), NULL)) == NULL) return NULL;
if((e = BN_bin2bn(reinterpret_cast<unsigned char const *>(exponent.c_str()), exponent.length(), NULL)) == NULL) return NULL;
RSA_set0_key(rsaKey.Ptr(), n, e, NULL);
AutoPointer<BIGNUM> n(NULL,&BN_free);
AutoPointer<BIGNUM> e(NULL,&BN_free);
if(!(n = BN_bin2bn(reinterpret_cast<unsigned char const *>(modulus.c_str()), modulus.length(), NULL))) return NULL;
if(!(e = BN_bin2bn(reinterpret_cast<unsigned char const *>(exponent.c_str()), exponent.length(), NULL))) return NULL;
if(!RSA_set0_key(rsaKey.Ptr(), n.Ptr(), e.Ptr(), NULL)) return NULL;
n.Release();
e.Release();
AutoPointer<EVP_PKEY> evpKey(EVP_PKEY_new(), &EVP_PKEY_free);
if(!evpKey) return NULL;
if(EVP_PKEY_set1_RSA(evpKey.Ptr(), rsaKey.Ptr()) != 1) return NULL;
if(EVP_PKEY_assign_RSA(evpKey.Ptr(), rsaKey.Ptr()) != 1) return NULL;
rsaKey.Release();
return evpKey.Release();
}
......@@ -112,6 +118,7 @@ namespace Arc {
if((keyObject->type != cJSON_String) || (algObject->type != cJSON_String)) return NULL;
std::string key = Base64::decodeURLSafe(keyObject->valuestring);
// It looks like RFC does not define any "alg" values with "JWK" usage.
// TODO: finish implementing
return NULL;
}
......@@ -139,14 +146,14 @@ namespace Arc {
}
static bool jwkRSASerialize(cJSON* jwkObject, EVP_PKEY* key) {
static bool jwkRSASerialize(cJSON* jwkObject, EVP_PKEY const * key) {
if(!key) return false;
RSA* rsaKey = EVP_PKEY_get0_RSA(key);
RSA const* rsaKey = EVP_PKEY_get0_RSA(const_cast<EVP_PKEY*>(key));
if(!rsaKey) return false;
BIGNUM const* n(NULL);
BIGNUM const* e(NULL);
BIGNUM const* d(NULL);
RSA_get0_key(rsaKey, &n, &e, &d);
RSA_get0_key(const_cast<RSA*>(rsaKey), &n, &e, &d);
if((!n) || (!e)) return false;
std::string modulus;
......@@ -163,7 +170,7 @@ namespace Arc {
return true;
}
static cJSON* jwkSerialize(JWSEKeyHolder& keyHolder) {
static cJSON* jwkSerialize(JWSEKeyHolder const& keyHolder) {
AutoPointer<cJSON> jwkObject(cJSON_CreateObject(), &cJSON_Delete);
if(jwkRSASerialize(jwkObject.Ptr(), keyHolder.PublicKey())) {
return jwkObject.Release();
......@@ -210,8 +217,8 @@ namespace Arc {
return true;
}
static cJSON* x5cSerialize(JWSEKeyHolder& keyHolder) {
X509* cert = keyHolder.Certificate();
static cJSON* x5cSerialize(JWSEKeyHolder const& keyHolder) {
X509 const* cert = keyHolder.Certificate();
if(!cert) return NULL;
const STACK_OF(X509)* chain = keyHolder.CertificateChain();
......@@ -219,7 +226,7 @@ namespace Arc {
int certN = 0;
while(true) {
AutoPointer<BIO> mem(BIO_new(BIO_s_mem()), &BIO_deallocate);
if(!PEM_write_bio_X509(mem.Ptr(), cert))
if(!PEM_write_bio_X509(mem.Ptr(), const_cast<X509*>(cert)))
return NULL;
std::string certStr;
std::string::size_type certStrSize = 0;
......@@ -254,24 +261,54 @@ namespace Arc {
bool JWSE::ExtractPublicKey() const {
key_.Release();
AutoPointer<JWSEKeyHolder> key(new JWSEKeyHolder());
// So far we are going to support only embedded keys - jwk and x5c
cJSON* x5cObject = cJSON_GetObjectItem(header_.Ptr(), HeaderNameX509CertChain);
cJSON* jwkObject = cJSON_GetObjectItem(header_.Ptr(), HeaderNameJSONWebKey);
cJSON* kidObject = cJSON_GetObjectItem(header_.Ptr(), HeaderNameJSONWebKeyId);
if(x5cObject != NULL) {
AutoPointer<JWSEKeyHolder> key(new JWSEKeyHolder());
logger_.msg(DEBUG, "JWSE::ExtractPublicKey: x5c key");
if(x5cParse(x5cObject, *key)) {
key_ = key.Release();
key_ = key;
return true;
}
} else if(jwkObject != NULL) {
AutoPointer<JWSEKeyHolder> key(new JWSEKeyHolder());
logger_.msg(DEBUG, "JWSE::ExtractPublicKey: jwk key");
if(jwkParse(jwkObject, *key)) {
key_ = key.Release();
key_ = key;
return true;
}
} else if(kidObject != NULL) {
logger_.msg(DEBUG, "JWSE::ExtractPublicKey: external jwk key");
if(kidObject->type != cJSON_String)
return false;
// TODO: authority validation
cJSON* issuerObj = cJSON_GetObjectItem(content_.Ptr(), ClaimNameIssuer);
if(!issuerObj || (issuerObj->type != cJSON_String))
return false;
OpenIDMetadata serviceMetadata;
OpenIDMetadataFetcher metadataFetcher(issuerObj->valuestring);
if(!metadataFetcher.Fetch(serviceMetadata))
return false;
char const * jwksUri = serviceMetadata.JWKSURI();
logger_.msg(DEBUG, "JWSE::ExtractPublicKey: fetching jwl key from %s", jwksUri);
JWSEKeyFetcher keyFetcher(jwksUri);
JWSEKeyHolderList keys;
if(!keyFetcher.Fetch(keys))
return false;
for(JWSEKeyHolderList::iterator keyIt = keys.begin(); keyIt != keys.end(); ++keyIt) {
if(strcmp(kidObject->valuestring, (*keyIt)->Id()) == 0) {
key_ = *keyIt;
return true;
}
}
} else {
logger_.msg(ERROR, "JWSE::ExtractPublicKey: no supported key");
return true; // No key - no processing
};
logger_.msg(ERROR, "JWSE::ExtractPublicKey: key parsing error");
return false;
}
......@@ -334,7 +371,6 @@ namespace Arc {
}
if(certN > 0) certificateChain_ = certchain.Release();
}
}
JWSEKeyHolder::~JWSEKeyHolder() {
......@@ -346,7 +382,15 @@ namespace Arc {
certificateChain_ = NULL;
}
EVP_PKEY* JWSEKeyHolder::PublicKey() {
char const* JWSEKeyHolder::Id() const {
return id_.c_str();
}
void JWSEKeyHolder::Id(char const* id) {
id_.assign(id ? id : "");
}
EVP_PKEY const* JWSEKeyHolder::PublicKey() const {
return publicKey_;
}
......@@ -357,7 +401,7 @@ namespace Arc {
certificate_ = NULL;
}
EVP_PKEY* JWSEKeyHolder::PrivateKey() {
EVP_PKEY const* JWSEKeyHolder::PrivateKey() const {
return privateKey_;
}
......@@ -368,7 +412,7 @@ namespace Arc {
certificate_ = NULL;
}
X509* JWSEKeyHolder::Certificate() {
X509 const* JWSEKeyHolder::Certificate() const {
return certificate_;
}
......@@ -383,7 +427,7 @@ namespace Arc {
certificate_ = certificate;
}
STACK_OF(X509)* JWSEKeyHolder::CertificateChain() {
STACK_OF(X509) const* JWSEKeyHolder::CertificateChain() const {
return certificateChain_;
}
......@@ -393,4 +437,43 @@ namespace Arc {
}
JWSEKeyFetcher::JWSEKeyFetcher(char const * endpoint_url) : url_(endpoint_url), client_(Arc::MCCConfig(), url_) {
}
bool JWSEKeyFetcher::Fetch(JWSEKeyHolderList& keys) {
HTTPClientInfo info;
PayloadRaw request;
PayloadRawInterface* response(NULL);
MCC_Status status = client_.process("GET", &request, &info, &response);
if(!status)
return false;
if(!response)
return false;
if(!(response->Content()))
return false;
AutoPointer<cJSON> content(cJSON_Parse(response->Content()), &cJSON_Delete);
if(!content)
return false;
cJSON* keysObj = cJSON_GetObjectItem(content.Ptr(), "keys");
if(!keysObj || (keysObj->type != cJSON_Array))
return false;
for(int idx = 0; idx < cJSON_GetArraySize(keysObj); ++idx) {
cJSON* keyObj = cJSON_GetArrayItem(keysObj, idx);
if(!keyObj || (keyObj->type != cJSON_Object))
continue;
cJSON* kidObj = cJSON_GetObjectItem(keyObj, "kid");
std::string id;
if(kidObj && (kidObj->type == cJSON_String))
id.assign(kidObj->valuestring);
Arc::AutoPointer<JWSEKeyHolder> key(new JWSEKeyHolder());
if(!key) continue;
if(!jwkParse(keyObj, *key.Ptr()))
continue;
key->Id(id.c_str());
keys.add(key);
};
return true;
}
} // namespace Arc
#include <openssl/x509.h>
#include <arc/URL.h>
#include <arc/communication/ClientInterface.h>