blob: 4f583f3781c3b49782311c24ef7f37348bf2450c [file] [log] [blame]
/*
* Copyright 2002-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* XSEC
*
* OpenSSLCryptoKeyRSA := RSA Keys
*
* Author(s): Berin Lautenbach
*
* $Id$
*
*/
#include <xsec/framework/XSECDefs.hpp>
#if defined (HAVE_OPENSSL)
#include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
#include <xsec/enc/OpenSSL/OpenSSLCryptoBase64.hpp>
#include <xsec/enc/XSECCryptoException.hpp>
#include <xsec/enc/XSECCryptoUtils.hpp>
#include <xsec/framework/XSECError.hpp>
#include <openssl/rsa.h>
#include <xercesc/util/Janitor.hpp>
XSEC_USING_XERCES(ArrayJanitor);
#include <memory.h>
OpenSSLCryptoKeyRSA::OpenSSLCryptoKeyRSA() :
mp_rsaKey(NULL),
mp_oaepParams(NULL),
m_oaepParamsLen(0) {
};
OpenSSLCryptoKeyRSA::~OpenSSLCryptoKeyRSA() {
// If we have a RSA, delete it (OpenSSL will clear the memory)
if (mp_rsaKey)
RSA_free(mp_rsaKey);
if (mp_oaepParams != NULL)
delete[] mp_oaepParams;
};
void OpenSSLCryptoKeyRSA::setOAEPparams(unsigned char * params, unsigned int paramsLen) {
if (mp_oaepParams != NULL) {
delete[] mp_oaepParams;
}
m_oaepParamsLen = paramsLen;
if (params != NULL) {
XSECnew(mp_oaepParams, unsigned char[paramsLen]);
memcpy(mp_oaepParams, params, paramsLen);
}
else
mp_oaepParams = NULL;
}
unsigned int OpenSSLCryptoKeyRSA::getOAEPparamsLen(void) const {
return m_oaepParamsLen;
}
const unsigned char * OpenSSLCryptoKeyRSA::getOAEPparams(void) const {
return mp_oaepParams;
}
// Generic key functions
XSECCryptoKey::KeyType OpenSSLCryptoKeyRSA::getKeyType() const {
// Find out what we have
if (mp_rsaKey == NULL)
return KEY_NONE;
if (mp_rsaKey->n != NULL && mp_rsaKey->d != NULL)
return KEY_RSA_PAIR;
if (mp_rsaKey->d != NULL)
return KEY_RSA_PRIVATE;
if (mp_rsaKey->n != NULL)
return KEY_RSA_PUBLIC;
return KEY_NONE;
}
void OpenSSLCryptoKeyRSA::loadPublicModulusBase64BigNums(const char * b64, unsigned int len) {
if (mp_rsaKey == NULL)
mp_rsaKey = RSA_new();
mp_rsaKey->n = OpenSSLCryptoBase64::b642BN((char *) b64, len);
}
void OpenSSLCryptoKeyRSA::loadPublicExponentBase64BigNums(const char * b64, unsigned int len) {
if (mp_rsaKey == NULL)
mp_rsaKey = RSA_new();
mp_rsaKey->e = OpenSSLCryptoBase64::b642BN((char *) b64, len);
}
// "Hidden" OpenSSL functions
OpenSSLCryptoKeyRSA::OpenSSLCryptoKeyRSA(EVP_PKEY *k) {
// Create a new key to be loaded as we go
mp_oaepParams = NULL;
m_oaepParamsLen = 0;
mp_rsaKey = RSA_new();
if (k == NULL || k->type != EVP_PKEY_RSA)
return; // Nothing to do with us
if (k->pkey.rsa->n)
mp_rsaKey->n = BN_dup(k->pkey.rsa->n);
if (k->pkey.rsa->e)
mp_rsaKey->e = BN_dup(k->pkey.rsa->e);
if (k->pkey.rsa->d)
mp_rsaKey->d = BN_dup(k->pkey.rsa->d);
if (k->pkey.rsa->p)
mp_rsaKey->p = BN_dup(k->pkey.rsa->p);
if (k->pkey.rsa->q)
mp_rsaKey->q = BN_dup(k->pkey.rsa->q);
if (k->pkey.rsa->dmp1)
mp_rsaKey->dmp1 = BN_dup(k->pkey.rsa->dmp1);
if (k->pkey.rsa->dmq1)
mp_rsaKey->dmq1 = BN_dup(k->pkey.rsa->dmq1);
if (k->pkey.rsa->iqmp)
mp_rsaKey->iqmp = BN_dup(k->pkey.rsa->iqmp);
}
// --------------------------------------------------------------------------------
// Verify a signature encoded as a Base64 string
// --------------------------------------------------------------------------------
bool OpenSSLCryptoKeyRSA::verifySHA1PKCS1Base64Signature(const unsigned char * hashBuf,
unsigned int hashLen,
const char * base64Signature,
unsigned int sigLen,
hashMethod hm = HASH_SHA1) {
// Use the currently loaded key to validate the Base64 encoded signature
if (mp_rsaKey == NULL) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA - Attempt to validate signature with empty key");
}
unsigned char sigVal[1024];
int sigValLen;
EVP_ENCODE_CTX m_dctx;
int rc;
char * cleanedBase64Signature;
unsigned int cleanedBase64SignatureLen = 0;
cleanedBase64Signature =
XSECCryptoBase64::cleanBuffer(base64Signature, sigLen, cleanedBase64SignatureLen);
ArrayJanitor<char> j_cleanedBase64Signature(cleanedBase64Signature);
EVP_DecodeInit(&m_dctx);
rc = EVP_DecodeUpdate(&m_dctx,
sigVal,
&sigValLen,
(unsigned char *) cleanedBase64Signature,
cleanedBase64SignatureLen);
if (rc < 0) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA - Error during Base64 Decode");
}
int t = 0;
EVP_DecodeFinal(&m_dctx, &sigVal[sigValLen], &t);
sigValLen += t;
// Now decrypt
unsigned char * decryptBuf;
// Decrypt will always be longer than (RSA_len(key) - 11)
decryptBuf = new unsigned char [RSA_size(mp_rsaKey)];
ArrayJanitor<unsigned char> j_decryptBuf(decryptBuf);
// Note at this time only supports PKCS1 padding
// As that is what is defined in the standard.
// If this ever changes we will need to pass some paramaters
// into this function to allow it to determine what the
// padding should be and what the message digest OID should
// be.
int decryptSize = RSA_public_decrypt(sigValLen,
sigVal,
decryptBuf,
mp_rsaKey,
RSA_PKCS1_PADDING);
if (decryptSize < 0) {
/* throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA::verify() - Error decrypting signature"); */
// Really - this is a failed signature check, not an exception!
return false;
}
/* Check the OID */
int oidLen = 0;
unsigned char * oid = getRSASigOID(hm, oidLen);
if (oid == NULL) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA::verify() - Unsupported HASH algorithm for RSA");
}
if (decryptSize != (int) (oidLen + hashLen) || hashLen != oid[oidLen-1]) {
return false;
}
for (t = 0; t < oidLen; ++t) {
if (oid[t] != decryptBuf[t]) {
return false;
}
}
for (;t < decryptSize; ++t) {
if (hashBuf[t-oidLen] != decryptBuf[t]) {
return false;
}
}
// All OK
return true;
}
// --------------------------------------------------------------------------------
// Sign and encode result as a Base64 string
// --------------------------------------------------------------------------------
unsigned int OpenSSLCryptoKeyRSA::signSHA1PKCS1Base64Signature(unsigned char * hashBuf,
unsigned int hashLen,
char * base64SignatureBuf,
unsigned int base64SignatureBufLen,
hashMethod hm) {
// Sign a pre-calculated hash using this key
if (mp_rsaKey == NULL) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA - Attempt to sign data with empty key");
}
// Build the buffer to be encrypted by prepending the SHA1 OID to the hash
unsigned char * encryptBuf;
unsigned char * preEncryptBuf;
unsigned char * oid;
int oidLen;
int encryptLen;
int preEncryptLen;
oid = getRSASigOID(hm, oidLen);
if (oid == NULL) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA::sign() - Unsupported HASH algorithm for RSA");
}
if (hashLen != oid[oidLen-1]) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA::sign() - hashLen incorrect for hash type");
}
preEncryptLen = hashLen + oidLen;
preEncryptBuf = new unsigned char[preEncryptLen];
encryptBuf = new unsigned char[RSA_size(mp_rsaKey)];
memcpy(preEncryptBuf, oid, oidLen);
memcpy(&preEncryptBuf[oidLen], hashBuf, hashLen);
// Now encrypt
encryptLen = RSA_private_encrypt(preEncryptLen,
preEncryptBuf,
encryptBuf,
mp_rsaKey,
RSA_PKCS1_PADDING);
delete[] preEncryptBuf;
if (encryptLen < 0) {
delete[] encryptBuf;
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA::sign() - Error encrypting hash");
}
// Now convert to Base 64
BIO * b64 = BIO_new(BIO_f_base64());
BIO * bmem = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(bmem, 0);
b64 = BIO_push(b64, bmem);
// Translate signature to Base64
BIO_write(b64, encryptBuf, encryptLen);
BIO_flush(b64);
unsigned int sigValLen = BIO_read(bmem, base64SignatureBuf, base64SignatureBufLen);
BIO_free_all(b64);
delete[] encryptBuf;
if (sigValLen <= 0) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:RSA - Error base64 encoding signature");
}
return sigValLen;
}
// --------------------------------------------------------------------------------
// decrypt a buffer
// --------------------------------------------------------------------------------
unsigned int OpenSSLCryptoKeyRSA::privateDecrypt(const unsigned char * inBuf,
unsigned char * plainBuf,
unsigned int inLength,
unsigned int maxOutLength,
PaddingType padding,
hashMethod hm) {
// Perform a decrypt
if (mp_rsaKey == NULL) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA - Attempt to decrypt data with empty key");
}
#if 0
/* normally commented out code to determine endian problems */
unsigned int i;
unsigned char e[2048];
unsigned char * inBuf1 = (unsigned char *) inBuf;
if (inLength < 2048) {
memcpy(e, inBuf, inLength);
for (i = 0; i < inLength;++i) {
inBuf1[i] = e[inLength - 1 - i];
}
}
#endif
int decryptSize;
switch (padding) {
case XSECCryptoKeyRSA::PAD_PKCS_1_5 :
decryptSize = RSA_private_decrypt(inLength,
#if defined(XSEC_OPENSSL_CONST_BUFFERS)
inBuf,
#else
(unsigned char *) inBuf,
#endif
plainBuf,
mp_rsaKey,
RSA_PKCS1_PADDING);
if (decryptSize < 0) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA privateKeyDecrypt - Error Decrypting PKCS1_5 padded RSA encrypt");
}
break;
case XSECCryptoKeyRSA::PAD_OAEP_MGFP1 :
{
unsigned char * tBuf;
int num = RSA_size(mp_rsaKey);
XSECnew(tBuf, unsigned char[inLength]);
ArrayJanitor<unsigned char> j_tBuf(tBuf);
decryptSize = RSA_private_decrypt(inLength,
#if defined(XSEC_OPENSSL_CONST_BUFFERS)
inBuf,
#else
(unsigned char *) inBuf,
#endif
tBuf,
mp_rsaKey,
RSA_NO_PADDING);
if (decryptSize < 0) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA privateKeyDecrypt - Error doing raw decrypt of RSA encrypted data");
}
// Clear out the "0"s at the front
int i;
for (i = 0; i < num && tBuf[i] == 0; ++i)
--decryptSize;
decryptSize = RSA_padding_check_PKCS1_OAEP(plainBuf,
maxOutLength,
&tBuf[i],
decryptSize,
num,
mp_oaepParams,
m_oaepParamsLen);
if (decryptSize < 0) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA privateKeyDecrypt - Error removing OAEPadding");
}
}
break;
default :
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA - Unknown padding method");
}
#if 0
/* normally commented out code to determine endian problems */
int i;
unsigned char t[512];
if (decryptSize < 512) {
memcpy(t, plainBuf, decryptSize);
for (i = 0; i < decryptSize;++i) {
plainBuf[i] = t[decryptSize - 1 - i];
}
}
#endif
return decryptSize;
}
// --------------------------------------------------------------------------------
// encrypt a buffer
// --------------------------------------------------------------------------------
unsigned int OpenSSLCryptoKeyRSA::publicEncrypt(const unsigned char * inBuf,
unsigned char * cipherBuf,
unsigned int inLength,
unsigned int maxOutLength,
PaddingType padding,
hashMethod hm) {
// Perform an encrypt
if (mp_rsaKey == NULL) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA - Attempt to encrypt data with empty key");
}
int encryptSize;
switch (padding) {
case XSECCryptoKeyRSA::PAD_PKCS_1_5 :
encryptSize = RSA_public_encrypt(inLength,
#if defined(XSEC_OPENSSL_CONST_BUFFERS)
inBuf,
#else
(unsigned char *) inBuf,
#endif
cipherBuf,
mp_rsaKey,
RSA_PKCS1_PADDING);
if (encryptSize < 0) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA publicKeyEncrypt - Error performing PKCS1_5 padded RSA encrypt");
}
break;
case XSECCryptoKeyRSA::PAD_OAEP_MGFP1 :
{
unsigned char * tBuf;
unsigned int num = RSA_size(mp_rsaKey);
if (maxOutLength < num) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA publicKeyEncrypt - Not enough space in cipherBuf");
}
XSECnew(tBuf, unsigned char[num]);
ArrayJanitor<unsigned char> j_tBuf(tBuf);
// First add the padding
encryptSize = RSA_padding_add_PKCS1_OAEP(tBuf,
num,
#if defined(XSEC_OPENSSL_CONST_BUFFERS)
inBuf,
#else
(unsigned char *) inBuf,
#endif
inLength,
mp_oaepParams,
m_oaepParamsLen);
if (encryptSize <= 0) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA publicKeyEncrypt - Error adding OAEPadding");
}
encryptSize = RSA_public_encrypt(num,
tBuf,
cipherBuf,
mp_rsaKey,
RSA_NO_PADDING);
if (encryptSize < 0) {
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA publicKeyEncrypt - Error encrypting padded data");
}
}
break;
default :
throw XSECCryptoException(XSECCryptoException::RSAError,
"OpenSSL:RSA - Unknown padding method");
}
return encryptSize;
}
// --------------------------------------------------------------------------------
// Size in bytes
// --------------------------------------------------------------------------------
unsigned int OpenSSLCryptoKeyRSA::getLength(void) const {
if (mp_rsaKey != NULL)
return RSA_size(mp_rsaKey);
return 0;
}
// --------------------------------------------------------------------------------
// Clone this key
// --------------------------------------------------------------------------------
XSECCryptoKey * OpenSSLCryptoKeyRSA::clone() const {
OpenSSLCryptoKeyRSA * ret;
XSECnew(ret, OpenSSLCryptoKeyRSA);
ret->mp_rsaKey = RSA_new();
if (mp_oaepParams != NULL) {
XSECnew(ret->mp_oaepParams, unsigned char[m_oaepParamsLen]);
memcpy(ret->mp_oaepParams, mp_oaepParams, m_oaepParamsLen);
ret->m_oaepParamsLen = m_oaepParamsLen;
}
else {
ret->mp_oaepParams = NULL;
ret->m_oaepParamsLen = 0;
}
// Duplicate parameters
if (mp_rsaKey->n)
ret->mp_rsaKey->n = BN_dup(mp_rsaKey->n);
if (mp_rsaKey->e)
ret->mp_rsaKey->e = BN_dup(mp_rsaKey->e);
if (mp_rsaKey->d)
ret->mp_rsaKey->d = BN_dup(mp_rsaKey->d);
if (mp_rsaKey->p)
ret->mp_rsaKey->p = BN_dup(mp_rsaKey->p);
if (mp_rsaKey->q)
ret->mp_rsaKey->q = BN_dup(mp_rsaKey->q);
if (mp_rsaKey->dmp1)
ret->mp_rsaKey->dmp1 = BN_dup(mp_rsaKey->dmp1);
if (mp_rsaKey->dmq1)
ret->mp_rsaKey->dmq1 = BN_dup(mp_rsaKey->dmq1);
if (mp_rsaKey->iqmp)
ret->mp_rsaKey->iqmp = BN_dup(mp_rsaKey->iqmp);
return ret;
}
#endif /* HAVE_OPENSSL */