63a9a01997-11-30Niels Möller /*
e4e7d81997-12-20Henrik Grubbström (Grubba)  * $Id: make_csr.pike,v 1.4 1997/12/20 00:20:38 grubba Exp $
63a9a01997-11-30Niels Möller  */ 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.");
e4e7d81997-12-20Henrik Grubbström (Grubba) #if !constant(_Crypto) || !constant(Crypto.rsa)
2e12951997-12-17Henrik Grubbström (Grubba)  constant action_disabled = 1;
e4e7d81997-12-20Henrik Grubbström (Grubba) #else /* constant(_Crypto) && constant(Crypto.rsa) */
2e12951997-12-17Henrik Grubbström (Grubba) 
63a9a01997-11-30Niels Möller 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; /* Remove initial and trailing whitespace, and ignore * empty attributes. */ 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); }
2e12951997-12-17Henrik Grubbström (Grubba) 
e4e7d81997-12-20Henrik Grubbström (Grubba) #endif /* constant(_Crypto) && constant(Crypto.rsa) */