//Sample code file: var/ndk/webBuildengine/tmp/viewable_samples/ce523e3a-5871-4fa6-9227-e51b7969ef46/CreateServerCert.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
#define MAX_KEY_GEN_SIZE 1024

NWRCODE CreateServerCertificate(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;
   nuint8 const      *selfsignedCert = NULL;
   nuint8 const      *serverCert = NULL;
   nuint8            *tempCert = NULL;
   nuint32            selfsignedCertSize;
   nuint32            serverCertSize;
   nuint32            numberOfCertsInList;
   
   // 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            certName[] = {'c','e','r','t','i','f','i','c','a','t','e',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[5] = {0};
   nuint8            myRegisteredID[] = {"\x06\x0B\x60\x86\x48\x01\x86\xF8\x37\x01\x02\x08\x28"}; // a Novell OID -- Note this alternative name may not be particularly useful

   unicode            myURI[] = {'h','t','t','p',':','/','/','w','w','w','.','c','o','m','p','a','n','y','.','c','o','m','/','s','e','r','v','e','r','/','s','e','r','v','e','r','n','a','m','e','.','h','t','m','l',0}; // A fake URI

   unicode            myDirectoryName[]   = {'.','C','N','=','S','e','r','v','e','r','N','a','m','e','.','O','=','c','o','n','t','e','x','t',0}; // A fake directory name

   unicode            myDNSName[]   = {'w','w','w','.','c','o','m','p','a','n','y','.','c','o','m',0}; // A fake DNS name

   nuint8            myIPAddress[] = {0xC0, 0xA8, 0x00, 0x02}; // Servers IP address 192.168.0.2



   // 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 server.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;
   }

   // Get a list of all the servers within the context

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

   if (numberOfServers < 1)
   {
      goto ERR_EXIT;
   }
   
   // Choose one of the servers (this is the server you are going to create a certificate for)

   ccode = NPKIServerNames (myPKI, 0, &keyGenSeverDN, NULL);
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }
   
   // Retrieve information about supported algorithms

   ccode = NPKIGetServerInfo
   (
      myPKI,
      keyGenSeverDN,
      PKI_SERVER_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;

   // 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_SERVER_INFO, 
      NULL,
      &signatureAlgorithms,
      &maxValidFromTime,
      &maxValidToTime,
      &caOperational,
      NULL,
      NULL,
      NULL,
      NULL
   );
   if (ccode != PKI_SUCCESS)
   {
      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;
   }
   
   if (caOperational != PKI_ORGANIZATIONAL_CA)   
   {
      ccode = PKI_E_CA_NOT_OPERATIONAL;
      goto ERR_EXIT;
   }   
         
   if (maxValidFromTime < currentServerTime)
      maxValidFromTime = currentServerTime;
      
   if (maxValidToTime > currentServerTime + (2 * SECONDS_IN_YEARS)) // approx 2 years -- no leap calculations

      maxValidToTime = currentServerTime + (2 * SECONDS_IN_YEARS);

   // Set up Subject Alternative names -- (optional extension)

   altName[0].type = X509_SUBJECT_ALT_NAME_IP_ADDRESS;
   altName[0].length = 4;   // get the number of bytes

   altName[0].value = (nuint8 *) myIPAddress;

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

   altName[1].value = (nuint8 *)myDNSName;

   altName[2].type = X509_SUBJECT_ALT_NAME_REGISTERED_ID;
   altName[2].length = 13;   // get the number of bytes

   altName[2].value = myRegisteredID;
   
   altName[3].type = X509_SUBJECT_ALT_NAME_DIRECTORY_NAME;
   altName[3].length = 2 * (unilen(myDirectoryName) + 1);   // get the number of bytes

   altName[3].value = (nuint8 *)myDirectoryName;
   
   altName[4].type = X509_SUBJECT_ALT_NAME_UNIFORM_RESOURCE_IDENTIFIER;
   altName[4].length = 2 * (unilen(myURI) + 1);   // get the number of bytes

   altName[4].value = (nuint8 *)myURI;
   
   subAltNames.flags = PKI_EXTENSION_INCLUDE;
   subAltNames.numberOfNames = 5;
   subAltNames.altName = altName;
   
#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;
   
   ccode = NPKICreateServerCertificate
   (
      myPKI,
      keyGenSeverDN,
      signingServerDN,
      certName, 
      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
   );
   
   if (ccode != PKI_SUCCESS)
   {
      goto ERR_EXIT;
   }

   if (keyGenCAStatus != PKI_ORGANIZATIONAL_CA) // If not the CA then we must store all the certs

   {
      // make sure the certificate list is cleared before using

      NPKICertificateList(myPKI, NULL, 0, PKI_CLEAR_CERTS, NULL);
      
      // get the object cert -- available because of the call to NPKICreateServerCertificate()

      ccode = NPKICertInfo(myPKI, &serverCertSize,   &serverCert   );
      if (ccode != PKI_SUCCESS)
      {
         goto ERR_EXIT;
      }
      // add the object cert to the list

      ccode = NPKICertificateList(myPKI, (nuint8 *)serverCert, serverCertSize, PKI_ADD_CERT, NULL);
      if (ccode != PKI_SUCCESS)
      {
         goto ERR_EXIT;
      }
      
      // get the CA's self-signed certifcate

      ccode = NPKIGetCACertificates  
      (
         myPKI,
         organizationalCADN,
         PKI_SELF_SIGNED_CERTIFICATE,
         NULL,
         NULL,
         &selfsignedCertSize,
         &selfsignedCert,
         NULL,
         NULL,
         NULL,
         NULL
      );
      if (ccode != PKI_SUCCESS)
      {
         goto ERR_EXIT;
      }
      
      ccode = NPKICertificateList
      (
         myPKI, 
         (nuint8 *)selfsignedCert, 
         selfsignedCertSize, 
         PKI_ADD_CERT | PKI_SORT_LIST, 
         &numberOfCertsInList
      );
      if (ccode != PKI_SUCCESS)
      {
         goto ERR_EXIT;
      }

      ccode = NPKIStoreServerCertificatesFromCertificateList
      (
         myPKI, 
         keyGenSeverDN, 
         certName, 
         0, 
         numberOfCertsInList,
         NULL, 
         NULL
      );
      if (ccode != PKI_SUCCESS)
      {
         goto ERR_EXIT;
      }
   }

   
ERR_EXIT:
   NPKIDSLogout(myPKI);
   
   if (myPKI != NPKI_INVALID_CONTEXT)
      NPKIFreeContext(myPKI);
   
   return ccode;
}