Hello list,

After weeks of "unqualified hacking around" on my machine adapting the openxpki installation to suit my needs I thought i'd share the experience and work I have put into this back to you. So, I came up with some modification proposals and setup hints for the other users who want to "try this at home" ;-)

Since this is only my second time posting something to a mailing list, I tried keeping the body text short and have therefore split everything into separate files - I hope MIME messages are'nt a problem...
Ok...

Well, what you will find attached here, is:
- teststeps.txt
A log of the features I have tested, along with an error message, which I cannot explain but doesn't present a problem later on.
- configuration-comments.txt
This file contains instructions how to modify your config files to try out the same things I did.
- modifications.txt
The diffs to the perl modules I modified under /usr/lib/perl5. BTW I'm using 1488 from SVN on Ubuntu 8.04.
- modifications-comments.txt
A log with comments to my modifications.
- modifedfiles.7z
Actual copies of my modified files from /usr/lib/perl5. Just in case the diff went wrong and to make the user happy (copy-paste ;-))

I hope these files will make it without problems...

A note to the developers regarding the proposals: please don't just adopt my modifications directly - the last time I was contributing to another project this way they did that and my changes broke things in other parts of the software which took them all the way from 1.0-RC1a with several 1.0 delays to 1.20 to discover the problem and fix it ;-( (they've mentioned me in their change log but I'm not very proud of that as you might imagine) Also, I don't know whether some of my changes are fitting into the "design goals" or not...
Anyway, please do a "proof read" before that. Thanks.

I hope it will work out for you,
have fun and best regards,

Marc

P.S.: I do scripting only if inevitable and I've used Perl on Win32 only - the last time in 2002. I'm a relatively busy sysad...@win32/64 and *nix-ing in my free time, but I'll do my best on answering questions if there are any. So please don't blame me on the code and/or on reply speed ;-)
1) root user certificate
- login as root and login to key group => ok
- request ca operator certificate: CN=Root DemoCA, UID=root (basic style) => ok
- login as raop and approve ca operator certificate => ok
- login as root and install certificate in browser => ok
- login as root using cert challenge/response and note username displayes upper 
right stays "root" instead of whole certificate subject => ok
- go to "my certificates" and see the just requested certificate listed instead 
of empty list => ok
2) raop user certificate
- login as raop and request ra operator certificate: CN=Raop DemoCA, UID=raop 
(basic style) => ok
- login as root using cert challenge/response and approve raop's csr using 
digital signature => fail (sure, i'm a ca operator, but i have to be a ra 
operator - i always forget that)
- login as root using "external static" (thank you for this "backdoor") and 
approve raop's csr => ok
- login as raop, install certificate and try to login with challenge/response 
again, checking the same (username display+certificate doenload) as already 
done with root => ok
3) jdoe user certificate (i've renamed him intendedly)
- login as jdoe and request user certificate CN=John Doe, UID=jdoe (basic 
style) => ok
- login as raop using cert challenge/response and approve jdoe's csr using 
digital signature (the ultimative test) => ok
- login as jdoe, install certificate, relogin using challenge response and 
check user+cert as before => ok
4) webserver certificate
- still logged in as jdoe, request webserver certificate using advanced style 
=> error but still ok (pending)
==============================================================
 I18N_OPENXPKI_CLIENT_HTML_MASON_CREATE_CSR_ERROR_OCCURED_DESC
