blob: ed1351772cbc1e8da8f79a5f9d9866bde8e09357 [file] [log] [blame]
/**
* 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
*
* OpenSSLCryptoKeyDSA := DSA Keys
*
* Author(s): Berin Lautenbach
*
* $Id$
*
*/
#include <xsec/framework/XSECDefs.hpp>
#if defined (XSEC_HAVE_OPENSSL)
#include <xsec/enc/OpenSSL/OpenSSLSupport.hpp>
#include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
#include <xsec/enc/OpenSSL/OpenSSLCryptoBase64.hpp>
#include <xsec/enc/XSECCryptoException.hpp>
#include <xsec/enc/XSECCryptoUtils.hpp>
#include <xsec/framework/XSECError.hpp>
#include <xercesc/util/Janitor.hpp>
XSEC_USING_XERCES(ArrayJanitor);
#include <openssl/dsa.h>
// Inline for older OpenSSL versions.
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
static int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
{
int bytes = BN_num_bytes(a);
if (bytes > tolen) {
return -1;
}
while (bytes < tolen) {
*to++ = 0;
bytes++;
}
BN_bn2bin(a, to);
return tolen;
}
#endif
OpenSSLCryptoKeyDSA::OpenSSLCryptoKeyDSA() : mp_dsaKey(NULL), mp_accumP(NULL), mp_accumQ(NULL), mp_accumG(NULL) {
};
OpenSSLCryptoKeyDSA::~OpenSSLCryptoKeyDSA() {
// If we have a DSA, delete it
// OpenSSL will ensure the memory holding any private key is freed.
if (mp_dsaKey)
DSA_free(mp_dsaKey);
if (mp_accumG)
BN_free(mp_accumG);
if (mp_accumP)
BN_free(mp_accumP);
if (mp_accumQ)
BN_free(mp_accumQ);
};
const XMLCh* OpenSSLCryptoKeyDSA::getProviderName() const {
return DSIGConstants::s_unicodeStrPROVOpenSSL;
}
// Generic key functions
XSECCryptoKey::KeyType OpenSSLCryptoKeyDSA::getKeyType() const {
// Find out what we have
if (mp_dsaKey == NULL)
return KEY_NONE;
if (DSA_get0_privkey(mp_dsaKey) != NULL && DSA_get0_pubkey(mp_dsaKey) != NULL)
return KEY_DSA_PAIR;
if (DSA_get0_privkey(mp_dsaKey) != NULL)
return KEY_DSA_PRIVATE;
if (DSA_get0_pubkey(mp_dsaKey) != NULL)
return KEY_DSA_PUBLIC;
return KEY_NONE;
}
void OpenSSLCryptoKeyDSA::loadPBase64BigNums(const char * b64, unsigned int len) {
setPBase(OpenSSLCryptoBase64::b642BN((char *) b64, len));
}
void OpenSSLCryptoKeyDSA::setPBase(BIGNUM * p) {
if (mp_dsaKey == NULL)
mp_dsaKey = DSA_new();
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
// Do it immediately
mp_dsaKey->p = p;
#else
// Save it for later
if (mp_accumP != NULL)
BN_free(mp_accumP);
mp_accumP = p;
commitPQG();
#endif
}
void OpenSSLCryptoKeyDSA::loadQBase64BigNums(const char * b64, unsigned int len) {
setQBase(OpenSSLCryptoBase64::b642BN((char *) b64, len));
}
void OpenSSLCryptoKeyDSA::setQBase(BIGNUM * q) {
if (mp_dsaKey == NULL)
mp_dsaKey = DSA_new();
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
mp_dsaKey->q = q;
#else
if (mp_accumQ != NULL)
BN_free(mp_accumQ);
mp_accumQ = q;
commitPQG();
#endif
}
void OpenSSLCryptoKeyDSA::loadGBase64BigNums(const char * b64, unsigned int len) {
setGBase(OpenSSLCryptoBase64::b642BN((char *) b64, len));
}
void OpenSSLCryptoKeyDSA::setGBase(BIGNUM * g) {
if (mp_dsaKey == NULL)
mp_dsaKey = DSA_new();
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
mp_dsaKey->g = g;
#else
if (mp_accumG != NULL)
BN_free(mp_accumG);
mp_accumG = g;
commitPQG();
#endif
}
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
void OpenSSLCryptoKeyDSA::commitPQG() {
if (mp_accumP != NULL && mp_accumQ != NULL && mp_accumG != NULL) {
DSA_set0_pqg(mp_dsaKey, mp_accumP, mp_accumQ, mp_accumG);
mp_accumP = NULL;
mp_accumQ = NULL;
mp_accumG = NULL;
}
}
#endif
void OpenSSLCryptoKeyDSA::loadYBase64BigNums(const char * b64, unsigned int len) {
if (mp_dsaKey == NULL)
mp_dsaKey = DSA_new();
BIGNUM *newPub = OpenSSLCryptoBase64::b642BN((char *) b64, len);
const BIGNUM *oldPriv;
DSA_get0_key(mp_dsaKey, NULL, &oldPriv);
DSA_set0_key(mp_dsaKey, newPub, (oldPriv?BN_dup(oldPriv):NULL));
}
void OpenSSLCryptoKeyDSA::loadJBase64BigNums(const char * b64, unsigned int len) {
if (mp_dsaKey == NULL)
mp_dsaKey = DSA_new();
// Do nothing
}
// "Hidden" OpenSSL functions
OpenSSLCryptoKeyDSA::OpenSSLCryptoKeyDSA(EVP_PKEY *k) : mp_accumP(NULL), mp_accumQ(NULL), mp_accumG(NULL) {
// Create a new key to be loaded as we go
mp_dsaKey = DSA_new();
mp_accumG = NULL;
mp_accumP = NULL;
mp_accumQ = NULL;
if (k == NULL || EVP_PKEY_id(k) != EVP_PKEY_DSA)
return; // Nothing to do with us
const BIGNUM *otherP = NULL, *otherQ = NULL, *otherG = NULL;
DSA_get0_pqg(EVP_PKEY_get0_DSA(k), &otherP, &otherQ, &otherG);
if (otherP != NULL && otherQ != NULL && otherG != NULL) {
DSA_set0_pqg(mp_dsaKey, BN_dup(otherP), BN_dup(otherQ), BN_dup(otherG));
}
const BIGNUM *otherPriv = NULL, *otherPub = NULL;
DSA_get0_key(EVP_PKEY_get0_DSA(k), &otherPub, &otherPriv);
if (otherPub != NULL) {
BIGNUM *newPriv = NULL;
if (otherPriv != NULL)
newPriv = BN_dup(otherPriv);
DSA_set0_key(mp_dsaKey, BN_dup(otherPub), newPriv);
}
}
// --------------------------------------------------------------------------------
// Verify a signature encoded as a Base64 string
// --------------------------------------------------------------------------------
bool OpenSSLCryptoKeyDSA::verifyBase64Signature(unsigned char * hashBuf,
unsigned int hashLen,
char * base64Signature,
unsigned int sigLen) const {
// Use the currently loaded key to validate the Base64 encoded signature
if (mp_dsaKey == NULL) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - Attempt to validate signature with empty key");
}
XSECCryptoKey::KeyType keyType = getKeyType();
if (keyType != KEY_DSA_PAIR && keyType != KEY_DSA_PUBLIC) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - 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::ECError,
"OpenSSL:DSA - 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::DSAError,
"OpenSSL:DSA - Error during Base64 Decode");
}
int t = 0;
EVP_DecodeFinal(dctx.of(), &sigVal[sigValLen], &t);
sigValLen += t;
// Translate to BNs and thence to DSA_SIG
BIGNUM * R;
BIGNUM * S;
if (sigValLen == 40) {
R = BN_bin2bn(sigVal, 20, NULL);
S = BN_bin2bn(&sigVal[20], 20, NULL);
}
else {
unsigned char rb[20];
unsigned char sb[20];
if (sigValLen == 46 && ASN2DSASig(sigVal, rb, sb) == true) {
R = BN_bin2bn(rb, 20, NULL);
S = BN_bin2bn(sb, 20, NULL);
}
else {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - Signature Length incorrect");
}
}
DSA_SIG * dsa_sig = DSA_SIG_new();
DSA_SIG_set0(dsa_sig, BN_dup(R), BN_dup(S));
BN_free(R);
BN_free(S);
// Now we have a signature and a key - lets check
int err = DSA_do_verify(hashBuf, hashLen, dsa_sig, mp_dsaKey);
DSA_SIG_free(dsa_sig);
if (err < 0) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - Error validating signature");
}
return (err == 1);
}
// --------------------------------------------------------------------------------
// Sign and encode result as a Base64 string
// --------------------------------------------------------------------------------
unsigned int OpenSSLCryptoKeyDSA::signBase64Signature(unsigned char * hashBuf,
unsigned int hashLen,
char * base64SignatureBuf,
unsigned int base64SignatureBufLen) const {
// Sign a pre-calculated hash using this key
if (mp_dsaKey == NULL) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - Attempt to sign data with empty key");
}
KeyType keyType = getKeyType();
if (keyType != KEY_DSA_PAIR && keyType != KEY_DSA_PRIVATE) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - Attempt to sign data without private key");
}
DSA_SIG* dsa_sig = DSA_do_sign(hashBuf, hashLen, mp_dsaKey);
if (dsa_sig == NULL) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - Error signing data");
}
// Now turn the signature into a base64 string
const BIGNUM *dsaSigR;
const BIGNUM *dsaSigS;
DSA_SIG_get0(dsa_sig, &dsaSigR, &dsaSigS);
const int DSAsigCompLen = 20; // XMLDSIG spec 6.4.1
unsigned char rawSigBuf[2*DSAsigCompLen];
if (BN_bn2binpad(dsaSigR, rawSigBuf, DSAsigCompLen) <= 0) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - Error converting signature to raw buffer");
}
if (BN_bn2binpad(dsaSigS, rawSigBuf+DSAsigCompLen, DSAsigCompLen) <= 0) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - Error converting signature to raw buffer");
}
// 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 from Base64
BIO_write(b64, rawSigBuf, 2*DSAsigCompLen);
BIO_flush(b64);
unsigned int sigValLen = BIO_read(bmem, base64SignatureBuf, base64SignatureBufLen);
BIO_free_all(b64);
if (sigValLen <= 0) {
throw XSECCryptoException(XSECCryptoException::DSAError,
"OpenSSL:DSA - Error base64 encoding signature");
}
return sigValLen;
}
XSECCryptoKey * OpenSSLCryptoKeyDSA::clone() const {
OpenSSLCryptoKeyDSA * ret;
XSECnew(ret, OpenSSLCryptoKeyDSA);
ret->mp_dsaKey = DSA_new();
// Duplicate parameters
const BIGNUM *p=NULL, *q=NULL, *g=NULL;
DSA_get0_pqg(mp_dsaKey, &p, &q, &g);
if (p && q && g) // DSA_set0_pqg only works if all three params are non zero
DSA_set0_pqg(ret->mp_dsaKey, BN_dup(p), BN_dup(q), BN_dup(g));
const BIGNUM *oldPub= NULL, *oldPriv=NULL;
DSA_get0_key(mp_dsaKey, &oldPub, &oldPriv);
if (oldPub) {
// DSA_setKey requires non-null Public
DSA_set0_key(ret->mp_dsaKey, BN_dup(oldPub), (oldPriv?BN_dup(oldPriv):NULL));
}
return ret;
}
#endif /* XSEC_HAVE_OPENSSL */