xsec/enc/NSS/NSSCryptoKeyRSA.cpp (426 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 * * NSSCryptoKeyRSA := RSA Keys * * Author(s): Milan Tomic * */ #include <xsec/enc/NSS/NSSCryptoKeyRSA.hpp> #include <xsec/enc/NSS/NSSCryptoProvider.hpp> #include <xsec/enc/XSCrypt/XSCryptCryptoBase64.hpp> #include <xsec/enc/XSECCryptoException.hpp> #include <xsec/framework/XSECError.hpp> #include <xercesc/util/Janitor.hpp> #if defined (XSEC_HAVE_NSS) #include <secder.h> #include <secdig.h> XSEC_USING_XERCES(ArrayJanitor); // -------------------------------------------------------------------------------- // Constructor // -------------------------------------------------------------------------------- NSSCryptoKeyRSA::NSSCryptoKeyRSA(SECKEYPublicKey * pubkey, SECKEYPrivateKey * privkey) { // NOTE - We OWN those handles mp_pubkey = pubkey; mp_privkey = privkey; mp_modulus = NULL; mp_exponent = NULL; }; // -------------------------------------------------------------------------------- // Destructor // -------------------------------------------------------------------------------- NSSCryptoKeyRSA::~NSSCryptoKeyRSA() { // Clean up if (mp_pubkey != 0) SECKEY_DestroyPublicKey(mp_pubkey); if (mp_privkey != 0) SECKEY_DestroyPrivateKey(mp_privkey); if (mp_modulus != NULL) SECITEM_FreeItem(mp_modulus, PR_TRUE); if (mp_exponent != NULL) SECITEM_FreeItem(mp_exponent, PR_TRUE); }; const XMLCh * NSSCryptoKeyRSA::getProviderName() const { return DSIGConstants::s_unicodeStrPROVNSS; } // -------------------------------------------------------------------------------- // Get key type // -------------------------------------------------------------------------------- XSECCryptoKey::KeyType NSSCryptoKeyRSA::getKeyType() const { // Find out what we have if (mp_pubkey == 0) { if (mp_privkey != 0) return KEY_RSA_PRIVATE; if (mp_exponent == NULL || mp_modulus == NULL) return KEY_NONE; else return KEY_RSA_PUBLIC; } if (mp_privkey != 0) return KEY_RSA_PAIR; return KEY_RSA_PUBLIC; } // -------------------------------------------------------------------------------- // Load modulus // -------------------------------------------------------------------------------- void NSSCryptoKeyRSA::loadPublicModulusBase64BigNums(const char* b64, unsigned int len) { if (mp_modulus != NULL) { SECITEM_FreeItem(mp_modulus, PR_TRUE); mp_modulus = NULL; // In case we get an exception } mp_modulus = NSSCryptoProvider::b642SI(b64, len); } // -------------------------------------------------------------------------------- // Load exponent // -------------------------------------------------------------------------------- void NSSCryptoKeyRSA::loadPublicExponentBase64BigNums(const char* b64, unsigned int len) { if (mp_exponent != NULL) { SECITEM_FreeItem(mp_exponent, PR_TRUE); mp_exponent = NULL; // In case we get an exception } mp_exponent = NSSCryptoProvider::b642SI(b64, len); } // -------------------------------------------------------------------------------- // Import key // -------------------------------------------------------------------------------- void NSSCryptoKeyRSA::importKey() const { if (mp_pubkey != 0 || mp_exponent == NULL || mp_modulus == NULL) return; PRArenaPool * arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if(arena == NULL) { throw XSECCryptoException(XSECCryptoException::GeneralError, "NSS:RSA Error attempting create new arena"); } mp_pubkey = (SECKEYPublicKey*)PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); if(mp_pubkey == NULL ) { PORT_FreeArena(arena, PR_FALSE); throw XSECCryptoException(XSECCryptoException::GeneralError, "NSS:RSA Error attempting create new arena"); } mp_pubkey->arena = arena; mp_pubkey->keyType = rsaKey; SECStatus s = SECITEM_CopyItem(arena, &(mp_pubkey->u.rsa.modulus), mp_modulus); if (s != SECSuccess) { PORT_FreeArena(arena, PR_FALSE); throw XSECCryptoException(XSECCryptoException::DSAError, "NSS:RSA Error attempting to import modulus"); } s = SECITEM_CopyItem(arena, &(mp_pubkey->u.rsa.publicExponent), mp_exponent); if (s != SECSuccess) { PORT_FreeArena(arena, PR_FALSE); throw XSECCryptoException(XSECCryptoException::DSAError, "NSS:RSA Error attempting to import exponent"); } } // -------------------------------------------------------------------------------- // Verify a signature encoded as a Base64 string // -------------------------------------------------------------------------------- bool NSSCryptoKeyRSA::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_pubkey == 0) { // Try to import from the parameters importKey(); if (mp_pubkey == 0) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Attempt to validate signature with empty key"); } } // Decode the signature unsigned char * rawSig; unsigned int rawSigLen; XSECnew(rawSig, unsigned char[sigLen]); ArrayJanitor<unsigned char> j_rawSig(rawSig); // Decode the signature XSCryptCryptoBase64 b64; b64.decodeInit(); rawSigLen = b64.decode((unsigned char *) base64Signature, sigLen, rawSig, sigLen); rawSigLen += b64.decodeFinish(&rawSig[rawSigLen], sigLen - rawSigLen); SECItem signature; signature.type = siBuffer; signature.data = rawSig; signature.len = rawSigLen; SECItem data; data.data = 0; data.len = 0; SECOidTag hashalg; PRArenaPool * arena = 0; SGNDigestInfo *di = 0; SECItem * res; switch (type) { case (XSECCryptoHash::HASH_MD5): hashalg = SEC_OID_MD5; break; case (XSECCryptoHash::HASH_SHA1): hashalg = SEC_OID_SHA1; break; case (XSECCryptoHash::HASH_SHA256): hashalg = SEC_OID_SHA256; break; case (XSECCryptoHash::HASH_SHA384): hashalg = SEC_OID_SHA384; break; case (XSECCryptoHash::HASH_SHA512): hashalg = SEC_OID_SHA512; break; default: throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Unsupported hash algorithm in RSA sign"); } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Error creating arena"); } di = SGN_CreateDigestInfo(hashalg, (unsigned char * )hashBuf, hashLen); if (di == NULL) { PORT_FreeArena(arena, PR_FALSE); throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Error creating digest info"); } res = SEC_ASN1EncodeItem(arena, &data, di, NSS_Get_sgn_DigestInfoTemplate(NULL, 0)); if (!res) { SGN_DestroyDigestInfo(di); PORT_FreeArena(arena, PR_FALSE); throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Error encoding digest info for RSA sign"); } // Verify signature SECStatus s = PK11_Verify(mp_pubkey, &signature, &data, NULL); return s == SECSuccess; } // -------------------------------------------------------------------------------- // Sign and encode result as a Base64 string // -------------------------------------------------------------------------------- unsigned int NSSCryptoKeyRSA::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_privkey == 0) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Attempt to sign data using a public or un-loaded key"); } unsigned char * rawSig; XSECnew(rawSig, unsigned char[base64SignatureBufLen]); ArrayJanitor<unsigned char> j_rawSig(rawSig); SECItem signature; signature.type = siBuffer; signature.data = rawSig; signature.len = base64SignatureBufLen; SECItem data; data.data = 0; SECOidTag hashalg; PRArenaPool * arena = 0; SGNDigestInfo *di = 0; SECItem * res; switch (type) { case (XSECCryptoHash::HASH_MD5): hashalg = SEC_OID_MD5; break; case (XSECCryptoHash::HASH_SHA1): hashalg = SEC_OID_SHA1; break; case (XSECCryptoHash::HASH_SHA256): hashalg = SEC_OID_SHA256; break; case (XSECCryptoHash::HASH_SHA384): hashalg = SEC_OID_SHA384; break; case (XSECCryptoHash::HASH_SHA512): hashalg = SEC_OID_SHA512; break; default: throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Unsupported hash algorithm in RSA sign"); } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Error creating arena"); } di = SGN_CreateDigestInfo(hashalg, hashBuf, hashLen); if (di == NULL) { PORT_FreeArena(arena, PR_FALSE); throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Error creating digest info"); } res = SEC_ASN1EncodeItem(arena, &data, di, NSS_Get_sgn_DigestInfoTemplate(NULL, 0)); if (!res) { SGN_DestroyDigestInfo(di); PORT_FreeArena(arena, PR_FALSE); throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Error encoding digest info for RSA sign"); } /* data.type = siBuffer; data.data = hashBuf; data.len = hashLen;*/ /* As of V1.3.1 - create a DigestInfo block */ SECStatus s = PK11_Sign(mp_privkey, &signature, &data); SGN_DestroyDigestInfo(di); PORT_FreeArena(arena, PR_FALSE); if (s != SECSuccess) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Error during signing operation"); } // Now encode XSCryptCryptoBase64 b64; b64.encodeInit(); unsigned int ret = b64.encode(signature.data, signature.len, (unsigned char *) base64SignatureBuf, base64SignatureBufLen); ret += b64.encodeFinish((unsigned char *) &base64SignatureBuf[ret], base64SignatureBufLen - ret); return ret; } // -------------------------------------------------------------------------------- // Clone key // -------------------------------------------------------------------------------- XSECCryptoKey * NSSCryptoKeyRSA::clone() const { NSSCryptoKeyRSA * ret; XSECnew(ret, NSSCryptoKeyRSA(mp_pubkey, mp_privkey)); if (mp_pubkey != 0) { ret->mp_pubkey = SECKEY_CopyPublicKey(mp_pubkey); if (ret->mp_pubkey == 0) { throw XSECCryptoException(XSECCryptoException::MemoryError, "NSS:RSA Error attempting to clone (copy) public key"); } } if (mp_privkey != 0) { ret->mp_privkey = SECKEY_CopyPrivateKey(mp_privkey); if (ret->mp_privkey == 0) { throw XSECCryptoException(XSECCryptoException::MemoryError, "NSS:RSA Error attempting to clone (copy) private key"); } } // Clone modulus if (mp_modulus != 0) { ret->mp_modulus = SECITEM_DupItem(mp_modulus); if (ret->mp_modulus == 0) { throw XSECCryptoException(XSECCryptoException::MemoryError, "NSS:RSA Error attempting to clone (copy) modulus"); } } // Clone exponent if (mp_exponent != 0) { ret->mp_exponent = SECITEM_DupItem(mp_exponent); if (ret->mp_exponent == 0) { throw XSECCryptoException(XSECCryptoException::MemoryError, "NSS:RSA Error attempting to clone (copy) exponent"); } } return ret; } // -------------------------------------------------------------------------------- // decrypt a buffer // -------------------------------------------------------------------------------- unsigned int NSSCryptoKeyRSA::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_privkey == 0) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Attempt to decrypt data with empty key"); } unsigned int decryptSize = inLength; SECStatus s; unsigned char *ptr = NULL; switch (padding) { case XSECCryptoKeyRSA::PAD_PKCS_1_5 : s = PK11_PubDecryptRaw(mp_privkey, plainBuf, (unsigned int*)&decryptSize, maxOutLength, (unsigned char*)inBuf, inLength); if (s != SECSuccess) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA privateKeyDecrypt - Error Decrypting PKCS1_5 padded RSA encrypt"); } //do the padding (http://www.w3.org/TR/xmlenc-core/#rsa-1_5) ptr = (unsigned char*) memchr(plainBuf, 0x02, decryptSize); if( ptr ) { unsigned int bytesToRemove = ((ptr-plainBuf)+1); memmove(plainBuf, ptr+1, decryptSize-bytesToRemove); decryptSize -= bytesToRemove; } ptr = (unsigned char*) memchr(plainBuf, 0x00, decryptSize); if( ptr ) { unsigned int bytesToRemove = ((ptr-plainBuf)+1); memmove(plainBuf, ptr+1, decryptSize-bytesToRemove); decryptSize -= bytesToRemove; } break; case XSECCryptoKeyRSA::PAD_OAEP : throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - OAEP padding method not supported in NSS yet"); break; default : throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Unknown padding method"); } return decryptSize; } // -------------------------------------------------------------------------------- // Encrypt a buffer // -------------------------------------------------------------------------------- unsigned int NSSCryptoKeyRSA::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_pubkey == 0) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Attempt to encrypt data with empty key"); } unsigned int encryptSize = SECKEY_PublicKeyStrength(mp_pubkey); if (maxOutLength < encryptSize) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Too small buffer for encrypted buffer output"); } SECStatus s; unsigned char * buf; XSECnew(buf, unsigned char[encryptSize]); ArrayJanitor<unsigned char> j_buf(buf); switch (padding) { case XSECCryptoKeyRSA::PAD_PKCS_1_5 : // do the padding (http://www.w3.org/TR/xmlenc-core/#rsa-1_5) { // generate random data for padding SECStatus s = PK11_GenerateRandom(buf, encryptSize); if (s != SECSuccess) { throw XSECException(XSECException::InternalError, "NSSCryptoKeyRSA() - Error generating Random data"); } // first byte have to be 0x02 buf[0] = 0x00; buf[1] = 0x02; // check that there are no 0x00 bytes among random data for (unsigned int i = 2; i < encryptSize - inLength - 1; i++) { while (buf[i] == 0x00) { // replace all 0x00 occurences in random data with random value PK11_GenerateRandom(&buf[i], 1); } } // before key add 0x00 byte buf[encryptSize - inLength - 1] = 0x00; // at the end of input buffer (to be encrypted) add key memcpy(&buf[encryptSize - inLength], inBuf, inLength); } // encrypt s = PK11_PubEncryptRaw(mp_pubkey, cipherBuf, (unsigned char*)buf, encryptSize, NULL); if (s != SECSuccess) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA publicKeyEncrypt - Error performing encrypt"); } break; case XSECCryptoKeyRSA::PAD_OAEP : throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - OAEP padding method not supported in NSS yet"); break; default : throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Unknown padding method"); } return encryptSize; } // -------------------------------------------------------------------------------- // Get key size in bytes // -------------------------------------------------------------------------------- unsigned int NSSCryptoKeyRSA::getLength(void) const { unsigned int ret = 0; if (mp_pubkey != 0) { ret = SECKEY_PublicKeyStrength(mp_pubkey); } else if (mp_privkey != 0) { ret = PK11_GetPrivateModulusLen(mp_privkey); } return ret; } // -------------------------------------------------------------------------------- // Load parameters from key (utility function) // -------------------------------------------------------------------------------- void NSSCryptoKeyRSA::loadParamsFromKey(void) { if (mp_pubkey == 0) return; mp_modulus = SECITEM_DupItem(&(mp_pubkey->u.rsa.modulus)); if (mp_modulus == 0) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Error during extracting modulus from public key"); } mp_exponent = SECITEM_DupItem(&(mp_pubkey->u.rsa.publicExponent)); if (mp_exponent == 0) { throw XSECCryptoException(XSECCryptoException::RSAError, "NSS:RSA - Error during extracting exponent from public key"); } } // -------------------------------------------------------------------------------- // Get exponent // -------------------------------------------------------------------------------- unsigned int NSSCryptoKeyRSA::getExponentBase64BigNums(char * b64, unsigned int len) { if (mp_pubkey == 0 && mp_exponent == 0) { return 0; // Nothing we can do } if (mp_exponent == NULL) { loadParamsFromKey(); } unsigned int bLen = 0; unsigned char * b = NSSCryptoProvider::SI2b64(mp_exponent, bLen); if (bLen > len) bLen = len; memcpy(b64, b, bLen); delete[] b; return bLen; } // -------------------------------------------------------------------------------- // Get modulus // -------------------------------------------------------------------------------- unsigned int NSSCryptoKeyRSA::getModulusBase64BigNums(char * b64, unsigned int len) { if (mp_pubkey == 0 && mp_modulus == 0) { return 0; // Nothing we can do } if (mp_modulus == NULL) { loadParamsFromKey(); } unsigned int bLen = 0; unsigned char * b = NSSCryptoProvider::SI2b64(mp_modulus, bLen); if (bLen > len) bLen = len; memcpy(b64, b, bLen); delete[] b; return bLen; } #endif /* XSEC_HAVE_NSS */