Errors

    * LIST
    * [ { 'LABEL' => 'I18N_OPENXPKI_SERVER_API_INVALID_PARAMETER', 'PARAMS' => 
{ 'ERROR' => 'The \'WORKFLOW\' parameter (undef) to 
OpenXPKI::Server::API::__ANON__ was an \'undef\', which is not one of the 
allowed types: scalar ' } } ]
    * SERVICE_MSG
    * ERROR
==============================================================
- login as raop using cert challenge/response and approve the csr using digital 
signature (i'm getting used to it) => ok
- install certificate on webserver (apache2), configure appropriately, restart 
it. (for hints see configuration-comments.txt)
- point browser to ssl url, give it any of the installed user certificates => ok
- choose "certificate via webserver" as authentication method and see if:
- login with all three users works => ok
- username in to right corner is correct (should be UID instead of CN) => ok
- the requested certificates are listed under "My Certificates" => ok
Configuration file changes needed to do the described tests in alphabetical 
order:
=== auth.xml ===
Find the following line:
<trust_anchors>your,identifiers,or,realms,here</trust_anchors>

And modify it to:
<trust_anchors>I18N_OPENXPKI_DEPLOYMENT_TEST_DUMMY_CA</trust_anchors>

This is to make signing in with certificate challenge/response possible.

=== config.xml ===
Find the following lines:
<!-- Subsystems defined for this realm -->
<!-- 'id' is the subsystem identifier -->

and verify if you will find the following lines, if not add them (openxpkid 
refused starting without that):
<scep id="testscepserver1">
  <cert>
        <alias>testscepserver1</alias>
        <realm>I18N_OPENXPKI_DEPLOYMENT_TEST_DUMMY_CA</realm>
  </cert>
  <retry_time>000001</retry_time>-->
  <scep_client>
          <enrollment_role>SCEP Client Enrollment</enrollment_role>
          <autoissuance_role>SCEP Client AutoIssuance</autoissuance_role>
  </scep_client>
  <token super="common/token_config/token{testscepserver1}"/>
</scep>

=== profile.xml ===
for each:
<netscape>
  ...

add the following line:
        <ssl_server>       false</ssl_server>

between:
  <certificate_type critical="false">
        <ssl_client>       false</ssl_client>
        --> here <--
        <smime_client>     false</smime_client>
        <object_signing>   false</object_signing>
        <ssl_ca>           false</ssl_ca>
        <smime_ca>         false</smime_ca>
        <object_signing_ca>false</object_signing_ca>
  </certificate_type>

except for:
<netscape>
  <comment critical="false">This is a TLS Server certificate.
Generated with OpenXPKI trustcenter software.</comment>
  ...

where you should add:
        <ssl_server>        true</ssl_server>
        
instead of the above line.
This allows issuing web server certificates where the netscape certificate type 
is set to "server".

=== token.xml ===

Search for:
<token id="testdummyca1" super="../token{default}">
        <!-- CA key (PEM encoded) -->
        
<key>/etc/openxpki/instances/trustcenter1/ca/testdummyca1/cakey.pem</key>

<!-- CA passphrase fragments -->
        <secret>default</secret>
</token>

and add the following below if it doesn't exist (openxpkid refused starting 
without that):
<token id="testscepserver1" super="../token{default}">
        <!-- Backend class -->
        <backend>OpenXPKI::Crypto::Tool::SCEP</backend>

        <!-- Backend shell command -->
        <shell>/usr/bin/openca-scep</shell>
        <!-- Private key (PEM encoded) -->
        
<key>/etc/openxpki/instances/trustcenter1/ca/scepdummyserver1/key.pem</key>

        <!-- CA passphrase -->
        <secret>default</secret>
</token>

=== workflow_validator_certificate_revocation_request.xml ===

Search for:
<param name="trust_anchors" value="identifier1,identifier2,..."/>

and replace it with:
<param name="trust_anchors" value="I18N_OPENXPKI_DEPLOYMENT_TEST_DUMMY_CA"/>

This allows signing CRRs with certificates.

=== workflow_validator_certificate_signing_request.xml ===

Again search for:
<param name="trust_anchors" value="identifier1,identifier2,..."/>

and replace it with:
<param name="trust_anchors" value="I18N_OPENXPKI_DEPLOYMENT_TEST_DUMMY_CA"/>

This allows signing CSRs with certificates.

=== you favorite apache config file goes here ===
Add the following lines there after generating your server certificate to do 
client certificate authentication via webserver:

SSLEngine on
SSLCACertificateFile 
/etc/openxpki/instances/trustcenter1/ca/testdummyca1/cacert.pem
SSLCARevocationFile /etc/openxpki/instances/trustcenter1/ca/testdummyca1/crl.pem
SSLVerifyClient optional
SSLVerifyDepth 10
SSLProtocol +SSLv3 +TLSv1
SSLCertificateFile /etc/apache2/server.cert.pem
SSLCertificateKeyFile /etc/apache2/server.key.pem
SSLOptions +StdEnvVars +ExportCertData
--- Crypto/Profile/Base.pm.orig 2009-07-27 11:18:51.000000000 +0200
+++ Crypto/Profile/Base.pm      2009-07-31 23:41:55.000000000 +0200
@@ -165,6 +165,7 @@
                                                   COUNTER => [...@counter, 0, 
0],
                                                   CONFIG_ID => $cfg_id);
             };
+           $config_value =~ s/\s+//g;
             if (defined $config_value &&
                    ($config_value eq 'true' || $config_value eq '1')) {
                 push @values, $bits[$i];
@@ -381,13 +382,14 @@
     }
     elsif ($path[$#path] eq "netscape/certificate_type")
     {
-        my @bits = ( "ssl_client", "smime_client", "object_signing",
+        my @bits = ( "ssl_client", "ssl_server", "smime_client", 
"object_signing",
                      "ssl_ca", "smime_ca", "object_signing_ca" );
         for (my $i=0; $i < scalar @bits; $i++)
         {
             my $bit = $self->{config}->get_xpath (XPATH   => [...@path, 
$bits[$i]],
                                                   COUNTER => [...@counter, 0, 
0],
                                                   CONFIG_ID => $cfg_id);
+           $bit =~ s/\s+//g;
             $bit = "1" if ($bit eq "true");
             $bit = "0" if ($bit eq "false");
             push @values, $bits[$i] if ($bit);
--- Crypto/Backend/OpenSSL/Config.pm.orig       2009-07-28 16:36:47.000000000 
+0200
+++ Crypto/Backend/OpenSSL/Config.pm    2009-07-31 23:47:15.000000000 +0200
@@ -582,16 +582,20 @@
         }
         elsif ($name eq "netscape/certificate_type")
         {
-            $config .= "nsCertType = $critical";
-            my @bits = @{$profile->get_extension("netscape/certificate_type")};
-            $config .= "client,"  if (grep /ssl_client/, @bits);
-            $config .= "objsign," if (grep /object_signing/, @bits);
-            $config .= "email,"   if (grep /smime_client/, @bits);
-            $config .= "sslCA,"   if (grep /ssl_client_ca/, @bits);
-            $config .= "objCA,"   if (grep /object_signing_ca/, @bits);
-            $config .= "emailCA," if (grep /smime_client_ca/, @bits);
-            $config = substr ($config, 0, length ($config)-1); ## remove 
trailing ,
-            $config .= "\n";
+           my @bits = @{$profile->get_extension("netscape/certificate_type")};
+           # OpenSSL doesn't like it when there is an empty nsCertType config 
flag, so check if there is any cert type set (length of array)
+           if (@bits) {
+               $config .= "nsCertType = $critical";
+               $config .= "client,"  if (grep /ssl_client/, @bits);
+               $config .= "server,"  if (grep /ssl_server/, @bits);
+               $config .= "objsign," if (grep /object_signing/, @bits);
+               $config .= "email,"   if (grep /smime_client/, @bits);
+               $config .= "sslCA,"   if (grep /ssl_client_ca/, @bits);
+               $config .= "objCA,"   if (grep /object_signing_ca/, @bits);
+               $config .= "emailCA," if (grep /smime_client_ca/, @bits);
+               $config = substr ($config, 0, length ($config)-1); ## remove 
trailing ,
+               $config .= "\n";
+           }
         }
         elsif ($name eq "netscape/comment")
         {
--- Crypto/Backend/OpenSSL/Command/pkcs7_get_chain.pm.orig      2009-07-13 
23:17:32.000000000 +0200
+++ Crypto/Backend/OpenSSL/Command/pkcs7_get_chain.pm   2009-08-01 
00:22:11.000000000 +0200
@@ -147,16 +147,40 @@
     while (exists $certs{$subject} && $iterations < $MAX_CHAIN_LENGTH)
     {
         ##! 16: 'while for subject: ' . $subject
         $pkcs7  .= $certs{$subject}->{CERT}."\n\n";
         last if ($subject eq $certs{$subject}->{ISSUER});
         $subject = $certs{$subject}->{ISSUER};
         $iterations++;
     }
+
+    if ($pkcs7 eq '') {
+       $subject =~ s/\+/\//g;
+       while (exists $certs{$subject} && $iterations < $MAX_CHAIN_LENGTH)
+       {
+            ##! 16: 'while for subject: ' . $subject
+            $pkcs7  .= $certs{$subject}->{CERT}."\n\n";
+            last if ($subject eq $certs{$subject}->{ISSUER});
+            $subject = $certs{$subject}->{ISSUER};
+            $iterations++;
+        }
+       if ($pkcs7 eq '') {
+           $subject =~ /^([^\/]+)\/([^,]+)(.*)$/;
+           $subject = $2 . '/' . $1 . $3;
+           while (exists $certs{$subject} && $iterations < $MAX_CHAIN_LENGTH)
+            {
+                ##! 16: 'while for subject: ' . $subject
+                $pkcs7  .= $certs{$subject}->{CERT}."\n\n";
+                last if ($subject eq $certs{$subject}->{ISSUER});
+                $subject = $certs{$subject}->{ISSUER};
+                $iterations++;
+            }
+       }
+    }
     ##! 2: "end"
     ##! 16: 'pkcs7: ' . $pkcs7
     if ($pkcs7 eq '') {
         OpenXPKI::Exception->throw(
             message => 
'I18N_OPENXPKI_CRYPTO_BACKEND_OPENSSL_COMMAND_PKCS7_GET_CHAIN_COULD_NOT_CREATE_CHAIN',
         );
     }
     return $pkcs7;
--- Server/Workflow/Validator/ApprovalSignature.pm.orig 2009-07-21 
17:08:00.000000000 +0200
+++ Server/Workflow/Validator/ApprovalSignature.pm      2009-07-31 
23:54:10.000000000 +0200
@@ -190,7 +190,7 @@
         );
     }
     ##! 16: 'signer subject: ' . $signer_subject
-    my $cfg_id = CTX('api')->get_config_id({ ID => $wf->id() });
+    $cfg_id = CTX('api')->get_config_id({ ID => $wf->id() });
     if (! defined $cfg_id) {
         # as this is called during creation, the cfg id is not defined
         # yet, so we use the current one
@@ -250,7 +250,13 @@
     }
     # FIXME - the code replacing the PKI realms is a duplication form
     # Authentication/X509.pm, this should only be in one place ...
-    my $realms = CTX('pki_realm');
+    $cfg_id = CTX('api')->get_config_id({ ID => $wf->id() });
+    if (! defined $cfg_id) {
+        # as this is called during creation, the cfg id is not defined
+        # yet, so we use the current one
+        $cfg_id = CTX('api')->get_current_config_id();
+    }
+    my $realms = CTX('pki_realm_by_cfg')->{$cfg_id};
     my @pki_realms = keys %{ $realms };
     my @trust_anchors;
     ##! 16: 'pki_realms: ' . Dumper \...@pki_realms
--- Server/Authentication/X509.pm.orig  2009-07-20 00:20:50.000000000 +0200
+++ Server/Authentication/X509.pm       2009-07-31 23:57:51.000000000 +0200
@@ -285,7 +285,17 @@
                 },
             );
         }
-        my $user = $signer_subject;
+       my $user = '';
+       #extract UID from certificate subject if defined, else use CN if 
available, else fallback to whole subject
+       if ($signer_subject =~ /(?<=UID=)([^,\+\/]+)/) {
+           $user = $1;
+       }
+       elsif ($signer_subject =~ /(?<=CN=)([^,\+\/]+)/) {
+           $user = $1;
+       }
+       else {
+            $user = $signer_subject;
+       }
         my $role = $cert_db->{ROLE};
         if (! defined $role) {
             # if the role is not defined in the certificate database,
--- Server/Authentication/ClientX509.pm.orig    2009-07-30 10:59:58.000000000 
+0200
+++ Server/Authentication/ClientX509.pm 2009-08-01 00:01:17.000000000 +0200
@@ -133,8 +133,8 @@
             },
         );
     }
