14 Commits
v0.1 ... v0.3.2

Author SHA1 Message Date
Grégory Soutadé
7f5a4900e0 Typo fix 2021-08-21 20:37:07 +02:00
Grégory Soutadé
d8f882a277 Fix a bug : gmtime() should used in place of localtime() to keep date in UTC and not local timezone 2021-08-12 21:11:40 +02:00
Grégory Soutadé
bdaceba2e0 Fix bad utils return value on error. Build network errors with a message and not just an error code. 2021-07-29 21:14:48 +02:00
Grégory Soutadé
b5eb0efd31 Force ACSM files to be parsed using UTF8 locale 2021-07-17 09:34:03 +02:00
Grégory Soutadé
20b4e69c3e Rename too generic "activate" util in "adept_activate" 2021-07-16 20:44:40 +02:00
Grégory Soutadé
878e4e7453 ACSM data mut be unescaped before parsing
Add a copy constructor to Exception
Start an authentication when E_ADEPT_DISTRIBUTOR_AUTH error is received during fulfill
Don't download external libraries if it already exists
2021-07-16 20:38:32 +02:00
Grégory Soutadé
1eebc88913 Update README.md 2021-07-12 21:26:53 +02:00
Grégory Soutadé
460f452783 Fix an error in hashNode : a tag can define multiple xml namespaces 2021-07-12 14:23:18 +02:00
Grégory Soutadé
8881a58c95 Handle 'Location' header reply in drmprocessorclientimpl 2021-07-10 12:51:36 +02:00
Grégory Soutadé
0e90b89382 v0.2 Fix a lot of things :
* add common method signNode() and addNonde()
  * Try to auth to operator if not already set
  * Fix an error in ADEPT protocol : attributes must be hashed in alphabetical order, not in reverse one
  * Update DRMProcessorClient
2021-07-09 21:55:39 +02:00
Grégory Soutadé
7adc6a0fc1 Fix bug : -o option not handled in acsmdownloader 2021-07-08 12:59:56 +02:00
Grégory Soutadé
429fe34e8e Add README_package.md 2021-07-06 22:16:26 +02:00
Grégory Soutadé
49a74cdd6f Set default login method to "AdobeID" 2021-07-06 21:04:48 +02:00
Grégory Soutadé
c85db73583 Fix misplaced break into switch 2021-07-06 20:52:43 +02:00
21 changed files with 420 additions and 117 deletions

View File

@@ -62,7 +62,7 @@ Utils
You can import configuration from your eReader or create a new one with utils/activate :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
./utils/activate -u <USERNAME>
./utils/activate -u <AdobeID USERNAME>
Then a _./.adept_ directory is created with all configuration file
@@ -85,3 +85,10 @@ License
libgourou : LGPL v3 or later
utils : BSD
Special thanks
--------------
* _Jens_ for all test samples and utils testing

68
README_package.md Normal file
View File

@@ -0,0 +1,68 @@
Introduction
------------
libgourou is a free implementation of Adobe's ADEPT protocol used to add DRM on ePub files. It overcome the lacks of Adobe support for Linux platforms.
Dependencies
------------
For libgourou :
* None
For utils :
* QT5Core
* QT5Network
* OpenSSL
* libzip
Utils
-----
You can import configuration from your eReader or create a new one with activate :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
./activate -u <AdobeID USERNAME>
Then a _./.adept_ directory is created with all configuration file
To download an ePub :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
./acsmdownloader -f <ACSM_FILE>
Sources
-------
http://indefero.soutade.fr/p/libgourou
Copyright
---------
Grégory Soutadé
License
-------
libgourou : LGPL v3 or later
utils : BSD
Special thanks
--------------
* _Jens_ for all test samples and utils testing

View File

@@ -48,7 +48,7 @@ namespace gourou
const unsigned char* getDeviceKey();
/**
* @brief Get one value of device.xml (deviceClass, deviceSerial, deviceName, deviceType, jobbes, clientOS, clientLocale)
* @brief Get one value of device.xml (deviceClass, deviceSerial, deviceName, deviceType, hobbes, clientOS, clientLocale)
*/
std::string getProperty(const std::string& property, const std::string& _default=std::string(""));
std::string operator[](const std::string& property);

