X509 Netscape-specific routines -- SSLeay 0.9.0b -- January 1999

NAME

NETSCAPE_SPKAC_new, NETSCAPE_SPKAC_free, i2d_NETSCAPE_SPKAC, d2i_NETSCAPE_SPKAC,
NETSCAPE_SPKI_new, NETSCAPE_SPKI_free, i2d_NETSCAPE_SPKI, d2i_NETSCAPE_SPKI,
NETSCAPE_SPKI_sign, NETSCAPE_SPKI_verify -- X509 Netscape-specific routines

SYNOPSIS

#include "x509.h"

NETSCAPE_SPKAC *NETSCAPE_SPKAC_new();

void NETSCAPE_SPKAC_free(a)
NETSCAPE_SPKAC *a;

int i2d_NETSCAPE_SPKAC(a, pp)
NETSCAPE_SPKAC *a;
unsigned char **pp;

NETSCAPE_SPKAC *d2i_NETSCAPE_SPKAC(a, pp, length)
NETSCAPE_SPKAC **a;
unsigned char **pp;
long length;

NETSCAPE_SPKI *NETSCAPE_SPKI_new();

void NETSCAPE_SPKI_free(a)
NETSCAPE_SPKI *a;

int i2d_NETSCAPE_SPKI(a, pp)
NETSCAPE_SPKI *a;
unsigned char **pp;

NETSCAPE_SPKI *d2i_NETSCAPE_SPKI(a, pp, length)
NETSCAPE_SPKI **a;
unsigned char **pp;
long length;

int NETSCAPE_SPKI_verify(a, r)
NETSCAPE_SPKI *a;
EVP_PKEY *r;

DESCRIPTION

Note: The Netscape-specific X509 extensions are described elsewhere, in X509_EXTENSION Handling.

This group of functions is designed to handle the Netscape <KEYGEN> tag.

This HTML tag, if encountered by your Netscape browser, causes the browser to generate a private and public key pair, which it stores in its key database on disk. It then packages up the public key with a challenge string and ships it off to the Web server which sent it the KEYGEN tag.

Here's the ASN.1 for the 'Signed Public Key And Challenge' that it sends to the Web browser.

SignedPublicKeyAndChallenge ::= SEQUENCE {
     publicKeyAndChallenge     PublicKeyAndChallenge,
     signatureAlgorithm        AlgorithmIdentifier,
     signature                 BIT STRING
}

PublicKeyAndChallenge ::= SEQUENCE {
     spki           SubjectPublicKeyInfo,
     challenge      IA5STRING
}

SubjectPublicKeyInfo  ::=  SEQUENCE  {
     algorithm            AlgorithmIdentifier,
     subjectPublicKey     BIT STRING  
}

Here's what the SSLeay library has for these structures:

typedef struct Netscape_spkac_st
        {
        X509_PUBKEY *pubkey;
        ASN1_IA5STRING *challenge;      /* challenge sent in atlas >= PR2 */
        } NETSCAPE_SPKAC;

typedef struct Netscape_spki_st
        {
        NETSCAPE_SPKAC *spkac;  
        X509_ALGOR *sig_algor;
        ASN1_BIT_STRING *signature;
        } NETSCAPE_SPKI;

I have a feeling that the library names an SPKI as NETSCAPE_SPKAC and an SPKAC as NETSCAPE_SPKI, but oh well. You get the idea.

The public key and challenge string are DER encoded as PublicKeyAndChallenge, and then digitally signed with the private key to produce a SignedPublicKeyAndChallenge. The SignedPublicKeyAndChallenge is base64 encoded, and the ascii data is finally submitted to the server as the value of a form name/value pair, where the name is the value specified by the NAME= attribute of the KEYGEN tag. If no challenge string is provided, then it will be encoded as a IA5STRING of length zero.

Now what you at the remote end need to do is un-URL-encode the stuff and then process it. At the point where you have a nice base64-decoded ASN1 blob, you can use the functions in this library to get the subject's public key and to verify the signature on the SPKAC.

On the other hand, if you were hacking KEYGEN support into a client, you could use the functions in this library to sign an SPKI (which would then give you an SPKAC to send to the server).

NETSCAPE_SPKAC_new creates a new NETSCAPE_SPKAC structure and returns a pointer to it, or NULL on error.

NETSCAPE_SPKAC_free frees a.

i2d_NETSCAPE_SPKAC converts a NETSCAPE_SPKAC structure pointed to by a to DER-encoded form; it places the results in *pp and then increments *pp to point to the end of the byte string it has just written, so you can call several i2d functions in a row. It returns the number of bytes written to the string or 0 on error.

See ASN.1 conversion to and from DER-encoded form for more on i2d functions.

d2i_NETSCAPE_SPKAC converts length bytes of the DER-encoded string in *pp to a NETSCAPE_SPKAC structure, updates *pp to point to the next byte to be processed, places the new NETSCAPE_SPKAC structure in *a, and returns it, or NULL on error.

See ASN.1 conversion to and from DER-encoded form for more on d2i functions.

NETSCAPE_SPKI_new creates a new NETSCAPE_SPKI structure and returns a pointer to it, or NULL on error.

NETSCAPE_SPKI_free frees a.

i2d_NETSCAPE_SPKI converts a i2d_NETSCAPE_SPKI structure pointed to by a to DER-encoded form; it places the results in *pp and then increments *pp to point to the end of the byte string it has just written, so you can call several i2d functions in a row. It returns the number of bytes written to the string or 0 on error.

See ASN.1 conversion to and from DER-encoded form for more on i2d functions.

d2i_NETSCAPE_SPKI converts length bytes of the DER-encoded string in *pp to a NETSCAPE_SPKI structure, updates *pp to point to the next byte to be processed, places the new NETSCAPE_SPKI structure in *a, and returns it, or NULL on error.

See ASN.1 conversion to and from DER-encoded form for more on d2i functions.

NETSCAPE_SPKI_sign is actually a macro:

#define NETSCAPE_SPKI_sign(x,pkey,md) 
  ASN1_sign((int (*)())i2d_NETSCAPE_SPKAC, x->sig_algor,NULL,
  x->signature, (char *)x->spkac,pkey,md)

Use this function, passing a NETSCAPE_SPKI structure with only the spkac subfield filled in, to do the signature. The function uses md to generate the digest and pkey to sign. It fills in the sig_algor and signature fields and returns the length of the signature in bytes to the caller, or 0 on error.

NETSCAPE_SPKI_verify converts the NETSCAPE_SPKAC structure a->spkac to DER-encoded form; computes the message digest of it, using a->sig_algor as the message digest algorithm; signs it using r as the key for signing; and finally compares the results against a->signature. 1 is returned if they match, 0 on failure or error.

This function actually just calls ASN1_verify, which calls EVP_VerifyInit, EVP_VerifyUpdate, and EVP_VerifyFinal; see EVP digest handling, signing, verification if you want details on these functions.

NOTE: this routine calls the dreaded EVP_get_digestbyname() which means that the static digest and alias stacks must be initialized; see EVP cipher/digest lookup routines for more on this.