-    my $notbefore = $cert_info->{BODY}->{NOTBEFORE};
-    my $notafter  = $cert_info->{BODY}->{NOTAFTER};
+    my $notbefore = 
DateTime::Format::DateParse->parse_datetime($cert_info->{BODY}->{NOTBEFORE}, 
'UTC');
+    my $notafter  = 
DateTime::Format::DateParse->parse_datetime($cert_info->{BODY}->{NOTAFTER}, 
'UTC');
     my $now = DateTime->now();
     if (DateTime->compare($now, $notbefore) == -1) {
         ##! 16: 'certificate is not yet valid'
@@ -166,6 +166,10 @@
             },
         );
     }
+    #check if certificate subject contains a UID field and if yes return that 
instead of the CN offered by the webserver
+    if ($cert_info->{BODY}->{SUBJECT} =~ /(?<=UID=)([^,\+\/]+)/) {
+        $username = $1;
+    }
     $self->{ROLE} = $role;
     $self->{USER} = $username;
Changes in chronological order:
=== ./Crypto/Backend/OpenSSL/Command/pkcs7_get_chain.pm ===
I first started trying to sign in with certificate challenge/response which led 
me to the following problem in the debug log:

For some reason the Certificate subject sent by the browser looked like that:
UID=root/CN=Root DemoCA,...