View File

@@ -173,6 +173,20 @@ namespace gourou
* @param keyOutLength Length of result
*/
virtual void extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength) = 0;
/**
* @brief Extract certificate from PKCS12 blob
*
* @param RSAKey RSA key in binary form
* @param RSAKeyLength RSA key length
* @param keyType Key type
* @param password Optional password for RSA PKCS12 certificate
* @param certOut Result certificate
* @param certOutLength Result certificate length
*/
virtual void extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength,
const RSA_KEY_TYPE keyType, const std::string& password,
unsigned char** certOut, unsigned int* certOutLength) = 0;
};
class CryptoInterface

View File

@@ -40,7 +40,7 @@
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
#endif
#define LIBGOUROU_VERSION "0.1"
#define LIBGOUROU_VERSION "0.3.2"
namespace gourou
{
@@ -184,6 +184,12 @@ namespace gourou
void pushTag(void* sha_ctx, uint8_t tag);
void hashNode(const pugi::xml_node& root, void *sha_ctx, std::map<std::string,std::string> nsHash);
void hashNode(const pugi::xml_node& root, unsigned char* sha_out);
std::string signNode(const pugi::xml_node& rootNode);
void addNonce(pugi::xml_node& root);
void buildAuthRequest(pugi::xml_document& authReq);
void buildInitLicenseServiceRequest(pugi::xml_document& initLicReq, std::string operatorURL);
void doOperatorAuth(std::string operatorURL);
void operatorAuth(std::string operatorURL);
void buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq);
void buildActivateReq(pugi::xml_document& activateReq);
ByteArray sendFulfillRequest(const pugi::xml_document& document, const std::string& url);

View File

