/** * 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 * * OpenSSLCryptoKeyEC := EC Keys * * Author(s): Scott Cantor * * $Id:$ * */ #include #if defined (XSEC_HAVE_OPENSSL) && defined (XSEC_OPENSSL_HAVE_EC) #include #include #include #include #include #include #include #include #include #include XSEC_USING_XERCES(Janitor); XSEC_USING_XERCES(ArrayJanitor); #include OpenSSLCryptoKeyEC::OpenSSLCryptoKeyEC() : mp_ecKey(NULL) { }; OpenSSLCryptoKeyEC::~OpenSSLCryptoKeyEC() { // If we have a EC_KEY, delete it // OpenSSL will ensure the memory holding any private key is freed. if (mp_ecKey) EC_KEY_free(mp_ecKey); }; const XMLCh* OpenSSLCryptoKeyEC::getProviderName() const { return DSIGConstants::s_unicodeStrPROVOpenSSL; } // Generic key functions XSECCryptoKey::KeyType OpenSSLCryptoKeyEC::getKeyType() const { // Find out what we have if (mp_ecKey == NULL) return KEY_NONE; if (EC_KEY_get0_private_key(mp_ecKey) && EC_KEY_get0_public_key(mp_ecKey)) return KEY_EC_PAIR; if (EC_KEY_get0_private_key(mp_ecKey)) return KEY_EC_PRIVATE; if (EC_KEY_get0_public_key(mp_ecKey)) return KEY_EC_PUBLIC; return KEY_NONE; } void OpenSSLCryptoKeyEC::loadPublicKeyBase64(const char* curveName, const char * buf, unsigned int len) { if (mp_ecKey) { EC_KEY_free(mp_ecKey); mp_ecKey = NULL; } EC_KEY* key = EC_KEY_new_by_curve_name(static_cast(XSECPlatformUtils::g_cryptoProvider)->curveNameToNID(curveName)); int bufLen = len; unsigned char * outBuf; XSECnew(outBuf, unsigned char[len + 1]); ArrayJanitor j_outBuf(outBuf); XSCryptCryptoBase64 *b64; XSECnew(b64, XSCryptCryptoBase64); Janitor j_b64(b64); b64->decodeInit(); bufLen = b64->decode((unsigned char *) buf, len, outBuf, len); bufLen += b64->decodeFinish(&outBuf[bufLen], len-bufLen); if (bufLen > 0) { if (o2i_ECPublicKey(&key, (const unsigned char **) &outBuf, bufLen) == NULL) { EC_KEY_free(key); key = NULL; } } if (key == NULL) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Error translating Base64 octets into OpenSSL EC_KEY structure"); } mp_ecKey = key; } // "Hidden" OpenSSL functions OpenSSLCryptoKeyEC::OpenSSLCryptoKeyEC(EVP_PKEY *k) { // Create a new key to be loaded as we go if (k == NULL || EVP_PKEY_id(k) != EVP_PKEY_EC) return; // Nothing to do with us mp_ecKey = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(k)); } // -------------------------------------------------------------------------------- // Verify a signature encoded as a Base64 string // -------------------------------------------------------------------------------- bool OpenSSLCryptoKeyEC::verifyBase64SignatureDSA(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_ecKey == NULL) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Attempt to validate signature with empty key"); } char * cleanedBase64Signature; unsigned int cleanedBase64SignatureLen = 0; cleanedBase64Signature = XSECCryptoBase64::cleanBuffer(base64Signature, sigLen, cleanedBase64SignatureLen); ArrayJanitor j_cleanedBase64Signature(cleanedBase64Signature); int sigValLen; unsigned char* sigVal = new unsigned char[sigLen + 1]; ArrayJanitor j_sigVal(sigVal); EvpEncodeCtxRAII dctx; if (!dctx.of()) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - 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::ECError, "OpenSSL:EC - Error during Base64 Decode"); } int t = 0; EVP_DecodeFinal(dctx.of(), &sigVal[sigValLen], &t); sigValLen += t; if (sigValLen <= 0 || sigValLen % 2 != 0) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Signature length was odd"); } // Translate to BNs by splitting in half, and thence to ECDSA_SIG ECDSA_SIG * ecdsa_sig = ECDSA_SIG_new(); BIGNUM *newR = BN_bin2bn(sigVal, sigValLen / 2, NULL); BIGNUM *newS = BN_bin2bn(&sigVal[sigValLen / 2], sigValLen / 2, NULL); ECDSA_SIG_set0(ecdsa_sig, newR, newS); // Now we have a signature and a key - lets check int err = ECDSA_do_verify(hashBuf, hashLen, ecdsa_sig, mp_ecKey); ECDSA_SIG_free(ecdsa_sig); if (err < 0) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Error validating signature"); } return (err == 1); } // -------------------------------------------------------------------------------- // Sign and encode result as a Base64 string // -------------------------------------------------------------------------------- unsigned int OpenSSLCryptoKeyEC::signBase64SignatureDSA(unsigned char * hashBuf, unsigned int hashLen, char * base64SignatureBuf, unsigned int base64SignatureBufLen) const { // Sign a pre-calculated hash using this key if (mp_ecKey == NULL) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Attempt to sign data with empty key"); } ECDSA_SIG * ecdsa_sig; ecdsa_sig = ECDSA_do_sign(hashBuf, hashLen, mp_ecKey); if (ecdsa_sig == NULL) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Error signing data"); } // To encode the signature properly, we need to know the "size of the // base point order of the curve in bytes", which seems to correspond to the // number of bits in the EC Group "order", using the OpenSSL API. // This is the size of the r and s values in the signature when converting them // to octet strings. The code below is cribbed from ECDSA_size. unsigned int keyLen = 0; const EC_GROUP* group = EC_KEY_get0_group(mp_ecKey); if (group) { BIGNUM* order = BN_new(); if (order) { if (EC_GROUP_get_order(group, order, NULL)) { keyLen = (BN_num_bits(order) + 7) / 8; // round up to byte size } BN_clear_free(order); } } if (keyLen == 0) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Error caclulating signature size"); } // Now turn the signature into a raw octet string, half r and half s. unsigned char* rawSigBuf = new unsigned char[keyLen * 2]; memset(rawSigBuf, 0, keyLen * 2); ArrayJanitor j_sigbuf(rawSigBuf); const BIGNUM *sigR; const BIGNUM *sigS; ECDSA_SIG_get0(ecdsa_sig, &sigR, &sigS); unsigned int rawLen = (BN_num_bits(sigR) + 7) / 8; if (BN_bn2bin(sigR, rawSigBuf + keyLen - rawLen) <= 0) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Error copying signature 'r' value to buffer"); } rawLen = (BN_num_bits(sigS) + 7) / 8; if (BN_bn2bin(sigS, rawSigBuf + keyLen + keyLen - rawLen) <= 0) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Error copying signature 's' value to 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); BIO_write(b64, rawSigBuf, keyLen * 2); BIO_flush(b64); unsigned int sigValLen = BIO_read(bmem, base64SignatureBuf, base64SignatureBufLen); BIO_free_all(b64); if (sigValLen <= 0) { throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL:EC - Error base64 encoding signature"); } return sigValLen; } XSECCryptoKey * OpenSSLCryptoKeyEC::clone() const { OpenSSLCryptoKeyEC * ret; XSECnew(ret, OpenSSLCryptoKeyEC); if (mp_ecKey) ret->mp_ecKey = EC_KEY_dup(mp_ecKey); return ret; } #endif /* XSEC_HAVE_OPENSSL */