and was compared to the following certificate subject in the database:
CN=Root DemoCA+UID=root,...

I didn't look into the reason why the subject line looked that weird (exchanged 
separator, swapped elements)
but just added a few checks and changing the subject line on the fly (replacing 
/ with + and swapping the elements)
until it matches. Challenge/response signon was possible after these changes.
Not very clean, but it works for me. ;-)

=== ./Server/Workflow/Validator/ApprovalSignature.pm ===
When trying to approve CSRs with signature I think I received the
I18N_OPENXPKI_SERVER_WORKFLOW_VALIDATOR_APPROVALSIGNATURE_UNTRUSTED_CERTIFICATE 
error, even though I had defined the trust anchors
in workflow_validator_certificate_revocation_request.xml and 
workflow_validator_certificate_signing_request.xml.
Digging deeper in the debug logs I found that the module tried to load its 
config from CTX('pki_realm')
which is not allowed in Workflow context so this call didn't return the config 
and so the trust anchor variables didn't get filled.

=== ./Crypto/Profile/Base.pm ===
With Challenge Responese and Signature working, I moved on ot generating server 
certificates and trying to authenticate with
client certificates via webserver. This led first to the problem that apache2 
rejected my ssl server certificate - but only when
I presented a client certificate to it. Examining more closely the netscape 
certificate type field (and after endless searching for
openssl error codes) I found out that there were certificate types set which 
must not be set for a server sertificate to fulfil its role.
It must be either not set or have the server bit set.
Examinig the certificate issuance debug logs I found that the XML file was nor 
parsed correctly, so I copied the "space eliminator":
$bit =~ s/\s+//g; from elsewhere ;-)

