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