/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
//=--------------------------------------------------------------------------=
// security.cpp by Stanley Man-Kit Ho
//=--------------------------------------------------------------------------=
//
#include <jni.h>
#include <stdlib.h>
#include <windows.h>
#include <BaseTsd.h>
#include <wincrypt.h>
#include <stdio.h>
#define CERTIFICATE_PARSING_EXCEPTION \
#define INVALID_KEY_EXCEPTION \
extern "C" {
/*
* Throws an arbitrary Java exception.
* The exception message is a Windows system error message.
*/
{
szMessage[0] = '\0';
1024, NULL);
}
/*
* Maps the name of a hash algorithm to an algorithm identifier.
*/
}
if (pszHashAlgorithm)
return algId;
}
/*
* Returns a certificate chain context given a certificate context and key
* usage identifier.
*/
bool GetCertificateChain(LPSTR lpszKeyUsageIdentifier, PCCERT_CONTEXT pCertContext, PCCERT_CHAIN_CONTEXT* ppChainContext)
{
// Build a chain using CertGetCertificateChain
// and the certificate retrieved.
pCertContext, // pointer to the end certificate
NULL, // use the default time
NULL, // search no additional stores
&ChainPara, // use AND logic and enhanced key usage
// as indicated in the ChainPara
// data structure
NULL, // currently reserved
}
/////////////////////////////////////////////////////////////////////////////
//
/*
* Class: sun_security_mscapi_PRNG
* Method: generateSeed
* Signature: (I[B)[B
*/
{
{
// Acquire a CSP context.
if(::CryptAcquireContext(
NULL,
NULL,
CRYPT_VERIFYCONTEXT) == FALSE)
{
}
/*
* If length is negative then use the supplied seed to re-seed the
* generator and return null.
* If length is non-zero then generate a new seed according to the
* requested length and return the new seed.
* If length is zero then overwrite the supplied seed with a new
* seed of the same length and return the seed.
*/
if (length < 0) {
if (::CryptGenRandom(
}
} else if (length > 0) {
if (::CryptGenRandom(
}
} else { // length == 0
if (::CryptGenRandom(
}
}
}
{
//--------------------------------------------------------------------
// Clean up.
if (reseedBytes)
if (pbData)
delete [] pbData;
if (seedBytes)
if (hCryptProv)
::CryptReleaseContext(hCryptProv, 0);
}
return result;
}
/*
* Class: sun_security_mscapi_KeyStore
* Method: loadKeysOrCertificateChains
*/
{
/**
* Certificate in cert store has enhanced key usage extension
* property (or EKU property) that is not part of the certificate itself. To determine
* if the certificate should be returned, both the enhanced key usage in certificate
* extension block and the extension property stored along with the certificate in
* certificate store should be examined. Otherwise, we won't be able to determine
* the proper key usage from the Java side because the information is not stored as
* part of the encoded certificate.
*/
{
// Open a system certificate store.
== NULL) {
}
// Determine clazz and method ID to generate certificate
"generateCertificate",
"([BLjava/util/Collection;)V");
// Determine method ID to generate certificate chain
"generateCertificateChain",
"(Ljava/lang/String;Ljava/util/Collection;Ljava/util/Collection;)V");
// Determine method ID to generate RSA certificate chain
"generateRSAKeyAndCertificateChain",
"(Ljava/lang/String;JJILjava/util/Collection;Ljava/util/Collection;)V");
// Use CertEnumCertificatesInStore to get the certificates
// from the open store. pCertContext must be reset to
// NULL to retrieve the first certificate in the store.
{
// Check if private key available - client authentication certificate
// must have private key available.
{
} else {
// Private key is available
// Skip certificate if cannot find private key
if (bGetUserKey == FALSE)
{
if (bCallerFreeProv)
continue;
}
// Set cipher mode to ECB
// If the private key is present in smart card, we may not be able to
// determine the key length by using the private key handle. However,
// determine the key length of the private key by using the public key
// in the certificate.
}
// Build certificate chain by using system certificate store.
// Add cert chain into collection for any key usage.
//
{
for (unsigned int i=0; i < pCertChainContext->cChain; i++)
{
// Found cert chain
// Create ArrayList to store certs in each chain
{
rgpChain->rgpElement[j];
// Retrieve the friendly name of the first certificate
// in the chain
if (j == 0) {
// If the cert's name cannot be retrieved then
// pszNameString remains set to NULL.
// (An alias name will be generated automatically
// when storing this cert in the keystore.)
// Get length of friendly name
NULL, 0)) > 1) {
// Found friendly name
pszNameString = new char[cchNameString];
}
}
// Allocate and populate byte array
(jbyte*) pbCertEncoded);
// Generate certificate from byte array and store into
// cert collection
}
if (bHasNoPrivateKey)
{
// Generate certificate chain and store into cert chain
// collection
}
else
{
// Determine key type: RSA or DSA
{
// Generate RSA certificate chain and store into cert
// chain collection
}
}
}
// Free cert chain
if (pCertChainContext)
}
}
}
{
if (hCertStore)
::CertCloseStore(hCertStore, 0);
if (pszCertStoreName)
if (pszNameString)
delete [] pszNameString;
}
}
/*
* Class: sun_security_mscapi_Key
* Method: cleanUp
* Signature: (JJ)V
*/
{
if (hCryptProv != NULL)
}
/*
* Class: sun_security_mscapi_RSASignature
* Method: signHash
*/
{
{
// Map hash algorithm
// Acquire a hash object handle.
{
// Failover to using the PROV_RSA_AES CSP
pbData[0] = '\0';
// Get name of the key container
// Acquire an alternative CSP handle
PROV_RSA_AES, 0) == FALSE)
{
}
// Acquire a hash object handle.
{
}
}
// Copy hash from Java to native buffer
// Set hash value in the hash object
{
}
// Determine key spec.
}
if (CALG_RSA_KEYX == dwAlgId) {
}
// Determine size of buffer
}
{
}
{
}
// Create new byte array
// Copy data from native buffer
jSignedHash = temp;
}
{
if (hCryptProvAlt)
::CryptReleaseContext(hCryptProvAlt, 0);
if (pSignedHashBuffer)
delete [] pSignedHashBuffer;
if (pHashBuffer)
delete [] pHashBuffer;
if (hHash)
}
return jSignedHash;
}
/*
* Class: sun_security_mscapi_RSASignature
* Method: verifySignedHash
*/
{
{
// Map hash algorithm
// Acquire a hash object handle.
== FALSE)
{
// Failover to using the PROV_RSA_AES CSP
pbData[0] = '\0';
// Get name of the key container
// Acquire an alternative CSP handle
PROV_RSA_AES, 0) == FALSE)
{
}
// Acquire a hash object handle.
{
}
}
// Copy hash and signedHash from Java to native buffer
// Set hash value in the hash object
== FALSE)
{
}
// For RSA, the hash encryption algorithm is normally the same as the
// public key algorithm, so AT_SIGNATURE is used.
// Verify the signature
{
}
}
{
if (hCryptProvAlt)
::CryptReleaseContext(hCryptProvAlt, 0);
if (pSignedHashBuffer)
delete [] pSignedHashBuffer;
if (pHashBuffer)
delete [] pHashBuffer;
if (hHash)
}
return result;
}
/*
* Class: sun_security_mscapi_RSAKeyPairGenerator
* Method: generateRSAKeyPair
*/
{
{
// Acquire a CSP context (create a new key container).
// Prefer a PROV_RSA_AES CSP, when available, due to its support
// for SHA-2-based signatures.
if (::CryptAcquireContext(
NULL,
CRYPT_NEWKEYSET) == FALSE)
{
// Failover to using the default CSP (PROV_RSA_FULL)
if (::CryptAcquireContext(
NULL,
CRYPT_NEWKEYSET) == FALSE)
{
}
}
// Generate an RSA keypair
if(::CryptGenKey(
{
}
// Get the method ID for the RSAKeyPair constructor
// Create a new RSA keypair
}
{
//--------------------------------------------------------------------
// Clean up.
if (pszKeyContainerName)
}
return keypair;
}
/*
* Class: sun_security_mscapi_Key
* Method: getContainerName
*/
{
pbData[0] = '\0';
&cbData,
0);
}
/*
* Class: sun_security_mscapi_Key
* Method: getKeyType
*/
{
if (CALG_RSA_SIGN == dwAlgId) {
} else if (CALG_RSA_KEYX == dwAlgId) {
} else {
}
}
}
}
/*
* Class: sun_security_mscapi_KeyStore
* Method: storeCertificate
*/
{
{
// Open a system certificate store.
}
// Copy encoding from Java to native buffer
// Create a certificate context from the encoded cert
}
// Set the certificate's friendly name
(BYTE *) pszCertAliasName
};
CERT_FRIENDLY_NAME_PROP_ID, 0, &friendlyName)) {
}
// Attach the certificate's private key (if supplied)
if (hCryptProv != 0 && hCryptKey != 0) {
// Get the name of the key container
if (! ::CryptGetProvParam(
NULL,
0)) {
}
pszContainerName = new char[dwDataLen];
if (! ::CryptGetProvParam(
(BYTE *) pszContainerName,
0)) {
}
// Convert to a wide char string
}
// Set the name of the key container
// Get the name of the provider
if (! ::CryptGetProvParam(
NULL,
0)) {
}
pszProviderName = new char[dwDataLen];
if (! ::CryptGetProvParam(
(BYTE *) pszProviderName,
0)) {
}
// Convert to a wide char string
}
// Set the name of the provider
// Get and set the type of the provider
if (! ::CryptGetProvParam(
0)) {
}
// Set no provider flags
keyProviderInfo.dwFlags = 0;
// Set no provider parameters
// Get the key's algorithm ID
if (! ::CryptGetKeyParam(
0)) {
}
// Set the key spec (using the algorithm ID).
switch (keyProviderInfo.dwKeySpec) {
case CALG_RSA_KEYX:
case CALG_DH_SF:
break;
case CALG_RSA_SIGN:
case CALG_DSS_SIGN:
break;
default:
}
}
}
// Import encoded certificate
{
}
}
{
//--------------------------------------------------------------------
// Clean up.
if (hCertStore)
::CertCloseStore(hCertStore, 0);
if (pszCertStoreName)
if (pbCertEncoding)
delete [] pbCertEncoding;
if (pszCertAliasName)
delete [] pszCertAliasName;
if (pszContainerName)
delete [] pszContainerName;
if (pwszContainerName)
delete [] pwszContainerName;
if (pszProviderName)
delete [] pszProviderName;
if (pwszProviderName)
delete [] pwszProviderName;
if (pCertContext)
}
}
/*
* Class: sun_security_mscapi_KeyStore
* Method: removeCertificate
*/
{
// Open a system certificate store.
}
// Copy encoding from Java to native buffer
// Create a certificate context from the encoded cert
}
// Find the certificate to be deleted
}
// Check that its friendly name matches the supplied alias
pszNameString = new char[cchNameString];
// Compare the certificate's friendly name with supplied alias name
// Only delete the certificate if the alias names matches
if (! ::CertDeleteCertificateFromStore(pTBDCertContext)) {
// pTBDCertContext is always freed by the
// CertDeleteCertificateFromStore method
}
}
}
}
{
//--------------------------------------------------------------------
// Clean up.
if (hCertStore)
::CertCloseStore(hCertStore, 0);
if (pszCertStoreName)
if (pszCertAliasName)
if (pbCertEncoding)
delete [] pbCertEncoding;
if (pszNameString)
delete [] pszNameString;
if (pCertContext)
if (bDeleteAttempted && pTBDCertContext)
}
}
/*
* Class: sun_security_mscapi_KeyStore
* Method: destroyKeyContainer
*/
{
{
// Destroying the default key container is not permitted
// (because it may contain more one keypair).
if (pszKeyContainerName == NULL) {
}
// Acquire a CSP context (to the key container).
if (::CryptAcquireContext(
NULL,
CRYPT_DELETEKEYSET) == FALSE)
{
}
}
{
//--------------------------------------------------------------------
// Clean up.
if (pszKeyContainerName)
}
}
/*
* Class: sun_security_mscapi_RSACipher
* Method: findCertificateUsingAlias
*/
{
{
// Open a system certificate store.
}
// Use CertEnumCertificatesInStore to get the certificates
// from the open store. pCertContext must be reset to
// NULL to retrieve the first certificate in the store.
{
continue; // not found
}
pszNameString = new char[cchNameString];
if (::CertGetNameString(pCertContext,
cchNameString) == 1) {
continue; // not found
}
// Compare the certificate's friendly name with supplied alias name
delete [] pszNameString;
break;
} else {
delete [] pszNameString;
}
}
}
{
if (hCertStore)
::CertCloseStore(hCertStore, 0);
if (pszCertStoreName)
if (pszCertAliasName)
}
return (jlong) pCertContext;
}
/*
* Class: sun_security_mscapi_RSACipher
* Method: getKeyFromCert
* Signature: (JZ)J
*/
{
{
if (usePrivateKey == JNI_TRUE) {
// Locate the key container for the certificate's private key
if (!(::CryptAcquireCertificatePrivateKey(
}
// Get a handle to the private key
}
} else { // use public key
// Acquire a CSP context.
if(::CryptAcquireContext(
"J2SE",
NULL,
0) == FALSE)
{
// If CSP context hasn't been created, create one.
//
if (::CryptAcquireContext(
"J2SE",
NULL,
CRYPT_NEWKEYSET) == FALSE)
{
}
}
// Import the certificate's public key into the key container
&hKey))) {
}
}
}
{
//--------------------------------------------------------------------
// Clean up.
if (hCryptProv)
::CryptReleaseContext(hCryptProv, 0);
}
return hKey; // TODO - when finished with this key, call
// CryptDestroyKey(hKey)
}
/*
* Class: sun_security_mscapi_KeyStore
* Method: getKeyLength
* Signature: (J)I
*/
{
{
// Get key length (in bits)
//TODO - may need to use KP_BLOCKLEN instead?
0))) {
}
}
{
// no cleanup required
}
}
/*
* Class: sun_security_mscapi_RSACipher
* Method: encryptDecrypt
* Signature: ([BIJZ)[B
*/
{
DWORD i;
{
// Copy data from Java buffer to native buffer
// encrypt
}
// convert from little-endian
for (i = 0; i < dwBufLen / 2; i++) {
}
} else {
// convert to little-endian
for (i = 0; i < dwBufLen / 2; i++) {
}
// decrypt
&dwBufLen)) {
}
}
// Create new byte array
// Copy data from native buffer to Java buffer
}
{
if (pData)
delete [] pData;
}
return result;
}
/*
* Class: sun_security_mscapi_RSAPublicKey
* Method: getPublicKeyBlob
* Signature: (J)[B
*/
{
// Determine the size of the blob
&dwBlobLen)) {
}
// Generate key blob
}
// Create new byte array
// Copy data from native buffer to Java buffer
}
{
if (pbKeyBlob)
delete [] pbKeyBlob;
}
return blob;
}
/*
* Class: sun_security_mscapi_RSAPublicKey
* Method: getExponent
* Signature: ([B)[B
*/
__try {
// Check BLOB type
}
// convert from little-endian while copying from blob
}
}
{
if (keyBlob)
if (exponentBytes)
delete [] exponentBytes;
}
return exponent;
}
/*
* Class: sun_security_mscapi_RSAPublicKey
* Method: getModulus
* Signature: ([B)[B
*/
__try {
// Check BLOB type
}
// convert from little-endian while copying from blob
modulusBytes[i] = pbModulus[j];
}
}
{
if (keyBlob)
if (modulusBytes)
delete [] modulusBytes;
}
return modulus;
}
/*
* Convert an array in big-endian byte order into little-endian byte order.
*/
int destinationLength) {
int count = 0;
if (sourceLength < destinationLength) {
return -1;
}
// Copy bytes from the end of the source array to the beginning of the
// destination array (until the destination array is full).
// This ensures that the sign byte from the source array will be excluded.
for (int i = 0; i < destinationLength; i++) {
count++;
}
if (sourceBytes)
return count;
}
/*
* The Microsoft Base Cryptographic Provider supports public-key BLOBs
* that have the following format:
*
* PUBLICKEYSTRUC publickeystruc;
* RSAPUBKEY rsapubkey;
* BYTE modulus[rsapubkey.bitlen/8];
*
* and private-key BLOBs that have the following format:
*
* PUBLICKEYSTRUC publickeystruc;
* RSAPUBKEY rsapubkey;
* BYTE modulus[rsapubkey.bitlen/8];
* BYTE prime1[rsapubkey.bitlen/16];
* BYTE prime2[rsapubkey.bitlen/16];
* BYTE exponent1[rsapubkey.bitlen/16];
* BYTE exponent2[rsapubkey.bitlen/16];
* BYTE coefficient[rsapubkey.bitlen/16];
* BYTE privateExponent[rsapubkey.bitlen/8];
*
* This method generates such BLOBs from the key elements supplied.
*/
{
// Determine whether to generate a public-key or a private-key BLOB
if (jPrivateExponent != NULL &&
jExponentP != NULL &&
jExponentQ != NULL &&
jCrtCoefficient != NULL) {
jBlobLength = sizeof(BLOBHEADER) +
sizeof(RSAPUBKEY) +
(jKeyBitLength / 16);
} else {
jBlobLength = sizeof(BLOBHEADER) +
sizeof(RSAPUBKEY) +
(jKeyBitLength / 8);
}
__try {
if (bGeneratePrivateKeyBlob) {
} else {
}
if (bGeneratePrivateKeyBlob) {
} else {
}
// Sanity check
}
// The length argument must be the smaller of jPublicExponentLength
// and sizeof(pRsaPubKey->pubkey)
// Modulus n
if (bGeneratePrivateKeyBlob) {
// Prime p
jKeyByteLength / 2);
// Prime q
jKeyByteLength / 2);
// Prime exponent p
// Prime exponent q
// CRT coefficient
// Private exponent
}
}
{
if (jBlobBytes)
delete [] jBlobBytes;
}
return jBlob;
}
/*
* Class: sun_security_mscapi_KeyStore
* Method: generatePrivateKeyBlob
* Signature: (I[B[B[B[B[B[B[B[B)[B
*/
{
}
/*
* Class: sun_security_mscapi_RSASignature
* Method: generatePublicKeyBlob
* Signature: (I[B[B)[B
*/
{
}
/*
* Class: sun_security_mscapi_KeyStore
* Method: storePrivateKey
*/
{
{
// Acquire a CSP context (create a new key container).
if (::CryptAcquireContext(
NULL,
CRYPT_NEWKEYSET) == FALSE)
{
}
// Import the private key
if (::CryptImportKey(
0,
{
}
// Get the method ID for the RSAPrivateKey constructor
// Create a new RSA private key
}
{
//--------------------------------------------------------------------
// Clean up.
if (pszKeyContainerName)
if (pbKeyBlob)
}
return privateKey;
}
/*
* Class: sun_security_mscapi_RSASignature
* Method: importPublicKey
* Signature: ([BI)Lsun/security/mscapi/RSAPublicKey;
*/
{
{
// Acquire a CSP context (create a new key container).
// Prefer a PROV_RSA_AES CSP, when available, due to its support
// for SHA-2-based signatures.
if (::CryptAcquireContext(
NULL,
NULL,
CRYPT_VERIFYCONTEXT) == FALSE)
{
// Failover to using the default CSP (PROV_RSA_FULL)
if (::CryptAcquireContext(
NULL,
NULL,
CRYPT_VERIFYCONTEXT) == FALSE)
{
}
}
// Import the public key
if (::CryptImportKey(
0,
{
}
// Get the method ID for the RSAPublicKey constructor
// Create a new RSA public key
}
{
//--------------------------------------------------------------------
// Clean up.
if (pbKeyBlob)
}
return publicKey;
}
} /* extern "C" */