Still - that didn't satisfy me, because now openssl command always failed when 
issuing server certificates, and I didn't figure out yet
(and I still didn't) how to actually get the verbose openssl command text 
output in the debug logs. But I accidentally stumbled across the
contents of the openssl.cnf file which had the nsCertType line set to:
nsCertType =

So I assumed this was the error, found out later that the @values array was 
empty which caused this, and added a security check in:
=== ./Crypto/Backend/OpenSSL/Config.pm ===
if (@bits) {
...
}
so the array was checked for existing elements before any line was written to 
the config file.

But this didn't make me happy either, because all other issued certificates had 
the nsCertType bit set, only the TLS cert didn't. I don't
know what happens if it isn't set - maybe all purposes are allowed then - so I 
decided this to be too risky and added the ssl_server bit
support both in Config.pm and Base.pm.

=== ./Server/Authentication/X509.pm ===
and
=== ./Server/Authentication/ClientX509.pm ===

I first thought asking on the mailing list if it was intended that when logging 
on with certificate challenge/response you wouldn't see
workflows and certificates created/issued when logging in using "basic 
authentication". When I finally tried logging on with a client
certificate using the webserver I saw that it wasn't: John Doe saw his 
certificates again ;-)
So I decided that these two Modules would deserve the "cosmetic" change to 
check in the certificate subject for the existence of a UID=
field and if set return its value as user name.
Regarding the ([^,\+\/]+)/): Checking for not + and not , might seem obvoius, 
but checking for not / ?
I was hasty after having been hacking around for several weeks and just wanted 
to make sure that if some cert subject weirdness is happening
(see at the beginning of this file) this possibility was ruled out too. If 
needed change to ([^,\+]+).

Attachment: modifiedfiles.7z
Description: Binary data

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
OpenXPKI-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openxpki-users

Reply via email to