//Sample code file: var/ndk/webBuildengine/tmp/viewable_samples/ce523e3a-5871-4fa6-9227-e51b7969ef46/CreateUserCert.cpp

//Warning: This code has been marked up for HTML

/***************************************************************************
 Copyright (c) 1998-2002 Novell, Inc.  All Rights Reserved.

 THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
 TREATIES.  USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE 
 LICENSE AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK)
 THAT CONTAINS THIS WORK.

 Pursuant to the SDK License Agreement, Novell hereby grants to 
 Developer a royalty-free, non-exclusive license to include the
 sample code AES.C and derivative binaries in its product.
 Novell grants to Developer worldwide distribution rights to market,
 distribute or sell the sample code AES.C and derivative
 binaries as a component of Developer's product(s).  Novell shall 
 have no obligations to Developer or Developer's customers with 
 respect to this code.

DISCLAIMER:

   Novell, Inc. makes no representations or warranties with respect
to the contents or use of this code, and specifically disclaims any
express or implied warranties of merchantability or fitness for any
particular purpose.  Further, Novell, Inc. reserves the right to revise
this publication and to make changes to its content, at any time,
without obligation to notify any person or entity of such revisions or
changes.

   Further, Novell, Inc. makes no representations or warranties with
respect to any software, and specifically disclaims any express or
implied warranties of merchantability or fitness for any particular
purpose.  Further, Novell, Inc. reserves the right to make changes to
any and all parts of the software, at any time, without obligation to
notify any person or entity of such changes.                          

***************************************************************************/
#ifdef WIN32
#   include <windows.h>
#endif

#include "npki.h"
#include "pkierr.h"

#define SECONDS_IN_YEARS   365 * 24 * 60 * 60 // Approximate number of seconds in a year

#define MAX_KEY_GEN_SIZE   1024 // User defined maximum key size


