xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.cpp (270 lines of code) (raw):

/** * 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 */