|
|
|
|
inherit "wizard"; |
|
import Standards.PKCS; |
import Standards.ASN1.Encode; |
|
#if 0 |
#define WERROR werror |
#else |
#define WERROR(x) |
#endif |
|
constant name = "Security//Generate a Certificate Signing Request for an RSA key..."; |
|
constant doc = ("To use an RSA key with your server, you must have a certificate " |
"for it. You request a certificate by sending a Certificate " |
"Signing Request to a Certificate Authority, for example Thawte " |
"or VeriSign."); |
|
#if !constant(_Crypto) || !constant(Crypto.rsa) |
|
constant action_disabled = 1; |
|
#else /* constant(_Crypto) && constant(Crypto.rsa) */ |
|
mixed page_0(object id, object mc) |
{ |
string msg; |
|
if (id->variables->_error) |
{ |
msg = "<font color=red>" + id->variables->_error |
+ "</font><p>"; |
id->variables->_error = 0; |
} |
|
return (msg || "" ) |
+ ("<font size=+1>Which key do you want to certify?</font><p>" |
"<var name=key_file type=string><br>\n" |
"Where the private key is stored.<br> " |
"<help><blockquote>" |
"A filename in the real filesystem, where the private key is stored. " |
"(The private key is needed to sign the CSR. It is <em>not</em> " |
"included in the file sent to the Certificate Authority)." |
"</blockquote></help>"); |
} |
|
mixed verify_0(object id, object mc) |
{ |
if (!file_stat(id->variables->key_file)) |
{ |
id->variables->_error = "File not found."; |
return 1; |
} |
return 0; |
} |
|
mixed page_1(mixed id, mixed mc) |
{ |
return ("<font size=+1>Your Distinguished Name?</font><p>" |
"<help><blockquote>" |
"Your X.501 Distinguished Name consists of a chain of attributes " |
"and values, where each link in the chain defines more precisely " |
"who you are. Which attributes are necessary or useful " |
"depends on what you will use the certificate for, and which " |
"Certificate Authority you use. This page lets you specify " |
"the most useful attributes. If you leave a field blank, " |
"that attribute will be omitted from your name.<p>\n" |
"Unfortunately, all fields should be in US-ASCII." |
"</blockquote></help>" |
|
"<var name=countryName type=string default=SE><br>" |
"Your country code<br>\n" |
"<help><blockquote>" |
"Your two-letter country code, for example GB (United Kingdom). " |
"This attribute is required." |
"</blockquote></help>" |
|
"<var name=stateOrProvinceName type=string><br>" |
"State/Province<br>\n" |
"<help><blockquote>" |
"The state where you are operating. VeriSign requires this attribute " |
"to be present for US and Canadian customers. Do not abbreviate." |
"</blockquote></help>" |
|
"<var name=localityName type=string default=Stockholm><br>" |
"City/Locality<br>\n" |
"<help><blockquote>" |
"The city or locality where you are registered. VeriSign " |
"requires that at least one of the locality and the state " |
"attributes are present. Do not abbreviate." |
"</blockquote></help>" |
|
"<var name=organizationName type=string default=\"Idonex AB\"><br>" |
"Organization/Company<br>\n" |
"<help><blockquote>" |
"The organization name under which you are registered with some " |
"national or regional authority." |
"</blockquote></help>" |
|
"<var name=organizationUnitName type=string " |
"default=\"Roxen Development\"><br>" |
"Organizational unit<br>\n" |
"<help><blockquote>" |
"This attribute is optional, and there are no " |
"specific requirements on the value of this attribute." |
"</blockquote></help>" |
|
"<var name=commonName type=string default=\"www.idonex.se\"><br>" |
"Common Name<br>\n" |
"This is the DNS name of your server (i.e. the host part of " |
"the URL).\n" |
"<help><blockquote>" |
"Browsers will compare the URL they are connecting to with " |
"the Common Name in the server's certificate, and warn the user " |
"if they don't match.<p>" |
"Some Certificate Authorities allow wild cards in the Common " |
"Name. This means that you can have a certificate for " |
"<tt>*.idonex.se</tt> which will match all servers at Idonex." |
"Thawte allows wild card certificates, while VeriSign does not." |
"</blockquote></help>"); |
} |
|
mixed page_2(object id, object mc) |
{ |
return ("<font size=+1>Certificate Attributes?</font><p>" |
"<help><blockquote>" |
"The primary parts of a certificate are a Common Name " |
"and a public key. In addition to these components, a " |
"certificate may contain other useful information." |
"</blockquote></help>\n" |
|
"<var name=emailAddress type=string><br>Email address<br>" |
"<help><blockquote>" |
"An email address to be embedded in the certificate." |
"</blockquote></help>\n"); |
} |
|
mixed page_3(object id, object mc) |
{ |
return ("<font size=+1>CSR Attributes?</font><p>" |
"At last, you can add attributes to the Certificate Signing " |
"Request, which are meant for the Certificate Authority " |
"and are not included in the issued Certificate." |
|
"<var name=challengePassword type=password> <br>Challenge Password<br>" |
"<help><blockquote>" |
"This password could be used if you ever want to revoke " |
"your certificate. Of course, this depends on the policy of " |
"your Certificate Authority." |
"</blockquote></help>\n"); |
} |
|
object trim = Regexp("^[ \t]*([^ \t](.*[^ \t]|))[ \t]*$"); |
|
mixed page_4(object id, object mc) |
{ |
object file = Stdio.File(); |
|
object privs = Privs("Reading private RSA key"); |
if (!file->open(id->variables->key_file, "r")) |
{ |
privs = 0; |
|
return "<font color=red>Could not open key file: " |
+ strerror(file->errno()) + "\n</font>"; |
} |
privs = 0; |
string s = file->read(0x10000); |
if (!s) |
return "<font color=red>Could not read private key: " |
+ strerror(file->errno()) + "\n</font>"; |
|
mapping m = SSL.pem.parse_pem(s); |
if (!m || !m["RSA PRIVATE KEY"]) |
return "<font color=red>Key file not formatted properly.\n</font>"; |
|
object rsa = RSA.parse_private_key(m["RSA PRIVATE KEY"]); |
if (!rsa) |
return "<font color=red>Invalid key.\n</font>"; |
|
mapping attrs = ([]); |
string attr; |
|
|
|
foreach( ({ "countryName", "stateOrProvinceName", "localityName", |
"organizationName", "organizationUnitName", "commonName", |
"emailAddress", "challengePassword"}), attr) |
{ |
if (id->variables[attr]) |
{ |
array a = trim->split(id->variables[attr]); |
if (a) |
attrs[attr] = a[0]; |
} |
} |
|
array name = ({ }); |
foreach( ({ "countryName", "localityName", "organizationName", |
"organizationUnitName", "commonName" }), attr) |
{ |
if (attrs[attr]) |
name += ({ ([ attr : asn1_printable_string(attrs[attr]) ]) }); |
} |
|
mapping csr_attrs = ([]); |
foreach( ({ "challengePassword" }), attr) |
{ |
if (attrs[attr]) |
csr_attrs[attr] = ({ asn1_printable_string(attrs[attr]) }); |
} |
|
mapping cert_attrs = ([ ]); |
foreach( ({ "emailAddress" }), attr) |
{ |
if (attrs[attr]) |
cert_attrs[attr] = ({ asn1_IA5_string(attrs[attr]) }); |
} |
|
csr_attrs->extendedCertificateAttributes = |
({ Certificate.Attributes(Identifiers.attribute_ids, |
cert_attrs) }); |
|
object csr = CSR.build_csr(rsa, |
Certificate.build_distinguished_name(@name), |
csr_attrs); |
|
return "<pre>" + SSL.pem.build_pem("CERTIFICATE REQUEST", csr->der()) |
+"</pre>"; |
} |
|
mixed wizard_done(object id, object mc) |
{ |
return 0; |
} |
|
mixed handle(object id) { return wizard_for(id,0); } |
|
#endif /* constant(_Crypto) && constant(Crypto.rsa) */ |
|
|