NWRCODE CreateUserCertificate(void)
{
   NWRCODE            ccode = PKI_SUCCESS;
   NPKIContext         myPKI = NPKI_INVALID_CONTEXT;
   unicode   const      *organizationalCADN = NULL;
   unicode   const      *keyGenSeverDN   = NULL;
   unicode   const      *signingServerDN = NULL;
   nuint32            numberOfServers = 0;
   nuint32            currentServerTime   = 0;
   nuint32            keyGenerationAlgorithms = 0; 
   nuint32            signatureAlgorithms = 0;
   nuint32            maxValidFromTime = 0;
   nuint32            maxValidToTime   = 0;
   nuint32            caOperational = 0;
   nuint32            keyGenCAStatus = 0;
   nuint32            maxKeyEncryptKeySize = 0;
   nuint32            maxSignKeySize = 0;
   nuint32            maxKeySize = 0;
   
   // user/system infomation

   unicode            myTree[]   = {'T','E','S','T',0};
   unicode            myUser[]   = {'A','d','m','i','n','.','n','o','v','e','l','l',0};
   char               password[]   = {'t','e','s','t',0};
   char*               startIPAddress = "192.168.0.2";
   unicode            myNameContext[] = {'n','o','v','e','l','l',0};
   unicode            myNick[]   = {'n','i','c','k','n','a','m','e',0};
   unicode            myEmailName[] = {'u','s','e','r','@','c','o','m','p','a','n','y','.c','o','m',0};
   
   // certificate info

   nuint32            publicKeyFlags   = PUBLIC_KEY_TWO_SERVER;
   nuint16            sslKeyUsage = X509_KEY_USAGE_DIGITAL_SIGNATURE | X509_KEY_USAGE_KEY_ENCIPHERMENT;
   NPKI_Extension    keyUsage   = {0};
   NPKI_ExtAltNames   subAltNames = {0};
   NPKI_AltName        altName[1] = {0};


   // Before the certificate can be created, you must be logged in as the user

   // or logged in as someone with admin rights.  The certificate that is 

   // being created is being created for "user.context" with the nickname of

   // "nickname".


   ccode = NPKICreateContext(&myPKI);
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }
   
   // Set the tree name, this is the tree we will make all calls to.

     ccode = NPKISetTreeName(myPKI, myTree);    
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }
   ccode = NPKIConnectToIPAddress(myPKI, 0, 0, startIPAddress,   NULL,   NULL);
   
   // Login in

   ccode = NPKIDSLogin(myPKI, myUser, password);
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }

   // Find the Organiztional Certificate Authority 

   ccode = NPKIFindOrganizationalCA(myPKI, &organizationalCADN);
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }
   
   // Get the DN of the CA server

   // (server which hosts the Organiztional Certificate Authority)

   ccode = NPKIGetHostServerDN(myPKI, organizationalCADN, &signingServerDN);
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }

   ccode = NPKIGetServerUTCTime
   (
      myPKI,
      signingServerDN, 
      &currentServerTime
   );
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }
   
   // Get information from the CA server

   ccode = NPKIGetServerInfo
   (
      myPKI, 
      signingServerDN, 
      PKI_USER_INFO, 
      NULL,
      &signatureAlgorithms,
      &maxValidFromTime,
      &maxValidToTime,
      &caOperational,
      NULL,
      NULL,
      NULL,
      NULL
   );
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }

   if (caOperational != PKI_ORGANIZATIONAL_CA)
   {                                             
      ccode = PKI_E_CA_NOT_OPERATIONAL;
      goto ERR_EXIT;
   }   
   
   if (!(signatureAlgorithms & PKI_SIGN_WITH_RSA_AND_SHA1))   // (other algorithms could be used)

   {
      ccode = PKI_E_ALGORITHM_NOT_SUPPORTED;
      goto ERR_EXIT;
   }
   
   // Find servers to generate the key pair

   ccode = NPKIFindKeyGenServersForUser(myPKI, myNameContext, &numberOfServers);
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }

   if (numberOfServers < 1)
   {
      ccode = PKI_E_NOT_CONNECTED_TO_SERVICE;
      goto ERR_EXIT;
   }
   
   // Get the DN of the first server.

   ccode = NPKIServerNames (myPKI, 0, &keyGenSeverDN, NULL);
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }
   
   ccode = NPKIGetServerInfo
   (
      myPKI, 
      keyGenSeverDN, 
      PKI_USER_INFO, 
      &keyGenerationAlgorithms, 
      &signatureAlgorithms,
      NULL,
      NULL,
      &keyGenCAStatus,
      NULL,
      NULL,
      NULL,
      NULL
   );
   if (ccode != PKI_SUCCESS)
   {
       goto ERR_EXIT;
   }
   
   if (keyGenCAStatus == PKI_ORGANIZATIONAL_CA) // The Key Gen server is also the CA server

   {
      // change the flags for single-server mode

      publicKeyFlags = PUBLIC_KEY_SINGLE_SERVER; 
   }   
      
   if (!(keyGenerationAlgorithms & PKI_RSA_ALGORITHM)) // For this demo the server must support the RSA algorithm

   {                                                    // (other algorithms could be used)

      ccode = PKI_E_ALGORITHM_NOT_SUPPORTED;
      goto ERR_EXIT;
   }
         
   if (!(signatureAlgorithms & PKI_SIGN_WITH_RSA_AND_SHA1))   // (other algorithms could be used)

   {
      ccode = PKI_E_ALGORITHM_NOT_SUPPORTED;
      goto ERR_EXIT;
   }
   
   ccode = NPKIGetAlgorithmInfo
   (
      myPKI,
      PKI_RSA_ALGORITHM,
      &maxKeyEncryptKeySize,
      &maxSignKeySize,           
      NULL,                        // We don't need to get maxDataEncryptKeySize because 

      NULL,                        // we are not including data encryption in the key usages.

      NULL    
   );
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }
   
   maxKeySize = maxSignKeySize;
   
   if (maxKeyEncryptKeySize < maxKeySize)
      maxKeySize = maxKeyEncryptKeySize; 
                                         
   if (maxKeySize > MAX_KEY_GEN_SIZE) // This is the user defined max key size

      maxKeySize = MAX_KEY_GEN_SIZE;
      
   if (maxValidFromTime < currentServerTime)
      maxValidFromTime = currentServerTime;
      
   if (maxValidToTime > currentServerTime + (2 * SECONDS_IN_YEARS)) // approx 2 years -- no leap calculations

      maxValidToTime = currentServerTime + (2 * SECONDS_IN_YEARS);
      
   // Setup the optional Subject Alternative Names   extension

   altName[0].type = X509_SUBJECT_ALT_NAME_RFC822_NAME;
   altName[0].length = 2 * (unilen(myEmailName) + 1);   // get the number of bytes

   altName[0].value = (nuint8 *)myEmailName;
   
   subAltNames.flags = PKI_EXTENSION_INCLUDE;
   subAltNames.numberOfNames = 1;
   subAltNames.altName = altName;
   
   // Setup the optional Key Usage extension   

#ifndef HI_LO_MACH_TYPE // The key usage needs to be in Big Endian so swap

   {                     // the byte ordering if we are not on a HI LO machine type

      nuint16   newValue = 0;
      nuint8   *oldValuePtr = (nuint8 *)&sslKeyUsage;
      nuint8   *newValuePtr = (nuint8 *)&newValue;
      
      newValuePtr[0] = oldValuePtr[1];
      newValuePtr[1] = oldValuePtr[0];
      sslKeyUsage = newValue;
   }
#endif

   keyUsage.flags = PKI_EXTENSION_INCLUDE;  // Setup the key usages extension

   keyUsage.length = sizeof(sslKeyUsage);
   keyUsage.value = (nuint8 *) &sslKeyUsage;
   
   // Create the certificate

   ccode = NPKICreateUserCertificate
   (
      myPKI,
      keyGenSeverDN,
      signingServerDN,
      myUser, 
      myNick, 
      PKI_RSA_ALGORITHM,
      maxKeySize,
      NULL,
      PKI_SIGN_WITH_RSA_AND_SHA1,
      DEFAULT_YEAR_ENCODING, 
      maxValidFromTime, 
      maxValidToTime,
      publicKeyFlags,
      PRIVATE_KEY | PRIVATE_KEY_EXTRACTABLE,
      &keyUsage,
      NULL,
      &subAltNames,
      NULL,
      NULL,
      NULL,
      NULL
   );
   
ERR_EXIT:
   NPKIDSLogout(myPKI);
   
   if (myPKI != NPKI_INVALID_CONTEXT)
      NPKIFreeContext(myPKI);

   return ccode;
}