@@ -107,8 +107,8 @@ namespace gourou
CLIENT_BAD_KEY_SIZE,
CLIENT_BAD_ZIP_FILE,
CLIENT_ZIP_ERROR,
CLIENT_GENERIC_EXCEPTION
CLIENT_GENERIC_EXCEPTION,
CLIENT_NETWORK_ERROR,
};
/**
@@ -128,6 +128,14 @@ namespace gourou
fullmessage = strdup(msg.str().c_str());
}
Exception(const Exception& other)
{
this->code = other.code;
this->line = line;
this->file = file;
this->fullmessage = strdup(other.fullmessage);
}
~Exception()
{
free(fullmessage);

View File

@@ -70,6 +70,11 @@ namespace gourou
*/
std::string getProperty(const std::string property);
/**
* @brief Get all nodes with property name
*/
pugi::xpath_node_set getProperties(const std::string property);
/**
* @brief Create activation.xml and devicesalt files if they did not exists
*

View File

@@ -1,10 +1,14 @@
#!/bin/bash
# Pugixml
if [ ! -d lib/pugixml ] ; then
git clone https://github.com/zeux/pugixml.git lib/pugixml
pushd lib/pugixml
git checkout latest
popd
fi
# Base64
if [ ! -d lib/base64 ] ; then
git clone https://gist.github.com/f0fd86b6c73063283afe550bc5d77594.git lib/base64
fi

View File

@@ -20,6 +20,7 @@
#include <arpa/inet.h>
#include <sys/time.h>
#include <time.h>
#include <vector>
#include <libgourou.h>
#include <libgourou_common.h>
@@ -131,7 +132,8 @@ namespace gourou
ns = attrName.substr(attrName.find(':')+1);
nsHash[ns] = ait->value();
break;
// Don't break here because we may multiple xmlns definitions
// break;
}
}
@@ -156,13 +158,28 @@ namespace gourou
pushString(sha_ctx, name);
// Must be parsed in reverse order
for (pugi::xml_attribute attr = root.last_attribute();
attr; attr = attr.previous_attribute())
std::vector<std::string> attributes;
pugi::xml_attribute attr;
for (attr = root.first_attribute();
attr; attr = attr.next_attribute())
{
if (std::string(attr.name()).find("xmlns") != std::string::npos)
continue;
attributes.push_back(attr.name());
}
// Attributes must be handled in alphabetical order
std::sort(attributes.begin(), attributes.end());
std::vector<std::string>::iterator attributesIt;
for(attributesIt = attributes.begin();
attributesIt != attributes.end();
attributesIt++)
{
attr = root.attribute(attributesIt->c_str());
pushTag(sha_ctx, ASN_ATTRIBUTE);
pushString(sha_ctx, "");
@@ -213,9 +230,74 @@ namespace gourou
for(int i=0; i<(int)SHA1_LEN; i++)
printf("%02x ", sha_out[i]);
printf("\n");
}
}
std::string DRMProcessor::signNode(const pugi::xml_node& rootNode)
{
// Compute hash
unsigned char sha_out[SHA1_LEN];
hashNode(rootNode, sha_out);
// Sign with private key
unsigned char res[RSA_KEY_SIZE];
ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);
std::string pkcs12 = user->getPKCS12();
ByteArray privateRSAKey = ByteArray::fromBase64(pkcs12);
client->RSAPrivateEncrypt(privateRSAKey.data(), privateRSAKey.length(),
RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),
sha_out, sizeof(sha_out), res);
if (logLevel >= DEBUG)
{
printf("Sig : ");
for(int i=0; i<(int)sizeof(res); i++)
printf("%02x ", res[i]);
printf("\n");
}
ByteArray signature(res, sizeof(res));
return signature.toBase64();
}
void DRMProcessor::addNonce(pugi::xml_node& root)
{
/*
r4 = tp->time
r3 = 0
r2 = tm->militime
r0 = 0x6f046000
r1 = 0x388a
r3 += high(r4*1000)
r2 += low(r4*1000)
r0 += r2
r1 += r3
*/
struct timeval tv;
gettimeofday(&tv, 0);
uint32_t nonce32[2] = {0x6f046000, 0x388a};
uint64_t bigtime = tv.tv_sec*1000;
nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000);
nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF);
ByteArray nonce((const unsigned char*)&nonce32, sizeof(nonce32));
uint32_t tmp = 0;
nonce.append((const unsigned char*)&tmp, sizeof(tmp));
appendTextElem(root, "adept:nonce", nonce.toBase64().data());
time_t _time = time(0) + 10*60; // Cur time + 10 minutes
struct tm* tm_info = gmtime(&_time);
char buffer[32];
strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", tm_info);
appendTextElem(root, "adept:expiration", buffer);
}
ByteArray DRMProcessor::sendRequest(const std::string& URL, const std::string& POSTdata, const char* contentType)
{
if (contentType == 0)
@@ -243,6 +325,107 @@ namespace gourou
return sendRequest(url, xmlStr, (const char*)"application/vnd.adobe.adept+xml");
}
void DRMProcessor::buildAuthRequest(pugi::xml_document& authReq)
{
pugi::xml_node decl = authReq.append_child(pugi::node_declaration);
decl.append_attribute("version") = "1.0";
pugi::xml_node root = authReq.append_child("adept:credentials");
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
appendTextElem(root, "adept:user", user->getUUID());
ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);
unsigned char* pkcs12 = 0;
unsigned int pkcs12Length;
ByteArray pkcs12Cert = ByteArray::fromBase64(user->getPKCS12());
client->extractCertificate(pkcs12Cert.data(), pkcs12Cert.length(),
RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),
&pkcs12, &pkcs12Length);
ByteArray privateCertificate(pkcs12, pkcs12Length);
free(pkcs12);
appendTextElem(root, "adept:certificate", privateCertificate.toBase64());
appendTextElem(root, "adept:licenseCertificate", user->getProperty("//adept:licenseCertificate"));
appendTextElem(root, "adept:authenticationCertificate", user->getProperty("//adept:authenticationCertificate"));
}
void DRMProcessor::buildInitLicenseServiceRequest(pugi::xml_document& initLicReq, std::string operatorURL)
{
pugi::xml_node decl = initLicReq.append_child(pugi::node_declaration);
decl.append_attribute("version") = "1.0";
pugi::xml_node root = initLicReq.append_child("adept:licenseServiceRequest");
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
root.append_attribute("identity") = "user";
appendTextElem(root, "adept:operatorURL", operatorURL);
addNonce(root);
appendTextElem(root, "adept:user", user->getUUID());
std::string signature = signNode(root);
appendTextElem(root, "adept:signature", signature);
}
void DRMProcessor::doOperatorAuth(std::string operatorURL)
{
pugi::xml_document authReq;
buildAuthRequest(authReq);
std::string authURL = operatorURL;
unsigned int fulfillPos = authURL.rfind("Fulfill");
if (fulfillPos == (authURL.size() - (sizeof("Fulfill")-1)))
authURL = authURL.substr(0, fulfillPos-1);
ByteArray replyData = sendRequest(authReq, authURL + "/Auth");
pugi::xml_document initLicReq;
std::string activationURL = user->getProperty("//adept:activationURL");
buildInitLicenseServiceRequest(initLicReq, authURL);
sendRequest(initLicReq, activationURL + "/InitLicenseService");
}
void DRMProcessor::operatorAuth(std::string operatorURL)
{
pugi::xpath_node_set operatorList = user->getProperties("//adept:operatorURL");
for (pugi::xpath_node_set::const_iterator operatorIt = operatorList.begin();
operatorIt != operatorList.end(); ++operatorIt)
{
std::string value = operatorIt->node().first_child().value();
if (trim(value) == operatorURL)
{
GOUROU_LOG(DEBUG, "Already authenticated to operator " << operatorURL);
return;
}
}
doOperatorAuth(operatorURL);
// Add new operatorURL to list
pugi::xml_document activationDoc;
user->readActivation(activationDoc);
pugi::xml_node root;
pugi::xpath_node xpathRes = activationDoc.select_node("//adept:operatorURLList");
// Create adept:operatorURLList if it doesn't exists
if (!xpathRes)
{
xpathRes = activationDoc.select_node("/activationInfo");
root = xpathRes.node();
root = root.append_child("adept:operatorURLList");
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
appendTextElem(root, "adept:user", user->getUUID());
}
else
root = xpathRes.node();
appendTextElem(root, "adept:operatorURL", operatorURL);
user->updateActivationFile(activationDoc);
}
void DRMProcessor::buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq)
{
pugi::xml_node decl = fulfillReq.append_child(pugi::node_declaration);
@@ -277,7 +460,7 @@ namespace gourou
pugi::xml_document acsmDoc;
if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single))
if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8))
EXCEPTION(FF_INVALID_ACSM_FILE, "Invalid ACSM file " << ACSMFile);
GOUROU_LOG(INFO, "Fulfill " << ACSMFile);
@@ -300,36 +483,12 @@ namespace gourou
hmacParentNode.remove_child(hmacNode);
// Compute hash
unsigned char sha_out[SHA1_LEN];
hashNode(rootNode, sha_out);
// Sign with private key
unsigned char res[RSA_KEY_SIZE];
ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);
std::string pkcs12 = user->getPKCS12();
ByteArray privateRSAKey = ByteArray::fromBase64(pkcs12);
client->RSAPrivateEncrypt(privateRSAKey.data(), privateRSAKey.length(),
RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),
sha_out, sizeof(sha_out), res);
if (logLevel >= DEBUG)
{
printf("Sig : ");
for(int i=0; i<(int)sizeof(res); i++)
printf("%02x ", res[i]);
printf("\n");
}
std::string signature = signNode(rootNode);
// Add removed HMAC
appendTextElem(hmacParentNode, hmacNode.name(), hmacNode.first_child().value());
// Add base64 encoded signature
ByteArray signature(res, sizeof(res));
std::string b64Signature = signature.toBase64();
appendTextElem(rootNode, "adept:signature", b64Signature);
appendTextElem(rootNode, "adept:signature", signature);
pugi::xpath_node node = acsmDoc.select_node("//operatorURL");
if (!node)
@@ -338,7 +497,32 @@ namespace gourou
std::string operatorURL = node.node().first_child().value();
operatorURL = trim(operatorURL) + "/Fulfill";
ByteArray replyData = sendRequest(fulfillReq, operatorURL);
operatorAuth(operatorURL);
ByteArray replyData;
try
{
replyData = sendRequest(fulfillReq, operatorURL);
}
catch (gourou::Exception& e)
{
/*
Operator requires authentication even if it's already in
our operator list
*/
std::string errorMsg(e.what());
if (e.getErrorCode() == GOUROU_ADEPT_ERROR &&
errorMsg.find("E_ADEPT_DISTRIBUTOR_AUTH") != std::string::npos)
{
doOperatorAuth(operatorURL);
replyData = sendRequest(fulfillReq, operatorURL);
}
else
{
throw e;
}
}
pugi::xml_document fulfillReply;
@@ -373,7 +557,11 @@ namespace gourou
decl.append_attribute("version") = "1.0";
pugi::xml_node signIn = signInRequest.append_child("adept:signIn");
signIn.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
signIn.append_attribute("method") = user->getLoginMethod().c_str();
std::string loginMethod = user->getLoginMethod();
if (loginMethod.size())
signIn.append_attribute("method") = loginMethod.c_str();
else
signIn.append_attribute("method") = "AdobeID";
unsigned char encryptedSignInData[RSA_KEY_SIZE];
const unsigned char* deviceKey = device->getDeviceKey();
@@ -504,37 +692,7 @@ namespace gourou
appendTextElem(targetDevice, "adept:deviceType", (*device)["deviceType"]);
appendTextElem(targetDevice, "adept:fingerprint", (*device)["fingerprint"]);
/*
r4 = tp->time
r3 = 0
r2 = tm->militime
r0 = 0x6f046000
r1 = 0x388a
r3 += high(r4*1000)
r2 += low(r4*1000)
r0 += r2
r1 += r3
*/
struct timeval tv;
gettimeofday(&tv, 0);
uint32_t nonce32[2] = {0x6f046000, 0x388a};
uint64_t bigtime = tv.tv_sec*1000;
nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000);
nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF);
ByteArray nonce((const unsigned char*)&nonce32, sizeof(nonce32));
uint32_t tmp = 0;
nonce.append((const unsigned char*)&tmp, sizeof(tmp));
appendTextElem(root, "adept:nonce", nonce.toBase64().data());
time_t _time = time(0) + 10*60; // Cur time + 10 minutes
struct tm* tm_info = localtime(&_time);
char buffer[32];
strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", tm_info);
appendTextElem(root, "adept:expiration", buffer);
addNonce(root);
appendTextElem(root, "adept:user", user->getUUID());
}
@@ -547,28 +705,12 @@ namespace gourou
buildActivateReq(activateReq);
// Compute hash
unsigned char sha_out[SHA1_LEN];
pugi::xml_node root = activateReq.select_node("adept:activate").node();
hashNode(root, sha_out);
// Sign with private key
ByteArray RSAKey = ByteArray::fromBase64(user->getPKCS12());
unsigned char res[RSA_KEY_SIZE];
ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);
client->RSAPrivateEncrypt(RSAKey.data(), RSAKey.length(), RSAInterface::RSA_KEY_PKCS12,
deviceKey.toBase64().c_str(),
sha_out, sizeof(sha_out),
res);
// Add base64 encoded signature
ByteArray signature(res, sizeof(res));
std::string b64Signature = signature.toBase64();
std::string signature = signNode(root);
root = activateReq.select_node("adept:activate").node();
appendTextElem(root, "adept:signature", b64Signature);
appendTextElem(root, "adept:signature", signature);
pugi::xml_document activationDoc;
user->readActivation(activationDoc);

