netscape-revival
security/cmd/keyutil.c
#include "secutil.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <termios.h>
#if defined(LINUX)
#include <getopt.h>
#endif
static PRArenaPool *arena;
static char *progName;
#if defined(__sun) && !defined(SVR4)
extern int fclose(FILE*);
extern int fprintf(FILE *, char *, ...);
extern int getopt(int, char**, char*);
extern int isatty(int);
extern char *optarg;
extern char *sys_errlist[];
#define strerror(errno) sys_errlist[errno]
#endif
#define MIN_KEY_BITS 256
#define MAX_KEY_BITS 1024
#define NUM_KEYSTROKES 120
#define RAND_BUF_SIZE 60
#define SEC_KEY_DB_EXISTS 0
#define SEC_CREATE_KEY_DB 1
#define ERROR_BREAK rv = SECFailure;break;
static SECKEYKeyDBHandle *
OpenKeyDB(int createnew)
{
SECKEYKeyDBHandle *keyHandle;
keyHandle = SECU_OpenKeyDB();
if (keyHandle == NULL) {
SECU_PrintError(progName, "could not open key database");
return NULL;
}
return(keyHandle);
}
static RNGContext *
GetRNGContext(void)
{
RNGContext *rng;
SECStatus rv;
char *randbuf;
int fd, i, count;
char c;
struct termios tio;
cc_t orig_cc_min, orig_cc_time;
tcflag_t orig_lflag;
printf("\n");
printf("A random seed must be generated that will be used in the\n");
printf("creation of your key. One of the easiest ways to create a\n");
printf("random seed is to use the timing of keystrokes on a keyboard.\n");
printf("\n");
printf("To begin, type keys on the keyboard until this progress meter\n");
printf("is full. DO NOT USE THE AUTOREPEAT FUNCTION ON YOUR KEYBOARD!\n");
printf("\n");
printf("\n");
printf("Continue typing until the progress meter is full:\n\n");
printf("| |\r");
/* turn off echo on stdin & return on 1 char instead of NL */
fd = fileno(stdin);
tcgetattr(fd, &tio);
orig_lflag = tio.c_lflag;
orig_cc_min = tio.c_cc[VMIN];
orig_cc_time = tio.c_cc[VTIME];
tio.c_lflag &= ~ECHO;
tio.c_lflag &= ~ICANON;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
tcsetattr(fd, TCSAFLUSH, &tio);
/* Get random noise from keyboard strokes */
randbuf = (char *) PORT_Alloc(RAND_BUF_SIZE);
count = 0;
while (count < NUM_KEYSTROKES+1) {
c = getc(stdin);
RNG_GetNoise(&randbuf[1], sizeof(randbuf)-1);
RNG_RandomUpdate(randbuf, sizeof(randbuf));
if (c != randbuf[0]) {
randbuf[0] = c;
printf("\r|");
for (i=0; i<count/(NUM_KEYSTROKES/RAND_BUF_SIZE); i++) {
printf("*");
}
if (count%(NUM_KEYSTROKES/RAND_BUF_SIZE) == 1) printf("/");
count++;
}
}
free(randbuf);
printf("\n\n");
printf("Finished. Press enter to continue: ");
while (getc(stdin) != '\n')
;
/* set back termio the way it was */
tio.c_lflag = orig_lflag;
tio.c_cc[VMIN] = orig_cc_min;
tio.c_cc[VTIME] = orig_cc_time;
tcsetattr(fd, TCSAFLUSH, &tio);
rng = RNG_CreateContext();
if (!rng) goto loser;
return rng;
loser:
SECU_PrintError(progName, "unable to create key");
return NULL;
}
static SECStatus
GenerateRSAKey(char *nickname, int keyBits, int publicExponent)
{
RNGContext *rng;
SECStatus rv;
SECItem pe;
SECKEYLowPrivateKey *key;
SECKEYKeyDBHandle *handle;
/* open key database if it exists */
handle = OpenKeyDB(SEC_CREATE_KEY_DB);
if (handle == NULL) {
return(SECFailure);
}
/* New interface stuffs. */
printf("--------------------------------------------------------\n");
printf("| Netscape Communications Corporation |\n");
printf("| Key Generation |\n");
printf("--------------------------------------------------------\n");
printf("\n");
printf("Welcome to the key generator. With this program, you can\n");
printf("generate the public and private keys that you use for\n");
printf("secure communications.\n");
printf("\n");
printf("You have specified the name \"%s\" for your key\n\n", nickname);
printf("If this is correct, press enter: ");
while (getc(stdin) != '\n')
;
/* check if key already exists */
if (SECU_CheckKeyNameExists(handle, nickname) == PR_TRUE) {
printf("\n");
SECU_PrintError(progName, "key \"%s\" already exists", nickname);
return SECFailure;
}
rv = DER_SetInteger(arena, &pe, publicExponent);
if (rv) goto loser;
rng = GetRNGContext();
printf("\n\n");
printf("Generating key. This may take a few moments...\n\n");
key = RSA_NewKey(rng, keyBits, &pe);
if (!key) goto loser;
rv = SECU_PutPrivateKey(handle, nickname, key);
if(rv) {
goto loser;
}
printf("\n");
printf("Your new key named \"%s\" has been stored in the database.",
nickname);
printf("\n");
return SECSuccess;
loser:
return SECFailure;
}
static unsigned char P[] = { 0x00, 0x8d, 0xf2, 0xa4, 0x94, 0x49, 0x22, 0x76,
0xaa, 0x3d, 0x25, 0x75, 0x9b, 0xb0, 0x68, 0x69,
0xcb, 0xea, 0xc0, 0xd8, 0x3a, 0xfb, 0x8d, 0x0c,
0xf7, 0xcb, 0xb8, 0x32, 0x4f, 0x0d, 0x78, 0x82,
0xe5, 0xd0, 0x76, 0x2f, 0xc5, 0xb7, 0x21, 0x0e,
0xaf, 0xc2, 0xe9, 0xad, 0xac, 0x32, 0xab, 0x7a,
0xac, 0x49, 0x69, 0x3d, 0xfb, 0xf8, 0x37, 0x24,
0xc2, 0xec, 0x07, 0x36, 0xee, 0x31, 0xc8, 0x02,
0x91 };
static unsigned char Q[] = { 0x00, 0xc7, 0x73, 0x21, 0x8c, 0x73, 0x7e, 0xc8,
0xee, 0x99, 0x3b, 0x4f, 0x2d, 0xed, 0x30, 0xf4,
0x8e, 0xda, 0xce, 0x91, 0x5f };
static unsigned char G[] = { 0x00, 0x62, 0x6d, 0x02, 0x78, 0x39, 0xea, 0x0a,
0x13, 0x41, 0x31, 0x63, 0xa5, 0x5b, 0x4c, 0xb5,
0x00, 0x29, 0x9d, 0x55, 0x22, 0x95, 0x6c, 0xef,
0xcb, 0x3b, 0xff, 0x10, 0xf3, 0x99, 0xce, 0x2c,
0x2e, 0x71, 0xcb, 0x9d, 0xe5, 0xfa, 0x24, 0xba,
0xbf, 0x58, 0xe5, 0xb7, 0x95, 0x21, 0x92, 0x5c,
0x9c, 0xc4, 0x2e, 0x9f, 0x6f, 0x46, 0x4b, 0x08,
0x8c, 0xc5, 0x72, 0xaf, 0x53, 0xe6, 0xd7, 0x88,
0x02 };
static PQGParams default_pqg_params = {
NULL,
{ 0, P, sizeof(P) },
{ 0, Q, sizeof(Q) },
{ 0, G, sizeof(G) }
};
static SECStatus
GenerateDSAKey(char *nickname, char *pqgFile)
{
RNGContext *rng;
SECStatus rv;
SECItem pe;
SECKEYLowPrivateKey *privKey;
SECKEYLowPublicKey *pubKey;
SECKEYKeyDBHandle *handle;
PQGParams *pqg;
/* open key database if it exists */
handle = OpenKeyDB(SEC_CREATE_KEY_DB);
if (handle == NULL) {
return(SECFailure);
}
/* New interface stuffs. */
printf("--------------------------------------------------------\n");
printf("| Netscape Communications Corporation |\n");
printf("| Key Generation |\n");
printf("--------------------------------------------------------\n");
printf("\n");
printf("Welcome to the key generator. With this program, you can\n");
printf("generate the public and private keys that you use for\n");
printf("secure communications.\n");
printf("\n");
printf("You have specified the name \"%s\" for your key\n\n", nickname);
printf("If this is correct, press enter: ");
while (getc(stdin) != '\n')
;
/* check if key already exists */
if (SECU_CheckKeyNameExists(handle, nickname) == PR_TRUE) {
printf("\n");
SECU_PrintError(progName, "key \"%s\" already exists", nickname);
return SECFailure;
}
pqg = &default_pqg_params;
rng = GetRNGContext();
printf("\n\n");
printf("Generating key. This may take a few moments...\n\n");
rv = DSA_NewKey(pqg, &pubKey, &privKey);
if (rv) goto loser;
rv = SECU_PutPrivateKey(handle, nickname, privKey);
if(rv) {
goto loser;
}
printf("\n");
printf("Your new key named \"%s\" has been stored in the database.",
nickname);
printf("\n");
return SECSuccess;
loser:
return SECFailure;
}
static SECStatus
ListKeys(FILE *out)
{
int rt;
SECKEYKeyDBHandle *handle;
/* open key database if it exists */
handle = OpenKeyDB(SEC_KEY_DB_EXISTS);
if (handle == NULL) {
return(SECFailure);
}
rt = SECU_PrintKeyNames(handle, out);
if (rt) {
SECU_PrintError(progName, "unable to list nicknames");
return SECFailure;
}
return SECSuccess;
}
static SECStatus
DumpPublicKey(char *nickname, FILE *out)
{
SECKEYLowPrivateKey *privKey;
SECKEYLowPublicKey *publicKey;
SECKEYKeyDBHandle *handle;
/* open key database if it exists */
handle = OpenKeyDB(SEC_KEY_DB_EXISTS);
if (handle == NULL) {
return(SECFailure);
}
/* check if key actually exists */
if (SECU_CheckKeyNameExists(handle, nickname) == PR_FALSE) {
SECU_PrintError(progName, "the key \"%s\" does not exist", nickname);
return SECFailure;
}
/* Read in key */
privKey = SECU_GetPrivateKey(handle, nickname);
if (!privKey) {
return SECFailure;
}
publicKey = SECKEY_LowConvertToPublicKey(privKey);
/* Output public key (in the clear) */
switch(publicKey->keyType) {
case rsaKey:
fprintf(out, "RSA Public-Key:\n");
SECU_PrintInteger(out, &publicKey->u.rsa.modulus, "modulus", 1);
SECU_PrintInteger(out, &publicKey->u.rsa.publicExponent,
"publicExponent", 1);
break;
case dsaKey:
fprintf(out, "DSA Public-Key:\n");
SECU_PrintInteger(out, &publicKey->u.dsa.params.prime, "prime", 1);
SECU_PrintInteger(out, &publicKey->u.dsa.params.subPrime,
"subPrime", 1);
SECU_PrintInteger(out, &publicKey->u.dsa.params.base, "base", 1);
SECU_PrintInteger(out, &publicKey->u.dsa.publicValue, "publicValue", 1);
break;
default:
fprintf(out, "unknown key type\n");
break;
}
return SECSuccess;
}
static SECStatus
DumpPrivateKey(char *nickname, FILE *out)
{
SECKEYLowPrivateKey *key;
SECKEYKeyDBHandle *handle;
/* open key database if it exists */
handle = OpenKeyDB(SEC_KEY_DB_EXISTS);
if (handle == NULL) {
return(SECFailure);
}
/* check if key actually exists */
if (SECU_CheckKeyNameExists(handle, nickname) == PR_FALSE) {
SECU_PrintError(progName, "the key \"%s\" does not exist", nickname);
return SECFailure;
}
/* Read in key */
key = SECU_GetPrivateKey(handle, nickname);
if (!key) {
SECU_PrintError(progName, "error retrieving key");
return SECFailure;
}
switch(key->keyType) {
case rsaKey:
fprintf(out, "RSA Private-Key:\n");
SECU_PrintInteger(out, &key->u.rsa.modulus, "modulus", 1);
SECU_PrintInteger(out, &key->u.rsa.publicExponent, "publicExponent", 1);
SECU_PrintInteger(out, &key->u.rsa.privateExponent,
"privateExponent", 1);
SECU_PrintInteger(out, &key->u.rsa.prime[0], "prime[0]", 1);
SECU_PrintInteger(out, &key->u.rsa.prime[1], "prime[1]", 1);
SECU_PrintInteger(out, &key->u.rsa.primeExponent[0],
"primeExponent[0]", 1);
SECU_PrintInteger(out, &key->u.rsa.primeExponent[1],
"primeExponent[1]", 1);
SECU_PrintInteger(out, &key->u.rsa.coefficient, "coefficient", 1);
break;
case dsaKey:
fprintf(out, "DSA Private-Key:\n");
SECU_PrintInteger(out, &key->u.dsa.params.prime, "prime", 1);
SECU_PrintInteger(out, &key->u.dsa.params.subPrime, "subPrime", 1);
SECU_PrintInteger(out, &key->u.dsa.params.base, "base", 1);
SECU_PrintInteger(out, &key->u.dsa.publicValue, "publicValue", 1);
SECU_PrintInteger(out, &key->u.dsa.privateValue, "privateValue", 1);
break;
default:
fprintf(out, "unknown key type\n");
break;
}
return SECSuccess;
}
static SECStatus
ChangePassword(void)
{
SECKEYKeyDBHandle *handle;
SECStatus rv;
/* open key database if it exists */
handle = OpenKeyDB(SEC_KEY_DB_EXISTS);
if (handle == NULL) {
return(SECFailure);
}
/* Write out database with a new password */
rv = SECU_ChangeKeyDBPassword(handle);
if (rv) {
SECU_PrintError(progName, "unable to change key password");
}
return rv;
}
static AddPrivateKey (char *nickName, FILE *inFile)
{
PRArenaPool *poolp = NULL;
SECItem privKeyDER;
SECKEYKeyDBHandle *keyHandle;
SECKEYPrivateKeyInfo *pki;
SECKEYLowPrivateKey *pk;
SECStatus rv;
privKeyDER.data = NULL;
/* open key database if it exists */
keyHandle = OpenKeyDB(SEC_CREATE_KEY_DB);
if (keyHandle == NULL)
return (-1);
do {
rv = SECU_DER_Read(&privKeyDER, inFile);
if (rv != SECSuccess)
break;
poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (poolp == NULL) {
ERROR_BREAK;
}
pki = PORT_ArenaZAlloc(poolp, sizeof(SECKEYPrivateKeyInfo));
if (pki == NULL) {
ERROR_BREAK;
}
rv = SEC_ASN1DecodeItem(poolp, pki, SECKEY_PrivateKeyInfoTemplate,
&privKeyDER);
if (rv != SECSuccess) {
ERROR_BREAK;
}
pk = PORT_ArenaZAlloc(poolp, sizeof(SECKEYLowPrivateKey));
if (pk == NULL) {
ERROR_BREAK;
}
rv = SEC_ASN1DecodeItem(poolp, pk, SECKEY_RSAPrivateKeyTemplate,
&pki->privateKey);
if (rv != SECSuccess) {
ERROR_BREAK;
}
pk->keyType = rsaKey; /* XXX RSA */
rv = SECU_PutPrivateKey (keyHandle, nickName, pk);
} while (0);
PORT_Free (privKeyDER.data);
PORT_FreeArena (poolp, PR_TRUE);
if (rv != SECSuccess) {
fprintf(stderr, "%s: problem adding private key (%s)\n",
progName, SECU_ErrorString(PORT_GetError()));
return (-1);
}
return (rv);
}
static SECStatus DeletePrivateKey (char *nickName)
{
SECKEYKeyDBHandle *keyHandle;
int rv;
/* open key database if it exists */
keyHandle = OpenKeyDB(SEC_CREATE_KEY_DB);
if (keyHandle == NULL)
return (-1);
rv = SECU_DeleteKeyByName (keyHandle, nickName);
if (rv != SECSuccess)
fprintf(stderr, "%s: problem deleting private key (%s)\n",
progName, SECU_ErrorString(PORT_GetError()));
return (rv);
}
static void
Usage(const char *progName)
{
fprintf(stderr,
"Usage: %s -n name -g [-t rsa] [-s num] [-e exp] [-d keydir]\n"
" %s -n name -g -t dsa [-q pqgfile] [-d keydir]\n"
" %s -A -n nickname -i privatekey [-d keydir]\n"
" %s -D -n nickname [-d keydir]\n",
progName, progName, progName, progName);
fprintf(stderr, " %s -n name [-p|-P] [-d keydir]\n", progName);
fprintf(stderr, " %s [-c|-l] [-d keydir]\n", progName);
/* generate new key pair */
fprintf(stderr, "%-20s Nickname attributed to the key\n",
"-n name");
fprintf(stderr, "%-20s Generate a new key\n",
"-g");
fprintf(stderr, "%-20s Specify type of key to generate\n",
"-t [rsa|dsa]");
fprintf(stderr, "%-20s Set key size (min of %d, max of %d, default %d)\n",
"-s size", MIN_KEY_BITS, MAX_KEY_BITS, MAX_KEY_BITS);
fprintf(stderr, "%-20s Set the public exponent value (3, 17, 65537)\n",
"-e exp");
fprintf(stderr, "%-20s read PQG value from pqgfile\n",
"-q pqgfile");
fprintf(stderr, "%-20s Pretty print the public key information\n",
"-p");
fprintf(stderr, "%-20s Pretty print the private key information\n",
"-P");
fprintf(stderr, "%-20s Change the key database password\n",
"-c");
fprintf(stderr, "%-20s List the nicknames for the keys in a database\n",
"-l");
fprintf(stderr, "%-20s Key database directory (default is ~/.netscape)\n",
"-d keydir");
/* Add a private key to the dbase */
fprintf (stderr, "%-15s Add a private key to the key dbase\n", "-A");
fprintf(stderr, "%-20s Specify the nickname for the key to add\n",
"-n nickname");
fprintf(stderr, "%-20s Key database directory (default is ~/.netscape)\n",
"-d keydir");
fprintf(stderr, "%-20s Specify the binary private key file\n",
"-i privatekey");
/* Remove a private key from the dbase */
fprintf (stderr, "%-15s Delete a private key from the key dbase\n", "-D");
fprintf(stderr, "%-20s Specify the nickname for the key to add\n",
"-n nickname");
exit(-1);
}
int main(int argc, char **argv)
{
int o, generateKey, changePassword, dumpPublicKey, dumpPrivateKey, list;
int keyBits, publicExponent;
int addKey, deleteKey;
char *nickname;
FILE *inFile;
SECStatus rv;
char *pqgFile = NULL;
KeyType keyType = rsaKey;
addKey = deleteKey = 0;
inFile = NULL;
progName = strrchr(argv[0], '/');
progName = progName ? progName+1 : argv[0];
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
fprintf(stderr, "%s: unable to allocate memory\n", progName);
return -1;
}
/* Parse command line arguments */
keyBits = MAX_KEY_BITS;
publicExponent = 0x010001;
generateKey = changePassword = dumpPublicKey = dumpPrivateKey = list = 0;
nickname = 0;
while ((o = getopt(argc, argv, "Ai:Dn:clgpPd:s:e:t:q:")) != -1) {
switch (o) {
case '?':
Usage(progName);
break;
case 'A':
addKey = 1;
break;
case 'D':
deleteKey = 1;
break;
case 'n':
nickname = optarg;
break;
case 'l':
list = 1;
break;
case 'g':
generateKey = 1;
break;
case 'c':
changePassword = 1;
break;
case 'i':
inFile = fopen(optarg, "r");
if (!inFile) {
fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
progName, optarg);
return -1;
}
break;
case 'p':
dumpPublicKey = 1;
break;
case 'd':
SECU_ConfigDirectory(optarg);
break;
case 'P':
dumpPrivateKey = 1;
break;
case 's':
keyBits = atoi(optarg);
if ((keyBits < MIN_KEY_BITS) || (keyBits > MAX_KEY_BITS)) {
Usage(progName);
}
break;
case 'e':
publicExponent = atoi(optarg);
if ((publicExponent != 3) &&
(publicExponent != 17) &&
(publicExponent != 65537)) {
Usage(progName);
}
break;
case 't':
if (strcmp(optarg, "rsa") == 0) {
keyType = rsaKey;
} else if (strcmp(optarg, "dsa") == 0) {
keyType = dsaKey;
} else {
Usage(progName);
}
break;
case 'q':
if (keyType != dsaKey)
Usage(progName);
pqgFile = strdup(optarg);
break;
}
}
if (generateKey+dumpPublicKey+changePassword+
dumpPrivateKey+list+addKey+deleteKey != 1)
Usage(progName);
if (addKey && (!inFile || !nickname))
Usage (progName);
if ((list || changePassword) && nickname)
Usage(progName);
if ((generateKey || dumpPublicKey || dumpPrivateKey) && !nickname)
Usage(progName);
if (deleteKey && !nickname)
Usage (progName);
/* Call the libsec initialization routines */
PR_Init("key", 1, 1, 0);
SEC_Init();
RNG_SystemInfoForRNG();
/* XXX Try to use these files for RNG seed. If they're not there (i.e.
* installed somewhere else) oh well. */
RNG_FileForRNG("/usr/ns-home/https-443/config/magnus.conf");
RNG_FileForRNG("/usr/ns-home/https-443/config/obj.conf");
if (generateKey) {
if (keyType == rsaKey) {
rv = GenerateRSAKey(nickname, keyBits, publicExponent);
} else {
rv = GenerateDSAKey(nickname, pqgFile);
}
} else
if (dumpPublicKey) {
rv = DumpPublicKey(nickname, stdout);
} else
if (changePassword) {
rv = ChangePassword();
} else
if (dumpPrivateKey) {
rv = DumpPrivateKey(nickname, stdout);
} else
if (list) {
rv = ListKeys(stdout);
} else
if (addKey) {
rv = AddPrivateKey (nickname, inFile);
} else
if (deleteKey) {
rv = DeletePrivateKey (nickname);
}
return rv ? -1 : 0;
}