| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 (XSEC_HAVE_OPENSSL) |
| |
| #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp> |
| #include <xsec/enc/OpenSSL/OpenSSLCryptoBase64.hpp> |
| #include <xsec/enc/OpenSSL/OpenSSLSupport.hpp> |
| #include <xsec/enc/XSECCryptoException.hpp> |
| #include <xsec/enc/XSECCryptoUtils.hpp> |
| #include <xsec/framework/XSECError.hpp> |
| |
| #include "../../utils/XSECAlgorithmSupport.hpp" |
| |
| #include <openssl/err.h> |
| #include <openssl/rand.h> |
| #include <openssl/rsa.h> |
| #include <openssl/sha.h> |
| |
| #include <xercesc/util/Janitor.hpp> |
| |
| XSEC_USING_XERCES(ArrayJanitor); |
| |
| #include <memory.h> |
| |
| namespace { |
| |
| // This code is modified from OpenSSL to implement SHA-2 hashing with OAEP. |
| // The MGF code is limited to SHA-1 in accordance with the XML Encryption spec. |
| // 0.9.8+ has a public MGF routine to call, this is a copy of it for older versions. |
| |
| #ifndef XSEC_OPENSSL_HAVE_MGF1 |
| int PKCS1_MGF1(unsigned char *mask, long len, |
| const unsigned char *seed, long seedlen, const EVP_MD *dgst) |
| { |
| long i, outlen = 0; |
| unsigned char cnt[4]; |
| EVP_MD_CTX c; |
| unsigned char md[EVP_MAX_MD_SIZE]; |
| int mdlen; |
| int rv = -1; |
| |
| EVP_MD_CTX_init(&c); |
| mdlen = EVP_MD_size(dgst); |
| if (mdlen < 0) |
| goto err; |
| for (i = 0; outlen < len; i++) |
| { |
| cnt[0] = (unsigned char)((i >> 24) & 255); |
| cnt[1] = (unsigned char)((i >> 16) & 255); |
| cnt[2] = (unsigned char)((i >> 8)) & 255; |
| cnt[3] = (unsigned char)(i & 255); |
| if (!EVP_DigestInit_ex(&c,dgst, NULL) |
| || !EVP_DigestUpdate(&c, seed, seedlen) |
| || !EVP_DigestUpdate(&c, cnt, 4)) |
| goto err; |
| if (outlen + mdlen <= len) |
| { |
| if (!EVP_DigestFinal_ex(&c, mask + outlen, NULL)) |
| goto err; |
| outlen += mdlen; |
| } |
| else |
| { |
| if (!EVP_DigestFinal_ex(&c, md, NULL)) |
| goto err; |
| memcpy(mask + outlen, md, len - outlen); |
| outlen = len; |
| } |
| } |
| rv = 0; |
| err: |
| EVP_MD_CTX_cleanup(&c); |
| return rv; |
| } |
| #endif |
| |
| static int MGF1(unsigned char *mask, long len, const unsigned char *seed, long seedlen, const EVP_MD* digest) |
| { |
| return PKCS1_MGF1(mask, len, seed, seedlen, digest); |
| } |
| |
| int RSA_padding_add_PKCS1_OAEP(unsigned char *to, int tlen, |
| const unsigned char *from, int flen, |
| const unsigned char *param, int plen, |
| const EVP_MD* digest, |
| const EVP_MD* mgf_digest) |
| { |
| int i, digestlen = EVP_MD_size(digest), emlen = tlen - 1; |
| unsigned char *db, *seed; |
| unsigned char *dbmask, seedmask[EVP_MAX_MD_SIZE]; // accomodate largest hash size |
| |
| if (flen > emlen - 2 * digestlen - 1) |
| { |
| RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, |
| RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE); |
| return 0; |
| } |
| |
| if (emlen < 2 * digestlen + 1) |
| { |
| RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, RSA_R_KEY_SIZE_TOO_SMALL); |
| return 0; |
| } |
| |
| to[0] = 0; |
| seed = to + 1; |
| db = to + digestlen + 1; |
| |
| if (!EVP_Digest((void *)param, plen, db, NULL, digest, NULL)) |
| return 0; |
| memset(db + digestlen, 0, |
| emlen - flen - 2 * digestlen - 1); |
| db[emlen - flen - digestlen - 1] = 0x01; |
| memcpy(db + emlen - flen - digestlen, from, (unsigned int) flen); |
| if (RAND_bytes(seed, digestlen) <= 0) |
| return 0; |
| |
| dbmask = (unsigned char*) OPENSSL_malloc(emlen - digestlen); |
| if (dbmask == NULL) |
| { |
| RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, ERR_R_MALLOC_FAILURE); |
| return 0; |
| } |
| |
| if (MGF1(dbmask, emlen - digestlen, seed, digestlen, mgf_digest) < 0) |
| return 0; |
| for (i = 0; i < emlen - digestlen; i++) |
| db[i] ^= dbmask[i]; |
| |
| if (MGF1(seedmask, digestlen, db, emlen - digestlen, mgf_digest) < 0) |
| return 0; |
| for (i = 0; i < digestlen; i++) |
| seed[i] ^= seedmask[i]; |
| |
| OPENSSL_free(dbmask); |
| return 1; |
| } |
| |
| int RSA_padding_check_PKCS1_OAEP(unsigned char *to, int tlen, |
| const unsigned char *from, int flen, int num, |
| const unsigned char *param, int plen, |
| const EVP_MD* digest, |
| const EVP_MD* mgf_digest) |
| { |
| int i, digestlen = EVP_MD_size(digest), dblen, mlen = -1; |
| const unsigned char *maskeddb; |
| int lzero; |
| unsigned char *db = NULL, seed[EVP_MAX_MD_SIZE], phash[EVP_MAX_MD_SIZE]; |
| unsigned char *padded_from; |
| int bad = 0; |
| |
| if (--num < 2 * digestlen + 1) |
| /* 'num' is the length of the modulus, i.e. does not depend on the |
| * particular ciphertext. */ |
| goto decoding_err; |
| |
| lzero = num - flen; |
| if (lzero < 0) |
| { |
| /* signalling this error immediately after detection might allow |
| * for side-channel attacks (e.g. timing if 'plen' is huge |
| * -- cf. James H. Manger, "A Chosen Ciphertext Attack on RSA Optimal |
| * Asymmetric Encryption Padding (OAEP) [...]", CRYPTO 2001), |
| * so we use a 'bad' flag */ |
| bad = 1; |
| lzero = 0; |
| flen = num; /* don't overflow the memcpy to padded_from */ |
| } |
| |
| dblen = num - digestlen; |
| db = (unsigned char*) OPENSSL_malloc(dblen + num); |
| if (db == NULL) |
| { |
| RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP, ERR_R_MALLOC_FAILURE); |
| return -1; |
| } |
| |
| /* Always do this zero-padding copy (even when lzero == 0) |
| * to avoid leaking timing info about the value of lzero. */ |
| padded_from = db + dblen; |
| memset(padded_from, 0, lzero); |
| memcpy(padded_from + lzero, from, flen); |
| |
| maskeddb = padded_from + digestlen; |
| |
| if (MGF1(seed, digestlen, maskeddb, dblen, mgf_digest)) |
| return -1; |
| for (i = 0; i < digestlen; i++) |
| seed[i] ^= padded_from[i]; |
| |
| if (MGF1(db, dblen, seed, digestlen, mgf_digest)) |
| return -1; |
| for (i = 0; i < dblen; i++) |
| db[i] ^= maskeddb[i]; |
| |
| if (!EVP_Digest((void *)param, plen, phash, NULL, digest, NULL)) |
| return -1; |
| |
| if (memcmp(db, phash, digestlen) != 0 || bad) |
| goto decoding_err; |
| else |
| { |
| for (i = digestlen; i < dblen; i++) |
| if (db[i] != 0x00) |
| break; |
| if (i == dblen || db[i] != 0x01) |
| goto decoding_err; |
| else |
| { |
| /* everything looks OK */ |
| |
| mlen = dblen - ++i; |
| if (tlen < mlen) |
| { |
| RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP, RSA_R_DATA_TOO_LARGE); |
| mlen = -1; |
| } |
| else |
| memcpy(to, db + i, mlen); |
| } |
| } |
| OPENSSL_free(db); |
| return mlen; |
| |
| decoding_err: |
| /* to avoid chosen ciphertext attacks, the error message should not reveal |
| * which kind of decoding error happened */ |
| RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP, RSA_R_OAEP_DECODING_ERROR); |
| if (db != NULL) OPENSSL_free(db); |
| return -1; |
| } |
| |
| }; |
| |
| const EVP_MD* getDigestFromHashType(XSECCryptoHash::HashType type) { |
| |
| const EVP_MD* evp_md = NULL; |
| |
| switch (type) { |
| case XSECCryptoHash::HASH_SHA1: |
| evp_md = EVP_get_digestbyname("SHA1"); |
| break; |
| case XSECCryptoHash::HASH_SHA224: |
| evp_md = EVP_get_digestbyname("SHA224"); |
| break; |
| case XSECCryptoHash::HASH_SHA256: |
| evp_md = EVP_get_digestbyname("SHA256"); |
| break; |
| case XSECCryptoHash::HASH_SHA384: |
| evp_md = EVP_get_digestbyname("SHA384"); |
| break; |
| case XSECCryptoHash::HASH_SHA512: |
| evp_md = EVP_get_digestbyname("SHA512"); |
| break; |
| |
| default: |
| ; |
| } |
| |
| return evp_md; |
| } |
| |
| |
| OpenSSLCryptoKeyRSA::OpenSSLCryptoKeyRSA() : |
| mp_rsaKey(NULL), |
| mp_accumE(NULL), |
| mp_accumN(NULL) |
| { |
| }; |
| |
| OpenSSLCryptoKeyRSA::~OpenSSLCryptoKeyRSA() { |
| // If we have a RSA, delete it (OpenSSL will clear the memory) |
| |
| if (mp_rsaKey) |
| RSA_free(mp_rsaKey); |
| |
| if (mp_accumE) |
| BN_free(mp_accumE); |
| |
| if (mp_accumN) |
| BN_free(mp_accumN); |
| }; |
| |
| const XMLCh* OpenSSLCryptoKeyRSA::getProviderName() const { |
| return DSIGConstants::s_unicodeStrPROVOpenSSL; |
| } |
| |
| |
| // Generic key functions |
| |
| XSECCryptoKey::KeyType OpenSSLCryptoKeyRSA::getKeyType() const { |
| |
| // Find out what we have |
| if (mp_rsaKey == NULL) |
| return KEY_NONE; |
| |
| const BIGNUM *n, *d; |
| RSA_get0_key(mp_rsaKey, &n, NULL, &d); |
| |
| if (n != NULL && d != NULL) |
| return KEY_RSA_PAIR; |
| |
| if (d != NULL) |
| return KEY_RSA_PRIVATE; |
| |
| if (n != NULL) |
| return KEY_RSA_PUBLIC; |
| |
| return KEY_NONE; |
| } |
| |
| void OpenSSLCryptoKeyRSA::loadPublicModulusBase64BigNums(const char* b64, unsigned int len) { |
| setNBase(OpenSSLCryptoBase64::b642BN((char *) b64, len)); |
| } |
| |
| void OpenSSLCryptoKeyRSA::setNBase(BIGNUM *nBase) { |
| |
| if (mp_rsaKey == NULL) |
| mp_rsaKey = RSA_new(); |
| |
| #if (OPENSSL_VERSION_NUMBER < 0x10100000L) |
| |
| mp_rsaKey->n = nBase; |
| |
| #else |
| |
| if (mp_accumN) |
| BN_free(mp_accumN); |
| |
| mp_accumN = nBase; |
| commitEN(); |
| #endif |
| } |
| |
| |
| void OpenSSLCryptoKeyRSA::loadPublicExponentBase64BigNums(const char* b64, unsigned int len) { |
| setEBase(OpenSSLCryptoBase64::b642BN((char *) b64, len)); |
| } |
| |
| |
| void OpenSSLCryptoKeyRSA::setEBase(BIGNUM *eBase) { |
| |
| if (mp_rsaKey == NULL) |
| mp_rsaKey = RSA_new(); |
| |
| #if (OPENSSL_VERSION_NUMBER < 0x10100000L) |
| mp_rsaKey->e = eBase; |
| #else |
| |
| if (mp_accumE) |
| BN_free(mp_accumE); |
| |
| mp_accumE = eBase; |
| commitEN(); |
| #endif |
| } |
| |
| #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) |
| void OpenSSLCryptoKeyRSA::commitEN() { |
| |
| if (NULL == mp_accumN || NULL == mp_accumE) |
| return; |
| |
| |
| RSA_set0_key(mp_rsaKey, mp_accumN, mp_accumE, NULL); |
| |
| mp_accumN = NULL; |
| mp_accumE = NULL; |
| } |
| #endif |
| |
| // "Hidden" OpenSSL functions |
| |
| OpenSSLCryptoKeyRSA::OpenSSLCryptoKeyRSA(EVP_PKEY *k) : |
| mp_rsaKey(NULL), |
| mp_accumE(NULL), |
| mp_accumN(NULL) |
| { |
| |
| // Create a new key to be loaded as we go |
| |
| mp_rsaKey = RSA_new(); |
| |
| if (k == NULL || EVP_PKEY_id(k) != EVP_PKEY_RSA) |
| return; // Nothing to do with us |
| |
| const RSA *rsa = EVP_PKEY_get0_RSA(k); |
| |
| const BIGNUM *n=NULL, *e=NULL, *d=NULL; |
| RSA_get0_key(rsa, &n, &e, &d); |
| if (n && e) // Do not dup unless setter will work |
| RSA_set0_key(mp_rsaKey, DUP_NON_NULL(n), DUP_NON_NULL(e), DUP_NON_NULL(d)); |
| |
| const BIGNUM *p=NULL, *q=NULL; |
| RSA_get0_factors(rsa, &p, &q); |
| if (p && q) |
| RSA_set0_factors(mp_rsaKey, DUP_NON_NULL(p), DUP_NON_NULL(q)); |
| |
| const BIGNUM *dmp1=NULL, *dmq1=NULL, *iqmp=NULL; |
| RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); |
| if (dmp1 && dmq1 && iqmp) |
| RSA_set0_crt_params(mp_rsaKey, DUP_NON_NULL(dmp1), DUP_NON_NULL(dmq1), DUP_NON_NULL(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, |
| XSECCryptoHash::HashType type) const { |
| |
| // 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"); |
| } |
| |
| XSECCryptoKey::KeyType keyType = getKeyType(); |
| if (keyType != KEY_RSA_PAIR && keyType != KEY_RSA_PUBLIC) { |
| throw XSECCryptoException(XSECCryptoException::RSAError, |
| "OpenSSL:RSA - Attempt to validate signature without public key"); |
| } |
| |
| char* cleanedBase64Signature; |
| unsigned int cleanedBase64SignatureLen = 0; |
| |
| cleanedBase64Signature = |
| XSECCryptoBase64::cleanBuffer(base64Signature, sigLen, cleanedBase64SignatureLen); |
| ArrayJanitor<char> j_cleanedBase64Signature(cleanedBase64Signature); |
| |
| int sigValLen; |
| unsigned char* sigVal = new unsigned char[sigLen + 1]; |
| ArrayJanitor<unsigned char> j_sigVal(sigVal); |
| |
| EvpEncodeCtxRAII dctx; |
| |
| if (!dctx.of()) { |
| throw XSECCryptoException(XSECCryptoException::RSAError, |
| "OpenSSL:RSA - allocation fail during Context Creation"); |
| } |
| |
| EVP_DecodeInit(dctx.of()); |
| int rc = EVP_DecodeUpdate(dctx.of(), |
| sigVal, |
| &sigValLen, |
| (unsigned char *) cleanedBase64Signature, |
| cleanedBase64SignatureLen); |
| |
| if (rc < 0) { |
| throw XSECCryptoException(XSECCryptoException::RSAError, |
| "OpenSSL:RSA - Error during Base64 Decode"); |
| } |
| int t = 0; |
| |
| EVP_DecodeFinal(dctx.of(), &sigVal[sigValLen], &t); |
| |
| sigValLen += t; |
| |
| // OpenSSL allows the signature size to be less than the key size. |
| // Java does not and the spec requires that this fail, so we have to |
| // perform this check. |
| |
| int keySize = RSA_size(mp_rsaKey); |
| if (keySize != sigValLen) { |
| throw XSECCryptoException(XSECCryptoException::RSAError, |
| "OpenSSL:RSA - Signature size does not match key size"); |
| } |
| |
| // 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) { |
| // Really - this is a failed signature check, not an exception! |
| return false; |
| } |
| |
| /* Check the OID */ |
| int oidLen = 0; |
| unsigned char * oid = getRSASigOID(type, 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, |
| XSECCryptoHash::HashType type) const { |
| |
| // 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"); |
| } |
| |
| KeyType keyType = getKeyType(); |
| if (keyType != KEY_RSA_PAIR && keyType != KEY_RSA_PRIVATE) { |
| throw XSECCryptoException(XSECCryptoException::RSAError, |
| "OpenSSL:RSA - Attempt to sign data without private 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(type, 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, |
| const XMLCh* hashURI, |
| const XMLCh* mgfURI, |
| unsigned char* params, |
| unsigned int paramslen) const { |
| |
| // 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 : |
| { |
| const EVP_MD* evp_md = getDigestFromHashType(XSECAlgorithmSupport::getHashType(hashURI)); |
| if (evp_md == NULL) { |
| throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm, |
| "OpenSSL:RSA - OAEP digest algorithm not supported"); |
| } |
| |
| |
| const EVP_MD* mgf_md = getDigestFromHashType(XSECAlgorithmSupport::getMGF1HashType(mgfURI)); |
| if (mgf_md == NULL) { |
| throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm, |
| "OpenSSL:RSA - OAEP MGF algorithm not supported"); |
| } |
| |
| unsigned char * tBuf; |
| int num = RSA_size(mp_rsaKey); |
| XSECnew(tBuf, unsigned char[num]); |
| 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, |
| params, |
| paramslen, |
| evp_md, |
| mgf_md); |
| |
| 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, |
| const XMLCh* hashURI, |
| const XMLCh* mgfURI, |
| unsigned char* params, |
| unsigned int paramslen) const { |
| |
| // 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 : |
| { |
| 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"); |
| } |
| |
| const EVP_MD* evp_md = getDigestFromHashType(XSECAlgorithmSupport::getHashType(hashURI)); |
| if (evp_md == NULL) { |
| throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm, |
| "OpenSSL:RSA - OAEP digest algorithm not supported"); |
| } |
| |
| |
| const EVP_MD* mgf_md = getDigestFromHashType(XSECAlgorithmSupport::getMGF1HashType(mgfURI)); |
| if (mgf_md == NULL) { |
| throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm, |
| "OpenSSL:RSA - OAEP MGF algorithm not supported"); |
| } |
| |
| 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, |
| params, |
| paramslen, |
| evp_md, |
| mgf_md); |
| |
| 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() 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(); |
| |
| // Duplicate parameters |
| const BIGNUM *n=NULL, *e=NULL, *d=NULL; |
| RSA_get0_key(mp_rsaKey, &n, &e, &d); |
| if (n && e) // Do not dup unless setter will work |
| RSA_set0_key(ret->mp_rsaKey, DUP_NON_NULL(n), DUP_NON_NULL(e), DUP_NON_NULL(d)); |
| |
| const BIGNUM *p=NULL, *q=NULL; |
| RSA_get0_factors(mp_rsaKey, &p, &q); |
| if (p && q) |
| RSA_set0_factors(ret->mp_rsaKey, DUP_NON_NULL(p), DUP_NON_NULL(q)); |
| |
| const BIGNUM *dmp1=NULL, *dmq1=NULL, *iqmp=NULL; |
| RSA_get0_crt_params(mp_rsaKey, &dmp1, &dmq1, &iqmp); |
| if (dmp1 && dmq1 && iqmp) |
| RSA_set0_crt_params(ret->mp_rsaKey, DUP_NON_NULL(dmp1), DUP_NON_NULL(dmq1), DUP_NON_NULL(iqmp)); |
| |
| return ret; |
| } |
| |
| #endif /* XSEC_HAVE_OPENSSL */ |