View File

@@ -110,6 +110,11 @@ namespace gourou {
return trim(res);
}
pugi::xpath_node_set User::getProperties(const std::string property)
{
return activationDoc.select_nodes(property.c_str());
}
User* User::createUser(DRMProcessor* processor, const std::string& dirName, const std::string& ACSServer)
{
struct stat _stat;

View File

@@ -1,5 +1,5 @@
TARGETS=acsmdownloader activate
TARGETS=acsmdownloader adept_activate
CXXFLAGS=-Wall `pkg-config --cflags Qt5Core Qt5Network` -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
ifneq ($(STATIC_UTILS),)
@@ -19,7 +19,7 @@ all: $(TARGETS)
acsmdownloader: drmprocessorclientimpl.cpp acsmdownloader.cpp
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
activate: drmprocessorclientimpl.cpp activate.cpp
adept_activate: drmprocessorclientimpl.cpp adept_activate.cpp
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
clean:

View File

@@ -66,6 +66,7 @@ public:
void run()
{
int ret = 0;
try
{
DRMProcessorClientImpl client;
@@ -82,6 +83,8 @@ public:
else
filename += ".epub";
}
else
filename = outputFile;
if (outputDir)
{
@@ -97,10 +100,10 @@ public:
} catch(std::exception& e)
{
std::cout << e.what() << std::endl;
this->app->exit(1);
ret = 1;
}
this->app->exit(0);
this->app->exit(ret);
}
private:

View File

@@ -100,10 +100,10 @@ static std::string getpass(const char *prompt, bool show_asterisk=false)
}
class Activate: public QRunnable
class ADEPTActivate: public QRunnable
{
public:
Activate(QCoreApplication* app):
ADEPTActivate(QCoreApplication* app):
app(app)
{
setAutoDelete(false);
@@ -111,6 +111,7 @@ public:
void run()
{
int ret = 0;
try
{
DRMProcessorClientImpl client;
@@ -124,10 +125,10 @@ public:
} catch(std::exception& e)
{
std::cout << e.what() << std::endl;
this->app->exit(1);
ret = 1;
}
this->app->exit(0);
this->app->exit(ret);
}
private:
@@ -262,7 +263,7 @@ int main(int argc, char** argv)
QCoreApplication app(argc, argv);
Activate activate(&app);
ADEPTActivate activate(&app);
QThreadPool::globalInstance()->start(&activate);
ret = app.exec();

View File

@@ -111,6 +111,24 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
while (!reply->isFinished())
app->processEvents();
QByteArray location = reply->rawHeader("Location");
if (location.size() != 0)
{
GOUROU_LOG(gourou::DEBUG, "New location");
return sendHTTPRequest(location.constData(), POSTData, contentType);
}
if (reply->error() != QNetworkReply::NoError)
EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << reply->errorString().toStdString());
if (gourou::logLevel >= gourou::DEBUG)
{
QList<QByteArray> headers = reply->rawHeaderList();
for (int i = 0; i < headers.size(); ++i) {
std::cout << headers[i].constData() << " : " << reply->rawHeader(headers[i]).constData() << std::endl;
}
}
replyData = reply->readAll();
if (reply->rawHeader("Content-Type") == "application/vnd.adobe.adept+xml")
{
@@ -213,6 +231,25 @@ void DRMProcessorClientImpl::extractRSAPrivateKey(void* handler, unsigned char**
EVP_PKEY_free(evpKey);
}
void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength,
const RSA_KEY_TYPE keyType, const std::string& password,
unsigned char** certOut, unsigned int* certOutLength)
{
PKCS12 * pkcs12;
EVP_PKEY* pkey = 0;
X509* cert = 0;
STACK_OF(X509)* ca;
pkcs12 = d2i_PKCS12(NULL, &RSAKey, RSAKeyLength);
if (!pkcs12)
EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));
PKCS12_parse(pkcs12, password.c_str(), &pkey, &cert, &ca);
*certOutLength = i2d_X509(cert, certOut);
EVP_PKEY_free(pkey);
}
/* Crypto interface */
void DRMProcessorClientImpl::AESEncrypt(CHAINING_MODE chaining,
const unsigned char* key, unsigned int keyLength,
@@ -244,8 +281,8 @@ void* DRMProcessorClientImpl::AESEncryptInit(CHAINING_MODE chaining,
break;
default:
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
break;
}
break;
default:
EVP_CIPHER_CTX_free(ctx);
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);

View File

@@ -63,6 +63,9 @@ class DRMProcessorClientImpl : public gourou::DRMProcessorClient
virtual void extractRSAPublicKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength);
virtual void extractRSAPrivateKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength);
virtual void extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength,
const RSA_KEY_TYPE keyType, const std::string& password,
unsigned char** certOut, unsigned int* certOutLength);
/* Crypto interface */
virtual void AESEncrypt(CHAINING_MODE chaining,