Ruby  2.0.0p247(2013-06-27revision41674)
ext/openssl/ossl_ns_spki.c
Go to the documentation of this file.
00001 /*
00002  * $Id: ossl_ns_spki.c 33497 2011-10-20 17:22:09Z emboss $
00003  * 'OpenSSL for Ruby' project
00004  * Copyright (C) 2001-2002  Michal Rokos <m.rokos@sh.cvut.cz>
00005  * All rights reserved.
00006  */
00007 /*
00008  * This program is licenced under the same licence as Ruby.
00009  * (See the file 'LICENCE'.)
00010  */
00011 #include "ossl.h"
00012 
00013 #define WrapSPKI(klass, obj, spki) do { \
00014     if (!(spki)) { \
00015         ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \
00016     } \
00017     (obj) = Data_Wrap_Struct((klass), 0, NETSCAPE_SPKI_free, (spki)); \
00018 } while (0)
00019 #define GetSPKI(obj, spki) do { \
00020     Data_Get_Struct((obj), NETSCAPE_SPKI, (spki)); \
00021     if (!(spki)) { \
00022         ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \
00023     } \
00024 } while (0)
00025 
00026 /*
00027  * Classes
00028  */
00029 VALUE mNetscape;
00030 VALUE cSPKI;
00031 VALUE eSPKIError;
00032 
00033 /*
00034  * Public functions
00035  */
00036 
00037 /*
00038  * Private functions
00039  */
00040 static VALUE
00041 ossl_spki_alloc(VALUE klass)
00042 {
00043     NETSCAPE_SPKI *spki;
00044     VALUE obj;
00045 
00046     if (!(spki = NETSCAPE_SPKI_new())) {
00047         ossl_raise(eSPKIError, NULL);
00048     }
00049     WrapSPKI(klass, obj, spki);
00050 
00051     return obj;
00052 }
00053 
00054 /*
00055  * call-seq:
00056  *    SPKI.new([request]) => spki
00057  *
00058  * === Parameters
00059  * * +request+ - optional raw request, either in PEM or DER format.
00060  */
00061 static VALUE
00062 ossl_spki_initialize(int argc, VALUE *argv, VALUE self)
00063 {
00064     NETSCAPE_SPKI *spki;
00065     VALUE buffer;
00066     const unsigned char *p;
00067 
00068     if (rb_scan_args(argc, argv, "01", &buffer) == 0) {
00069         return self;
00070     }
00071     StringValue(buffer);
00072     if (!(spki = NETSCAPE_SPKI_b64_decode(RSTRING_PTR(buffer), -1))) {
00073         p = (unsigned char *)RSTRING_PTR(buffer);
00074         if (!(spki = d2i_NETSCAPE_SPKI(NULL, &p, RSTRING_LEN(buffer)))) {
00075             ossl_raise(eSPKIError, NULL);
00076         }
00077     }
00078     NETSCAPE_SPKI_free(DATA_PTR(self));
00079     DATA_PTR(self) = spki;
00080     ERR_clear_error();
00081 
00082     return self;
00083 }
00084 
00085 /*
00086  * call-seq:
00087  *    spki.to_der => DER-encoded string
00088  *
00089  * Returns the DER encoding of this SPKI.
00090  */
00091 static VALUE
00092 ossl_spki_to_der(VALUE self)
00093 {
00094     NETSCAPE_SPKI *spki;
00095     VALUE str;
00096     long len;
00097     unsigned char *p;
00098 
00099     GetSPKI(self, spki);
00100     if ((len = i2d_NETSCAPE_SPKI(spki, NULL)) <= 0)
00101         ossl_raise(eX509CertError, NULL);
00102     str = rb_str_new(0, len);
00103     p = (unsigned char *)RSTRING_PTR(str);
00104     if (i2d_NETSCAPE_SPKI(spki, &p) <= 0)
00105         ossl_raise(eX509CertError, NULL);
00106     ossl_str_adjust(str, p);
00107 
00108     return str;
00109 }
00110 
00111 /*
00112  * call-seq:
00113  *    spki.to_pem => PEM-encoded string
00114  *
00115  * Returns the PEM encoding of this SPKI.
00116  */
00117 static VALUE
00118 ossl_spki_to_pem(VALUE self)
00119 {
00120     NETSCAPE_SPKI *spki;
00121     char *data;
00122     VALUE str;
00123 
00124     GetSPKI(self, spki);
00125     if (!(data = NETSCAPE_SPKI_b64_encode(spki))) {
00126         ossl_raise(eSPKIError, NULL);
00127     }
00128     str = ossl_buf2str(data, rb_long2int(strlen(data)));
00129 
00130     return str;
00131 }
00132 
00133 /*
00134  * call-seq:
00135  *    spki.to_text => string
00136  *
00137  * Returns a textual representation of this SPKI, useful for debugging
00138  * purposes.
00139  */
00140 static VALUE
00141 ossl_spki_print(VALUE self)
00142 {
00143     NETSCAPE_SPKI *spki;
00144     BIO *out;
00145     BUF_MEM *buf;
00146     VALUE str;
00147 
00148     GetSPKI(self, spki);
00149     if (!(out = BIO_new(BIO_s_mem()))) {
00150         ossl_raise(eSPKIError, NULL);
00151     }
00152     if (!NETSCAPE_SPKI_print(out, spki)) {
00153         BIO_free(out);
00154         ossl_raise(eSPKIError, NULL);
00155     }
00156     BIO_get_mem_ptr(out, &buf);
00157     str = rb_str_new(buf->data, buf->length);
00158     BIO_free(out);
00159 
00160     return str;
00161 }
00162 
00163 /*
00164  * call-seq:
00165  *    spki.public_key => pkey
00166  *
00167  * Returns the public key associated with the SPKI, an instance of
00168  * OpenSSL::PKey.
00169  */
00170 static VALUE
00171 ossl_spki_get_public_key(VALUE self)
00172 {
00173     NETSCAPE_SPKI *spki;
00174     EVP_PKEY *pkey;
00175 
00176     GetSPKI(self, spki);
00177     if (!(pkey = NETSCAPE_SPKI_get_pubkey(spki))) { /* adds an reference */
00178         ossl_raise(eSPKIError, NULL);
00179     }
00180 
00181     return ossl_pkey_new(pkey); /* NO DUP - OK */
00182 }
00183 
00184 /*
00185  * call-seq:
00186  *    spki.public_key = pub => pkey
00187  *
00188  * === Parameters
00189  * * +pub+ - the public key to be set for this instance
00190  *
00191  * Sets the public key to be associated with the SPKI, an instance of
00192  * OpenSSL::PKey. This should be the public key corresponding to the
00193  * private key used for signing the SPKI.
00194  */
00195 static VALUE
00196 ossl_spki_set_public_key(VALUE self, VALUE key)
00197 {
00198     NETSCAPE_SPKI *spki;
00199 
00200     GetSPKI(self, spki);
00201     if (!NETSCAPE_SPKI_set_pubkey(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */
00202         ossl_raise(eSPKIError, NULL);
00203     }
00204 
00205     return key;
00206 }
00207 
00208 /*
00209  * call-seq:
00210  *    spki.challenge => string
00211  *
00212  * Returns the challenge string associated with this SPKI.
00213  */
00214 static VALUE
00215 ossl_spki_get_challenge(VALUE self)
00216 {
00217     NETSCAPE_SPKI *spki;
00218 
00219     GetSPKI(self, spki);
00220     if (spki->spkac->challenge->length <= 0) {
00221         OSSL_Debug("Challenge.length <= 0?");
00222         return rb_str_new(0, 0);
00223     }
00224 
00225     return rb_str_new((const char *)spki->spkac->challenge->data,
00226                       spki->spkac->challenge->length);
00227 }
00228 
00229 /*
00230  * call-seq:
00231  *    spki.challenge = str => string
00232  *
00233  * === Parameters
00234  * * +str+ - the challenge string to be set for this instance
00235  *
00236  * Sets the challenge to be associated with the SPKI. May be used by the
00237  * server, e.g. to prevent replay.
00238  */
00239 static VALUE
00240 ossl_spki_set_challenge(VALUE self, VALUE str)
00241 {
00242     NETSCAPE_SPKI *spki;
00243 
00244     StringValue(str);
00245     GetSPKI(self, spki);
00246     if (!ASN1_STRING_set(spki->spkac->challenge, RSTRING_PTR(str),
00247                          RSTRING_LENINT(str))) {
00248         ossl_raise(eSPKIError, NULL);
00249     }
00250 
00251     return str;
00252 }
00253 
00254 /*
00255  * call-seq:
00256  *    spki.sign(key, digest) => spki
00257  *
00258  * === Parameters
00259  * * +key+ - the private key to be used for signing this instance
00260  * * +digest+ - the digest to be used for signing this instance
00261  *
00262  * To sign an SPKI, the private key corresponding to the public key set
00263  * for this instance should be used, in addition to a digest algorithm in
00264  * the form of an OpenSSL::Digest. The private key should be an instance of
00265  * OpenSSL::PKey.
00266  */
00267 static VALUE
00268 ossl_spki_sign(VALUE self, VALUE key, VALUE digest)
00269 {
00270     NETSCAPE_SPKI *spki;
00271     EVP_PKEY *pkey;
00272     const EVP_MD *md;
00273 
00274     pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
00275     md = GetDigestPtr(digest);
00276     GetSPKI(self, spki);
00277     if (!NETSCAPE_SPKI_sign(spki, pkey, md)) {
00278         ossl_raise(eSPKIError, NULL);
00279     }
00280 
00281     return self;
00282 }
00283 
00284 /*
00285  * call-seq:
00286  *    spki.verify(key) => boolean
00287  *
00288  * === Parameters
00289  * * +key+ - the public key to be used for verifying the SPKI signature
00290  *
00291  * Returns +true+ if the signature is valid, +false+ otherwise. To verify an
00292  * SPKI, the public key contained within the SPKI should be used.
00293  */
00294 static VALUE
00295 ossl_spki_verify(VALUE self, VALUE key)
00296 {
00297     NETSCAPE_SPKI *spki;
00298 
00299     GetSPKI(self, spki);
00300     switch (NETSCAPE_SPKI_verify(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */
00301     case 0:
00302         return Qfalse;
00303     case 1:
00304         return Qtrue;
00305     default:
00306         ossl_raise(eSPKIError, NULL);
00307     }
00308     return Qnil; /* dummy */
00309 }
00310 
00311 /* Document-class: OpenSSL::Netscape::SPKI
00312  *
00313  * A Simple Public Key Infrastructure implementation (pronounced "spookey").
00314  * The structure is defined as
00315  *   PublicKeyAndChallenge ::= SEQUENCE {
00316  *     spki SubjectPublicKeyInfo,
00317  *     challenge IA5STRING
00318  *   }
00319  *
00320  *   SignedPublicKeyAndChallenge ::= SEQUENCE {
00321  *     publicKeyAndChallenge PublicKeyAndChallenge,
00322  *     signatureAlgorithm AlgorithmIdentifier,
00323  *     signature BIT STRING
00324  *   }
00325  * where the definitions of SubjectPublicKeyInfo and AlgorithmIdentifier can
00326  * be found in RFC5280. SPKI is typically used in browsers for generating
00327  * a public/private key pair and a subsequent certificate request, using
00328  * the HTML <keygen> element.
00329  *
00330  * == Examples
00331  *
00332  * === Creating an SPKI
00333  *   key = OpenSSL::PKey::RSA.new 2048
00334  *   spki = OpenSSL::Netscape::SPKI.new
00335  *   spki.challenge = "RandomChallenge"
00336  *   spki.public_key = key.public_key
00337  *   spki.sign(key, OpenSSL::Digest::SHA256.new)
00338  *   #send a request containing this to a server generating a certificate
00339  * === Verifiying an SPKI request
00340  *   request = #...
00341  *   spki = OpenSSL::Netscape::SPKI.new request
00342  *   unless spki.verify(spki.public_key)
00343  *     # signature is invalid
00344  *   end
00345  *   #proceed
00346  */
00347 
00348 /* Document-module: OpenSSL::Netscape
00349  *
00350  * OpenSSL::Netscape is a namespace for SPKI (Simple Public Key
00351  * Infrastructure) which implements Signed Public Key and Challenge.
00352  * See {RFC 2692}[http://tools.ietf.org/html/rfc2692] and {RFC
00353  * 2693}[http://tools.ietf.org/html/rfc2692] for details.
00354  */
00355 
00356 /* Document-class: OpenSSL::Netscape::SPKIError
00357  *
00358  * Generic Exception class that is raised if an error occurs during an
00359  * operation on an instance of OpenSSL::Netscape::SPKI.
00360  */
00361 
00362 void
00363 Init_ossl_ns_spki()
00364 {
00365 #if 0
00366     mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */
00367 #endif
00368 
00369     mNetscape = rb_define_module_under(mOSSL, "Netscape");
00370 
00371     eSPKIError = rb_define_class_under(mNetscape, "SPKIError", eOSSLError);
00372 
00373     cSPKI = rb_define_class_under(mNetscape, "SPKI", rb_cObject);
00374 
00375     rb_define_alloc_func(cSPKI, ossl_spki_alloc);
00376     rb_define_method(cSPKI, "initialize", ossl_spki_initialize, -1);
00377 
00378     rb_define_method(cSPKI, "to_der", ossl_spki_to_der, 0);
00379     rb_define_method(cSPKI, "to_pem", ossl_spki_to_pem, 0);
00380     rb_define_alias(cSPKI, "to_s", "to_pem");
00381     rb_define_method(cSPKI, "to_text", ossl_spki_print, 0);
00382     rb_define_method(cSPKI, "public_key", ossl_spki_get_public_key, 0);
00383     rb_define_method(cSPKI, "public_key=", ossl_spki_set_public_key, 1);
00384     rb_define_method(cSPKI, "sign", ossl_spki_sign, 2);
00385     rb_define_method(cSPKI, "verify", ossl_spki_verify, 1);
00386     rb_define_method(cSPKI, "challenge", ossl_spki_get_challenge, 0);
00387     rb_define_method(cSPKI, "challenge=", ossl_spki_set_challenge, 1);
00388 }
00389 
00390