directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From plusplusjia...@apache.org
Subject [20/21] directory-kerby git commit: Merge from pkinit-support branch.
Date Wed, 16 Dec 2015 06:17:07 GMT
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kdc-test/src/test/java/org/apache/kerby/kerberos/kdc/WithCertKdcTest.java
----------------------------------------------------------------------
diff --git a/kerby-kdc-test/src/test/java/org/apache/kerby/kerberos/kdc/WithCertKdcTest.java b/kerby-kdc-test/src/test/java/org/apache/kerby/kerberos/kdc/WithCertKdcTest.java
new file mode 100644
index 0000000..c47f334
--- /dev/null
+++ b/kerby-kdc-test/src/test/java/org/apache/kerby/kerberos/kdc/WithCertKdcTest.java
@@ -0,0 +1,115 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kdc;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.client.KrbPkinitClient;
+import org.apache.kerby.kerberos.kerb.server.KdcConfigKey;
+import org.apache.kerby.kerberos.kerb.server.KdcTestBase;
+import org.apache.kerby.kerberos.kerb.type.ticket.SgtTicket;
+import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
+import org.apache.kerby.pki.PkiLoader;
+import org.junit.Before;
+
+import java.io.InputStream;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ openssl genrsa -out cakey.pem 2048
+ openssl req -key cakey.pem -new -x509 -out cacert.pem -days 3650
+ vi extensions.kdc
+ openssl genrsa -out kdckey.pem 2048
+ openssl req -new -out kdc.req -key kdckey.pem
+ env REALM=SH.INTEL.COM openssl x509 -req -in kdc.req -CAkey cakey.pem \
+ -CA cacert.pem -out kdc.pem -days 365 -extfile extensions.kdc -extensions kdc_cert -CAcreateserial
+ */
+public class WithCertKdcTest extends KdcTestBase {
+    private PkiLoader pkiLoader;
+    private String clientPrincipal;
+    private String serverPrincipal;
+    private Certificate userCert;
+    private PrivateKey userKey;
+    private Certificate caCert;
+
+    @Before
+    public void setUp() throws Exception {
+        pkiLoader = new PkiLoader();
+
+        super.setUp();
+    }
+
+    @Override
+    protected void configKdcSeverAndClient() {
+        super.configKdcSeverAndClient();
+
+        String pkinitIdentity = getClass().getResource("/kdccerttest.pem").getPath() + ","
+                + getClass().getResource("/kdckey.pem").getPath();
+        String pkinitAnchors = getClass().getResource("/cacert.pem").getPath();
+        getKdcServer().getKdcConfig().setString(KdcConfigKey.PKINIT_IDENTITY, pkinitIdentity);
+        getKdcServer().getKdcConfig().setString(KdcConfigKey.PKINIT_ANCHORS, pkinitAnchors);
+    }
+
+    @Override
+    protected void setUpClient() throws Exception {
+        super.setUpClient();
+
+        loadCredentials();
+    }
+
+    @Override
+    protected void createPrincipals() throws KrbException {
+        super.createPrincipals();
+    }
+
+    //@Test
+    public void testPkinit() throws Exception {
+        assertThat(userCert).isNotNull();
+
+        getKrbClient().init();
+
+        TgtTicket tgt;
+        KrbPkinitClient pkinitClient = new KrbPkinitClient(getKrbClient());
+        try {
+            String userCertPath = getClass().getResource("/usercert.pem").getPath();
+            String userKeyPath = getClass().getResource("/userkey.pem").getPath();
+
+            tgt = pkinitClient.requestTgt(getClientPrincipal(), userCertPath, userKeyPath);
+        } catch (KrbException te) {
+            assertThat(te.getMessage().contains("timeout")).isTrue();
+            return;
+        }
+        assertThat(tgt).isNotNull();
+
+        serverPrincipal = getServerPrincipal();
+        SgtTicket tkt = getKrbClient().requestSgt(tgt, serverPrincipal);
+        assertThat(tkt).isNotNull();
+    }
+
+    private void loadCredentials() throws Exception {
+        InputStream res = getClass().getResourceAsStream("/usercert.pem");
+        userCert = pkiLoader.loadCerts(res).iterator().next();
+
+        res = getClass().getResourceAsStream("/userkey.pem");
+        userKey = pkiLoader.loadPrivateKey(res, null);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client-api-all/pom.xml
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client-api-all/pom.xml b/kerby-kerb/kerb-client-api-all/pom.xml
index 2649d5a..30bd6f6 100644
--- a/kerby-kerb/kerb-client-api-all/pom.xml
+++ b/kerby-kerb/kerb-client-api-all/pom.xml
@@ -56,6 +56,8 @@
                       <exclude>org.slf4j:slf4j-api</exclude>
                       <exclude>org.slf4j:slf4j-log4j12</exclude>
                       <exclude>org.apache.kerby:kerby-asn1</exclude>
+                      <exclude>org.bouncycastle:bcpkix-jdk15on</exclude>
+                      <exclude>org.bouncycastle:bcprov-jdk15on</exclude>
                     </excludes>
                   </artifactSet>
                 </configuration>

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/pom.xml
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/pom.xml b/kerby-kerb/kerb-client/pom.xml
index d75eaea..5bbc680 100644
--- a/kerby-kerb/kerb-client/pom.xml
+++ b/kerby-kerb/kerb-client/pom.xml
@@ -46,5 +46,10 @@
       <artifactId>kerb-util</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.bouncycastle</groupId>
+      <artifactId>bcpkix-jdk15on</artifactId>
+      <version>1.52</version>
+    </dependency>
   </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java
index 966f720..37161bf 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java
@@ -22,6 +22,7 @@ package org.apache.kerby.kerberos.kerb.client;
 import org.apache.kerby.kerberos.kerb.common.Krb5Conf;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -296,4 +297,19 @@ public class KrbConfig extends Krb5Conf {
     public List<EncryptionType> getDefaultTktEnctypes() {
         return getEncTypes(KrbConfigKey.DEFAULT_TKT_ENCTYPES, true, LIBDEFAULT);
     }
+
+    public List<String> getPkinitAnchors() {
+        return Arrays.asList(getStringArray(
+                KrbConfigKey.PKINIT_ANCHORS, true, LIBDEFAULT));
+    }
+
+    public List<String> getPkinitIdentities() {
+        return Arrays.asList(getStringArray(
+                KrbConfigKey.PKINIT_IDENTITIES, true, LIBDEFAULT));
+    }
+
+    public String getPkinitKdcHostName() {
+        return getString(
+                KrbConfigKey.PKINIT_KDC_HOSTNAME, true, LIBDEFAULT);
+    }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfigKey.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfigKey.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfigKey.java
index 0dd911a..4d5aa1d 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfigKey.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfigKey.java
@@ -56,8 +56,11 @@ public enum KrbConfigKey implements ConfigKey {
             + "camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4"),
     DEFAULT_TKT_ENCTYPES("aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 "
             + "des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac "
-            + "camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4");
+            + "camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4"),
 
+    PKINIT_ANCHORS(null),
+    PKINIT_IDENTITIES(null),
+    PKINIT_KDC_HOSTNAME();
 
     private Object defaultValue;
 

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java
index 3966d83..c46e38e 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java
@@ -108,6 +108,7 @@ public abstract class KrbHandler {
 
         KrbMessageType messageType = kdcRep.getMsgType();
         if (messageType == KrbMessageType.AS_REP) {
+
             kdcRequest.processResponse((KdcRep) kdcRep);
         } else if (messageType == KrbMessageType.TGS_REP) {
             kdcRequest.processResponse((KdcRep) kdcRep);

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbPkinitClient.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbPkinitClient.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbPkinitClient.java
index 2fb2712..ea6a700 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbPkinitClient.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbPkinitClient.java
@@ -23,9 +23,6 @@ import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
 import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
 
-import java.security.PrivateKey;
-import java.security.cert.Certificate;
-
 /**
  * A krb PKINIT client API for applications to interact with KDC using PKINIT.
  */
@@ -55,10 +52,13 @@ public class KrbPkinitClient {
      * @return TGT
      * @throws KrbException e
      */
-    public TgtTicket requestTgt(Certificate certificate,
-                                PrivateKey privateKey) throws KrbException {
+    public TgtTicket requestTgt(String principal, String certificate,
+                                String privateKey) throws KrbException {
         KOptions requestOptions = new KOptions();
-        requestOptions.add(KrbOption.PKINIT_X509_CERTIFICATE, certificate);
+        requestOptions.add(KrbOption.CLIENT_PRINCIPAL, principal);
+        requestOptions.add(KrbOption.USE_PKINIT);
+        requestOptions.add(KrbOption.PKINIT_USING_RSA);
+        requestOptions.add(KrbOption.PKINIT_X509_IDENTITY, certificate);
         requestOptions.add(KrbOption.PKINIT_X509_PRIVATE_KEY, privateKey);
         return krbClient.requestTgt(requestOptions);
     }
@@ -68,9 +68,11 @@ public class KrbPkinitClient {
      * @return TGT
      * @throws KrbException e
      */
-    public TgtTicket requestTgt() throws KrbException {
+    public TgtTicket requestTgt(String anchors) throws KrbException {
         KOptions requestOptions = new KOptions();
         requestOptions.add(KrbOption.USE_PKINIT_ANONYMOUS);
+        requestOptions.add(KrbOption.CLIENT_PRINCIPAL, "WELLKNOWN/ANONYMOUS");
+        requestOptions.add(KrbOption.PKINIT_X509_ANCHORS, anchors);
         return krbClient.requestTgt(requestOptions);
     }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/AbstractInternalKrbClient.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/AbstractInternalKrbClient.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/AbstractInternalKrbClient.java
index 25155c8..ac9e5ca 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/AbstractInternalKrbClient.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/AbstractInternalKrbClient.java
@@ -99,7 +99,11 @@ public abstract class AbstractInternalKrbClient implements InternalKrbClient {
             String principal = requestOptions.getStringOption(
                     KrbOption.CLIENT_PRINCIPAL);
             principal = fixPrincipal(principal);
-            asRequest.setClientPrincipal(new PrincipalName(principal));
+            PrincipalName principalName = new PrincipalName(principal);
+            if (requestOptions.contains(KrbOption.USE_PKINIT_ANONYMOUS)) {
+                principalName.setNameType(NameType.NT_WELLKNOWN);
+            }
+            asRequest.setClientPrincipal(principalName);
         }
         if (requestOptions.contains(KrbOption.SERVER_PRINCIPAL)) {
             String serverPrincipalName = requestOptions.getStringOption(KrbOption.SERVER_PRINCIPAL);

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/PreauthHandler.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/PreauthHandler.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/PreauthHandler.java
index fcd2d07..e3483aa 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/PreauthHandler.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/PreauthHandler.java
@@ -87,6 +87,7 @@ public class PreauthHandler {
             return;
         }
 
+        setPreauthOptions(kdcRequest, kdcRequest.getPreauthOptions());
         if (!preauthContext.hasInputPaData()) {
             tryFirst(kdcRequest, preauthContext.getOutputPaData());
             return;
@@ -94,8 +95,6 @@ public class PreauthHandler {
 
         // attemptETypeInfo(kdcRequest, preauthContext.getInputPaData());
 
-        setPreauthOptions(kdcRequest, kdcRequest.getPreauthOptions());
-
         prepareUserResponses(kdcRequest, preauthContext.getInputPaData());
 
         preauthContext.getUserResponser().respondQuestions();

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ClientConfiguration.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ClientConfiguration.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ClientConfiguration.java
new file mode 100644
index 0000000..58dbf29
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ClientConfiguration.java
@@ -0,0 +1,140 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kerb.client.preauth.pkinit;
+
+
+import org.apache.kerby.kerberos.kerb.crypto.dh.DhGroup;
+
+import javax.crypto.spec.DHParameterSpec;
+
+
+/**
+ * Client configuration settings.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ClientConfiguration {
+    /**
+     * The location of the user certificate.
+     */
+    private String certificatePath;
+
+    /**
+     * The CMS types to use.
+     */
+    private String cmsType;
+
+    /**
+     * Whether or not to use Diffie-Hellman.  The alternative is the "public key"
+     * method.
+     */
+    private boolean isDhUsed = true;
+
+    /**
+     * The Diffie-Hellman group to use.
+     */
+    private DHParameterSpec dhGroup = DhGroup.MODP_GROUP2;
+
+    /**
+     * Whether or not to reuse Diffie-Hellman keys.
+     */
+    private boolean isDhKeysReused;
+
+
+    /**
+     * @return the certificatePath
+     */
+    public String getCertificatePath() {
+        return certificatePath;
+    }
+
+
+    /**
+     * @param certificatePath the certificatePath to set
+     */
+    public void setCertificatePath(String certificatePath) {
+        this.certificatePath = certificatePath;
+    }
+
+
+    /**
+     * @return the cmsType
+     */
+    public String getCmsType() {
+        return cmsType;
+    }
+
+
+    /**
+     * @param cmsType the cmsType to set
+     */
+    public void setCmsType(String cmsType) {
+        this.cmsType = cmsType;
+    }
+
+
+    /**
+     * @return the isDhUsed
+     */
+    public boolean isDhUsed() {
+        return isDhUsed;
+    }
+
+
+    /**
+     * @param isDhUsed the isDhUsed to set
+     */
+    public void setDhUsed(boolean isDhUsed) {
+        this.isDhUsed = isDhUsed;
+    }
+
+
+    /**
+     * @return the dhGroup
+     */
+    public DHParameterSpec getDhGroup() {
+        return dhGroup;
+    }
+
+
+    /**
+     * @param dhGroup the dhGroup to set
+     */
+    public void setDhGroup(DHParameterSpec dhGroup) {
+        this.dhGroup = dhGroup;
+    }
+
+
+    /**
+     * @return the isDhKeysReused
+     */
+    public boolean isDhKeysReused() {
+        return isDhKeysReused;
+    }
+
+
+    /**
+     * @param isDhKeysReused the isDhKeysReused to set
+     */
+    public void setDhKeysReused(boolean isDhKeysReused) {
+        this.isDhKeysReused = isDhKeysReused;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/EnvelopedDataEngine.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/EnvelopedDataEngine.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/EnvelopedDataEngine.java
new file mode 100644
index 0000000..a72656a
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/EnvelopedDataEngine.java
@@ -0,0 +1,109 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kerb.client.preauth.pkinit;
+
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.cms.CMSEnvelopedData;
+import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.bc.BcCMSContentEncryptorBuilder;
+import org.bouncycastle.cms.bc.BcRSAKeyTransEnvelopedRecipient;
+import org.bouncycastle.cms.bc.BcRSAKeyTransRecipientInfoGenerator;
+import org.bouncycastle.crypto.util.PrivateKeyFactory;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * Encapsulates working with PKINIT enveloped data structures.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EnvelopedDataEngine {
+    /**
+     * Uses a certificate to encrypt data in a CMS EnvelopedData structure and
+     * returns the encoded EnvelopedData as bytes.
+     * <p/>
+     * 'encKeyPack' contains a CMS type ContentInfo encoded according to [RFC3852].
+     * The contentType field of the type ContentInfo is id-envelopedData (1.2.840.113549.1.7.3).
+     * The content field is an EnvelopedData. The contentType field for the type
+     * EnvelopedData is id-signedData (1.2.840.113549.1.7.2).
+     *
+     * @param dataToEnvelope
+     * @param certificate
+     * @return The EnvelopedData bytes.
+     * @throws IOException
+     * @throws CMSException
+     * @throws CertificateEncodingException
+     */
+    public static byte[] getEnvelopedReplyKeyPack(byte[] dataToEnvelope, X509Certificate certificate)
+            throws IOException, CMSException, CertificateEncodingException {
+        CMSProcessableByteArray content = new CMSProcessableByteArray(dataToEnvelope);
+
+        CMSEnvelopedDataGenerator envelopeGenerator = new CMSEnvelopedDataGenerator();
+        envelopeGenerator.addRecipientInfoGenerator(new BcRSAKeyTransRecipientInfoGenerator(
+                new JcaX509CertificateHolder(certificate)));
+        CMSEnvelopedData envdata = envelopeGenerator.generate(content,
+                new BcCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).build());
+
+        return envdata.getEncoded();
+    }
+
+
+    /**
+     * Uses a private key to decrypt data in a CMS EnvelopedData structure and
+     * returns the recovered (decrypted) data bytes.
+     *
+     * @param envelopedDataBytes
+     * @param privateKey
+     * @return The recovered (decrypted) data bytes.
+     * @throws IOException
+     * @throws CMSException
+     */
+    @SuppressWarnings("unchecked")
+    public static byte[] getUnenvelopedData(byte[] envelopedDataBytes,
+                                            PrivateKey privateKey) throws CMSException, IOException {
+        CMSEnvelopedData envelopedData = new CMSEnvelopedData(envelopedDataBytes);
+
+        // Set up to iterate through the recipients.
+        RecipientInformationStore recipients = envelopedData.getRecipientInfos();
+        Collection c = recipients.getRecipients();
+        Iterator it = c.iterator();
+
+        byte[] recData = new byte[0];
+        while (it.hasNext()) {
+            RecipientInformation recipient = (RecipientInformation) it.next();
+
+            recData = recipient.getContent(new BcRSAKeyTransEnvelopedRecipient(
+                    PrivateKeyFactory.createKey(PrivateKeyInfo.getInstance(privateKey.getEncoded()))));
+        }
+        return recData;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitContext.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitContext.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitContext.java
index b7902b4..8670b8c 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitContext.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitContext.java
@@ -6,25 +6,26 @@
  *  to you under the Apache License, Version 2.0 (the
  *  "License"); you may not use this file except in compliance
  *  with the License.  You may obtain a copy of the License at
- *  
+ *
  *    http://www.apache.org/licenses/LICENSE-2.0
- *  
+ *
  *  Unless required by applicable law or agreed to in writing,
  *  software distributed under the License is distributed on an
  *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  *  KIND, either express or implied.  See the License for the
  *  specific language governing permissions and limitations
- *  under the License. 
- *  
+ *  under the License.
+ *
  */
 package org.apache.kerby.kerberos.kerb.client.preauth.pkinit;
 
 import org.apache.kerby.kerberos.kerb.preauth.pkinit.IdentityOpts;
+import org.apache.kerby.kerberos.kerb.preauth.pkinit.PkinitPlgCryptoContext;
 import org.apache.kerby.kerberos.kerb.preauth.pkinit.PluginOpts;
 
 public class PkinitContext {
 
+    public PkinitPlgCryptoContext cryptoctx = new PkinitPlgCryptoContext();
     public PluginOpts pluginOpts = new PluginOpts();
     public IdentityOpts identityOpts = new IdentityOpts();
-
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitPreauth.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitPreauth.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitPreauth.java
index a1f2886..08b9988 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitPreauth.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitPreauth.java
@@ -20,32 +20,51 @@
 package org.apache.kerby.kerberos.kerb.client.preauth.pkinit;
 
 import org.apache.kerby.KOptions;
+import org.apache.kerby.asn1.type.Asn1Integer;
+import org.apache.kerby.asn1.type.Asn1ObjectIdentifier;
 import org.apache.kerby.kerberos.kerb.KrbException;
 import org.apache.kerby.kerberos.kerb.client.KrbContext;
 import org.apache.kerby.kerberos.kerb.client.KrbOption;
 import org.apache.kerby.kerberos.kerb.client.preauth.AbstractPreauthPlugin;
 import org.apache.kerby.kerberos.kerb.client.request.KdcRequest;
+import org.apache.kerby.kerberos.kerb.common.CheckSumUtil;
+import org.apache.kerby.kerberos.kerb.crypto.dh.DhClient;
+import org.apache.kerby.kerberos.kerb.crypto.dh.DhGroup;
 import org.apache.kerby.kerberos.kerb.preauth.PaFlag;
 import org.apache.kerby.kerberos.kerb.preauth.PaFlags;
 import org.apache.kerby.kerberos.kerb.preauth.PluginRequestContext;
+import org.apache.kerby.kerberos.kerb.preauth.pkinit.PkinitCrypto;
 import org.apache.kerby.kerberos.kerb.preauth.pkinit.PkinitIdenity;
 import org.apache.kerby.kerberos.kerb.preauth.pkinit.PkinitPreauthMeta;
 import org.apache.kerby.kerberos.kerb.type.KerberosTime;
+import org.apache.kerby.kerberos.kerb.type.base.CheckSum;
+import org.apache.kerby.kerberos.kerb.type.base.CheckSumType;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
-import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
 import org.apache.kerby.kerberos.kerb.type.pa.PaData;
 import org.apache.kerby.kerberos.kerb.type.pa.PaDataEntry;
 import org.apache.kerby.kerberos.kerb.type.pa.PaDataType;
 import org.apache.kerby.kerberos.kerb.type.pa.pkinit.AuthPack;
-import org.apache.kerby.kerberos.kerb.type.pa.pkinit.DHNonce;
 import org.apache.kerby.kerberos.kerb.type.pa.pkinit.PaPkAsReq;
 import org.apache.kerby.kerberos.kerb.type.pa.pkinit.PkAuthenticator;
 import org.apache.kerby.kerberos.kerb.type.pa.pkinit.TrustedCertifiers;
+import org.apache.kerby.x509.type.AlgorithmIdentifier;
+import org.apache.kerby.x509.type.DHParameter;
 import org.apache.kerby.x509.type.SubjectPublicKeyInfo;
-
-@SuppressWarnings("PMD")
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+@SuppressWarnings("PMD.UnusedFormalParameter")
 public class PkinitPreauth extends AbstractPreauthPlugin {
+    private static final Logger LOG = LoggerFactory.getLogger(PkinitPreauth.class);
 
     private PkinitContext pkinitContext;
 
@@ -87,15 +106,21 @@ public class PkinitPreauth extends AbstractPreauthPlugin {
         }
 
         if (options.contains(KrbOption.PKINIT_X509_ANCHORS)) {
-            pkinitContext.identityOpts.anchors.add(
-                    options.getStringOption(KrbOption.PKINIT_X509_ANCHORS));
+            String anchorsString = options.getStringOption(KrbOption.PKINIT_X509_ANCHORS);
+
+            List<String> anchors;
+            if (anchorsString == null) {
+                anchors = kdcRequest.getContext().getConfig().getPkinitAnchors();
+            } else {
+                anchors = Arrays.asList(anchorsString);
+            }
+            pkinitContext.identityOpts.anchors.addAll(anchors);
         }
 
         if (options.contains(KrbOption.PKINIT_USING_RSA)) {
             pkinitContext.pluginOpts.usingRsa =
                     options.getBooleanOption(KrbOption.PKINIT_USING_RSA, true);
         }
-
     }
 
     /**
@@ -123,6 +148,32 @@ public class PkinitPreauth extends AbstractPreauthPlugin {
                          PluginRequestContext requestContext,
                          PaData outPadata) throws KrbException {
 
+        /* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the
+         * same as in the AS_REQ. However, if we pick a different nonce, then we
+         * need to remember that info when AS_REP is returned. Here choose to
+         * reuse the AS_REQ nonce.
+         */
+        int nonce = kdcRequest.getChosenNonce();
+
+        // Get the current time
+        long now = System.currentTimeMillis();
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date(now));
+        int cusec = calendar.get(Calendar.SECOND);
+        KerberosTime ctime = new KerberosTime(now);
+
+        /* checksum of the encoded KDC-REQ-BODY */
+        CheckSum checkSum = null;
+        try {
+            checkSum = CheckSumUtil.makeCheckSum(CheckSumType.NIST_SHA,
+                    kdcRequest.getKdcReq().getReqBody().encode());
+        } catch (KrbException e) {
+            throw new KrbException("Fail to encode checksum.", e);
+        }
+
+        PaPkAsReq paPkAsReq = makePaPkAsReq(kdcRequest, (PkinitRequestContext) requestContext,
+                cusec, ctime, nonce, checkSum);
+        outPadata.addElement(makeEntry(paPkAsReq));
     }
 
     /**
@@ -163,51 +214,90 @@ public class PkinitPreauth extends AbstractPreauthPlugin {
 
     }
 
-    private PaPkAsReq makePaPkAsReq(PkinitContext pkinitContext, PkinitRequestContext reqCtx,
-                                    KerberosTime ctime, int cusec, int nonce, byte[] checksum,
-                                    PrincipalName client, PrincipalName server) {
+    private PaPkAsReq makePaPkAsReq(KdcRequest kdcRequest,
+                                    PkinitRequestContext reqCtx,
+                                    int cusec, KerberosTime ctime, int nonce, CheckSum checkSum) {
 
+        LOG.info("Making the PK_AS_REQ.");
         PaPkAsReq paPkAsReq = new PaPkAsReq();
         AuthPack authPack = new AuthPack();
-        SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo();
         PkAuthenticator pkAuthen = new PkAuthenticator();
 
-        boolean usingRsa = reqCtx.requestOpts.usingRsa;
+        boolean usingRsa = pkinitContext.pluginOpts.usingRsa;
         reqCtx.paType = PaDataType.PK_AS_REQ;
 
-        pkAuthen.setCtime(ctime);
         pkAuthen.setCusec(cusec);
+        pkAuthen.setCtime(ctime);
         pkAuthen.setNonce(nonce);
-        pkAuthen.setPaChecksum(checksum);
-
+        pkAuthen.setPaChecksum(checkSum.getChecksum());
         authPack.setPkAuthenticator(pkAuthen);
-        DHNonce dhNonce = new DHNonce();
-        authPack.setClientDhNonce(dhNonce);
-        authPack.setClientPublicValue(pubInfo);
-
         authPack.setsupportedCmsTypes(pkinitContext.pluginOpts.createSupportedCMSTypes());
 
-        if (usingRsa) {
-            System.out.println(); // DH case
+        if (!usingRsa) {
+            // DH case
+            LOG.info("DH key transport algorithm.");
+
+            String content = "0x06 07 2A 86 48 ce 3e 02 01";
+            Asn1ObjectIdentifier dhOid = PkinitCrypto.createOid(content);
+            AlgorithmIdentifier dhAlg = new AlgorithmIdentifier();
+            dhAlg.setAlgorithm(dhOid);
+
+            DhClient client = new DhClient();
+
+            DHPublicKey clientPubKey = null;
+            try {
+                clientPubKey = client.init(DhGroup.MODP_GROUP14);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+            kdcRequest.setDhClient(client);
+
+            DHParameterSpec type = clientPubKey.getParams();
+            BigInteger q = type.getP().shiftRight(1);
+            DHParameter dhParameter = new DHParameter();
+            dhParameter.setP(type.getP());
+            dhParameter.setG(type.getG());
+            dhParameter.setQ(q);
+            dhAlg.setParameters(dhParameter);
+
+            SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo();
+            pubInfo.setAlgorithm(dhAlg);
+
+            Asn1Integer publickey = new Asn1Integer(clientPubKey.getY());
+            pubInfo.setSubjectPubKey(publickey.encode());
+
+            authPack.setClientPublicValue(pubInfo);
+
+//            DHNonce dhNonce = new DHNonce();
+//            authPack.setClientDhNonce(dhNonce);
+
         } else {
-            authPack.setClientPublicValue(null);
+            LOG.info("RSA key transport algorithm");
+//            authPack.setClientPublicValue(null);
         }
 
-        byte[] signedAuthPack = signAuthPack(pkinitContext, reqCtx, authPack);
+        byte[] signedAuthPack = signAuthPack(authPack);
+
         paPkAsReq.setSignedAuthPack(signedAuthPack);
 
         TrustedCertifiers trustedCertifiers = pkinitContext.pluginOpts.createTrustedCertifiers();
         paPkAsReq.setTrustedCertifiers(trustedCertifiers);
 
-        byte[] kdcPkId = pkinitContext.pluginOpts.createIssuerAndSerial();
-        paPkAsReq.setKdcPkId(kdcPkId);
+//        byte[] kdcPkId = pkinitContext.pluginOpts.createIssuerAndSerial();
+//        paPkAsReq.setKdcPkId(kdcPkId);
 
         return paPkAsReq;
     }
 
-    private byte[] signAuthPack(PkinitContext pkinitContext,
-                                PkinitRequestContext reqCtx, AuthPack authPack) {
-        return null;
+    private byte[] signAuthPack(AuthPack authPack) {
+
+        Asn1ObjectIdentifier oid = pkinitContext.cryptoctx.getIdPkinitAuthDataOID();
+
+        byte[] signedDataBytes = PkinitCrypto.cmsSignedDataCreate(
+                authPack.encode(), oid, 3, null, null, null, null);
+
+        return signedDataBytes;
     }
 
     private void processReply(KdcRequest kdcRequest,
@@ -242,7 +332,7 @@ public class PkinitPreauth extends AbstractPreauthPlugin {
             //   switch (pde.getPaDataType()) {
             // TODO
             //    }
-            System.out.println();
+            System.out.println(pde.getPaDataType());
         }
 
         if (doAgain) {
@@ -263,4 +353,17 @@ public class PkinitPreauth extends AbstractPreauthPlugin {
         return paFlags;
     }
 
+    /**
+     * Make padata entry.
+     *
+     * @param paPkAsReq The PaPkAsReq
+     * @return PaDataEntry to be made.
+     */
+    private PaDataEntry makeEntry(PaPkAsReq paPkAsReq) throws KrbException {
+
+        PaDataEntry paDataEntry = new PaDataEntry();
+        paDataEntry.setPaDataType(PaDataType.PK_AS_REQ);
+        paDataEntry.setPaDataValue(paPkAsReq.encode());
+        return paDataEntry;
+    }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitRequestOpts.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitRequestOpts.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitRequestOpts.java
index 97e989f..9796f78 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitRequestOpts.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/PkinitRequestOpts.java
@@ -30,7 +30,7 @@ public class PkinitRequestOpts {
     // allow UPN-SAN instead of pkinit-SAN
     public boolean allowUpn = true;
     // selects DH or RSA based pkinit
-    public boolean usingRsa = true;
+    public boolean usingRsa = false;
     // require CRL for a CA (default is false)
     public boolean requireCrlChecking = false;
     // initial request DH modulus size (default=1024)

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ServerConfiguration.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ServerConfiguration.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ServerConfiguration.java
new file mode 100644
index 0000000..93dabcd
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ServerConfiguration.java
@@ -0,0 +1,143 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kerb.client.preauth.pkinit;
+
+import org.apache.kerby.kerberos.kerb.crypto.dh.DhGroup;
+import org.apache.kerby.kerberos.kerb.type.KerberosTime;
+
+import javax.crypto.spec.DHParameterSpec;
+
+/**
+ * Server configuration settings.
+ * <p/>
+ * TODO - Whether to use user cert vs. SAN binding.
+ * TODO - What trusted roots to use.
+ * TODO - The minimum allowed enc_types.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerConfiguration {
+    /**
+     * Whether or not to use Diffie-Hellman.  The alternative is the "public key"
+     * method.
+     */
+    private boolean isDhUsed;
+
+    /**
+     * The Diffie-Hellman group to use.
+     */
+    private DHParameterSpec dhGroup = DhGroup.MODP_GROUP2;
+
+    /**
+     * Whether or not to reuse Diffie-Hellman keys.
+     */
+    private boolean isDhKeysReused;
+
+    /**
+     * The length of time Diffie-Hellman keys can be reused.
+     */
+    private long dhKeyExpiration = KerberosTime.DAY;
+
+    /**
+     * The length of the Diffie-Hellman nonces.
+     */
+    private int dhNonceLength = 32;
+
+
+    /**
+     * @return the isDhUsed
+     */
+    public boolean isDhUsed() {
+        return isDhUsed;
+    }
+
+
+    /**
+     * @param isDhUsed the isDhUsed to set
+     */
+    public void setDhUsed(boolean isDhUsed) {
+        this.isDhUsed = isDhUsed;
+    }
+
+
+    /**
+     * @return the dhGroup
+     */
+    public DHParameterSpec getDhGroup() {
+        return dhGroup;
+    }
+
+
+    /**
+     * @param dhGroup the dhGroup to set
+     */
+    public void setDhGroup(DHParameterSpec dhGroup) {
+        this.dhGroup = dhGroup;
+    }
+
+
+    /**
+     * @return the isDhKeysReused
+     */
+    public boolean isDhKeysReused() {
+        return isDhKeysReused;
+    }
+
+
+    /**
+     * @param isDhKeysReused the isDhKeysReused to set
+     */
+    public void setDhKeysReused(boolean isDhKeysReused) {
+        this.isDhKeysReused = isDhKeysReused;
+    }
+
+
+    /**
+     * @return the dhKeyExpiration
+     */
+    public long getDhKeyExpiration() {
+        return dhKeyExpiration;
+    }
+
+
+    /**
+     * @param dhKeyExpiration the dhKeyExpiration to set
+     */
+    public void setDhKeyExpiration(long dhKeyExpiration) {
+        this.dhKeyExpiration = dhKeyExpiration;
+    }
+
+
+    /**
+     * @return the dhNonceLength
+     */
+    public int getDhNonceLength() {
+        return dhNonceLength;
+    }
+
+
+    /**
+     * @param dhNonceLength the dhNonceLength to set
+     */
+    public void setDhNonceLength(int dhNonceLength) {
+        this.dhNonceLength = dhNonceLength;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/SignedDataEngine.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/SignedDataEngine.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/SignedDataEngine.java
new file mode 100644
index 0000000..a63dfe9
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/SignedDataEngine.java
@@ -0,0 +1,210 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kerb.client.preauth.pkinit;
+
+import org.apache.kerby.kerberos.kerb.type.pa.pkinit.AuthPack;
+import org.apache.kerby.kerberos.kerb.type.pa.pkinit.KdcDHKeyInfo;
+import org.apache.kerby.kerberos.kerb.type.pa.pkinit.ReplyKeyPack;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Store;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Encapsulates working with PKINIT signed data structures.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SignedDataEngine {
+    private static final String ID_PKINIT_AUTHDATA = "1.3.6.1.5.2.3.1";
+    private static final String ID_PKINIT_DHKEYDATA = "1.3.6.1.5.2.3.2";
+    private static final String ID_PKINIT_RKEYDATA = "1.3.6.1.5.2.3.3";
+
+    /**
+     * Uses a private key to sign data in a CMS SignedData structure and returns
+     * the encoded CMS SignedData as bytes.
+     * <p/>
+     * 'signedAuthPack' contains a CMS type ContentInfo encoded according to [RFC3852].
+     * The contentType field of the type ContentInfo is id-signedData (1.2.840.113549.1.7.2),
+     * and the content field is a SignedData.
+     * <p/>
+     * The eContentType field for the type SignedData is id-pkinit-authData (1.3.6.1.5.2.3.1),
+     * and the eContent field contains the DER encoding of the type AuthPack.
+     *
+     * @param privateKey
+     * @param certificate
+     * @param authPack
+     * @return The CMS SignedData bytes.
+     * @throws OperatorCreationException
+     * @throws CertificateEncodingException
+     * @throws CMSException
+     * @throws IOException
+     */
+    public static byte[] getSignedAuthPack(PrivateKey privateKey, X509Certificate certificate,
+                                           AuthPack authPack)
+            throws OperatorCreationException, CertificateEncodingException, CMSException, IOException {
+        return getSignedData(privateKey, certificate, authPack.encode(), ID_PKINIT_AUTHDATA);
+    }
+
+
+    /**
+     * Uses a private key to sign data in a CMS SignedData structure and returns
+     * the encoded CMS SignedData as bytes.
+     * <p/>
+     * 'dhSignedData' contains a CMS type ContentInfo encoded according to [RFC3852].
+     * The contentType field of the type ContentInfo is id-signedData (1.2.840.113549.1.7.2),
+     * and the content field is a SignedData.
+     * <p/>
+     * The eContentType field for the type SignedData is id-pkinit-DHKeyData (1.3.6.1.5.2.3.2),
+     * and the eContent field contains the DER encoding of the type KDCDHKeyInfo.
+     *
+     * @param privateKey
+     * @param certificate
+     * @param kdcDhKeyInfo
+     * @return The CMS SignedData bytes.
+     * @throws OperatorCreationException
+     * @throws CertificateEncodingException
+     * @throws CMSException
+     * @throws IOException
+     */
+    public static byte[] getSignedKdcDhKeyInfo(PrivateKey privateKey, X509Certificate certificate,
+                                               KdcDHKeyInfo kdcDhKeyInfo)
+            throws OperatorCreationException, CertificateEncodingException, CMSException, IOException {
+        return getSignedData(privateKey, certificate, kdcDhKeyInfo.encode(), ID_PKINIT_DHKEYDATA);
+    }
+
+
+    /**
+     * Uses a private key to sign data in a CMS SignedData structure and returns
+     * the encoded CMS SignedData as bytes.
+     * <p/>
+     * Selected when public key encryption is used.
+     * <p/>
+     * The eContentType field for the inner type SignedData (when unencrypted) is
+     * id-pkinit-rkeyData (1.3.6.1.5.2.3.3) and the eContent field contains the
+     * DER encoding of the type ReplyKeyPack.
+     *
+     * @param privateKey
+     * @param certificate
+     * @param replyKeyPack
+     * @return The CMS SignedData bytes.
+     * @throws OperatorCreationException
+     * @throws CertificateEncodingException
+     * @throws CMSException
+     * @throws IOException
+     */
+    public static byte[] getSignedReplyKeyPack(PrivateKey privateKey, X509Certificate certificate,
+                                               ReplyKeyPack replyKeyPack)
+            throws OperatorCreationException, CertificateEncodingException, CMSException, IOException {
+        return getSignedData(privateKey, certificate, replyKeyPack.encode(), ID_PKINIT_RKEYDATA);
+    }
+
+
+    static byte[] getSignedData(PrivateKey privateKey, X509Certificate certificate, byte[] dataToSign,
+                                String eContentType) throws IOException, OperatorCreationException,
+            CertificateEncodingException, CMSException {
+
+        if (Security.getProvider("BC") == null) {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+
+
+        List certList = new ArrayList();
+        certList.add(certificate);
+        Store certs = new JcaCertStore(certList);
+
+        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
+
+        gen.addSignerInfoGenerator(
+                new JcaSimpleSignerInfoGeneratorBuilder()
+                        .setProvider("BC")
+                        .build("SHA1withRSA", privateKey, certificate));
+
+        gen.addCertificates(certs);
+
+        ASN1ObjectIdentifier asn1ObjectIdentifier = new ASN1ObjectIdentifier(eContentType);
+        CMSTypedData msg = new CMSProcessableByteArray(asn1ObjectIdentifier, dataToSign);
+        CMSSignedData s = gen.generate(msg, true);
+
+        return s.getEncoded();
+    }
+
+    /**
+     * Validates a CMS SignedData using the public key corresponding to the private
+     * key used to sign the structure.
+     *
+     * @param s
+     * @return true if the signature is valid.
+     * @throws Exception
+     */
+    public static boolean validateSignedData(CMSSignedData s) throws Exception {
+
+        Store certStore = s.getCertificates();
+        Store crlStore = s.getCRLs();
+        SignerInformationStore signers = s.getSignerInfos();
+
+        Collection c = signers.getSigners();
+        Iterator it = c.iterator();
+
+        while (it.hasNext()) {
+            SignerInformation signer = (SignerInformation) it.next();
+            Collection certCollection = certStore.getMatches(signer.getSID());
+
+            Iterator certIt = certCollection.iterator();
+            X509CertificateHolder cert = (X509CertificateHolder) certIt.next();
+
+            if (!signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
+                return false;
+            }
+        }
+
+        Collection certColl = certStore.getMatches(null);
+        Collection crlColl = crlStore.getMatches(null);
+
+        if (certColl.size() != s.getCertificates().getMatches(null).size()
+                || crlColl.size() != s.getCRLs().getMatches(null).size()) {
+            return false;
+        }
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/CertificateChainFactory.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/CertificateChainFactory.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/CertificateChainFactory.java
new file mode 100644
index 0000000..8434f50
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/CertificateChainFactory.java
@@ -0,0 +1,284 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kerb.client.preauth.pkinit.certs;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+
+/**
+ * Factory for dynamically generating certificate chains.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CertificateChainFactory {
+    /**
+     * The log for this class.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(CertificateChainFactory.class);
+
+    private static int trustAnchorLevel = 2;
+
+    private static int intermediateLevel = 1;
+
+    private static int endEntityLevel = 0;
+
+    private static SecureRandom secureRandom = new SecureRandom();
+
+    private static String container =
+            "C=US, ST=Maryland, L=Forest Hill, O=Apache Software Foundation, OU=Apache Directory, CN=";
+
+    private static boolean isGenerated = false;
+
+    private static boolean isInitialized = false;
+
+    private static X509Certificate[] clientChain;
+
+    private static X509Certificate[] kdcChain;
+
+    private static PrivateKey clientPrivateKey;
+
+    private static PrivateKey kdcPrivateKey;
+
+
+    public static X509Certificate[] getKdcChain() throws Exception {
+        init();
+
+        return kdcChain;
+    }
+
+
+    public static X509Certificate[] getClientChain() throws Exception {
+        init();
+
+        return clientChain;
+    }
+
+
+    public static PrivateKey getKdcPrivateKey() throws Exception {
+        init();
+
+        return kdcPrivateKey;
+    }
+
+
+    public static PrivateKey getClientPrivateKey() throws Exception {
+        init();
+
+        return clientPrivateKey;
+    }
+
+
+    private static void init() throws Exception {
+        if (!isInitialized) {
+            initClientChain();
+            initKdcChain();
+            isInitialized = true;
+        }
+    }
+
+
+    private static void initClientChain() throws Exception {
+        // Make trust anchor.
+        String friendlyName = "Test Root CA";
+        String dn = container + friendlyName;
+        int validityDays = 730;
+
+        KeyPair keyPair = getKeyPair(trustAnchorLevel);
+        PrivateKey trustAnchorPrivateKey = keyPair.getPrivate();
+        PublicKey trustAnchorPublicKey = keyPair.getPublic();
+
+        X509Certificate trustAnchorCert = TrustAnchorGenerator.generate(trustAnchorPublicKey, trustAnchorPrivateKey,
+                dn, validityDays, friendlyName);
+
+        trustAnchorCert.checkValidity();
+        trustAnchorCert.verify(trustAnchorPublicKey);
+
+        LOG.debug("Generated cert for friendly name '{}', valid for {} days.", friendlyName, validityDays);
+
+        // Make intermediate client CA.
+        friendlyName = "Client Test CA 1";
+        dn = container + friendlyName;
+        validityDays = 365;
+
+        keyPair = getKeyPair(intermediateLevel);
+        PrivateKey clientCaPrivateKey = keyPair.getPrivate();
+        PublicKey clientCaPublicKey = keyPair.getPublic();
+
+        X509Certificate clientCaCert = IntermediateCaGenerator.generate(trustAnchorCert, trustAnchorPrivateKey,
+                clientCaPublicKey, dn, validityDays, friendlyName);
+
+        clientCaCert.checkValidity();
+        clientCaCert.verify(trustAnchorPublicKey);
+
+        LOG.debug("Generated cert for friendly name '{}', valid for {} days.", friendlyName, validityDays);
+
+        // Make client certificate.
+        friendlyName = "hnelson@EXAMPLE.COM UPN";
+        dn = container + friendlyName;
+        validityDays = 30;
+
+        keyPair = getKeyPair(endEntityLevel);
+        clientPrivateKey = keyPair.getPrivate();
+        PublicKey clientPublicKey = keyPair.getPublic();
+
+        X509Certificate clientCert = EndEntityGenerator.generate(clientCaCert, clientCaPrivateKey, clientPublicKey,
+                dn, validityDays, friendlyName);
+
+        clientCert.checkValidity();
+        clientCert.verify(clientCaPublicKey);
+
+        LOG.debug("Generated cert for friendly name '{}', valid for {} days.", friendlyName, validityDays);
+
+        // Build client chain.
+        clientChain = new X509Certificate[3];
+
+        clientChain[2] = trustAnchorCert;
+        clientChain[1] = clientCaCert;
+        clientChain[0] = clientCert;
+    }
+
+
+    private static void initKdcChain() throws Exception {
+        // Make trust anchor.
+        String friendlyName = "Test Root CA";
+        String dn = container + friendlyName;
+        int validityDays = 730;
+
+        KeyPair keyPair = getKeyPair(trustAnchorLevel);
+        PrivateKey trustAnchorPrivateKey = keyPair.getPrivate();
+        PublicKey trustAnchorPublicKey = keyPair.getPublic();
+
+        X509Certificate trustAnchorCert = TrustAnchorGenerator.generate(trustAnchorPublicKey, trustAnchorPrivateKey,
+                dn, validityDays, friendlyName);
+
+        trustAnchorCert.checkValidity();
+        trustAnchorCert.verify(trustAnchorPublicKey);
+
+        LOG.debug("Generated cert for friendly name '{}', valid for {} days.", friendlyName, validityDays);
+
+        // Make intermediate KDC CA.
+        friendlyName = "KDC Test CA 1";
+        dn = container + friendlyName;
+        validityDays = 365;
+
+        keyPair = getKeyPair(intermediateLevel);
+        PrivateKey kdcCaPrivateKey = keyPair.getPrivate();
+        PublicKey kdcCaPublicKey = keyPair.getPublic();
+
+        X509Certificate kdcCaCert = IntermediateCaGenerator.generate(trustAnchorCert, trustAnchorPrivateKey,
+                kdcCaPublicKey, dn, validityDays, friendlyName);
+
+        kdcCaCert.checkValidity();
+        kdcCaCert.verify(trustAnchorPublicKey);
+
+        LOG.debug("Generated cert for friendly name '{}', valid for {} days.", friendlyName, validityDays);
+
+        // Make KDC certificate.
+        friendlyName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM KDC";
+        dn = container + friendlyName;
+        validityDays = 30;
+
+        keyPair = getKeyPair(endEntityLevel);
+        kdcPrivateKey = keyPair.getPrivate();
+        PublicKey kdcPublicKey = keyPair.getPublic();
+
+        X509Certificate kdcCert = EndEntityGenerator.generate(kdcCaCert, kdcCaPrivateKey, kdcPublicKey, dn,
+                validityDays, friendlyName);
+
+        kdcCert.checkValidity();
+        kdcCert.verify(kdcCaPublicKey);
+
+        LOG.debug("Generated cert for friendly name '{}', valid for {} days.", friendlyName, validityDays);
+
+        // Build KDC chain.
+        kdcChain = new X509Certificate[3];
+
+        kdcChain[2] = trustAnchorCert;
+        kdcChain[1] = kdcCaCert;
+        kdcChain[0] = kdcCert;
+    }
+
+
+    /**
+     * Get a key pair for the new certificate.  Depending on the static constant
+     * 'isGenerated', these key pairs can be dynamically generated (slower) or
+     * built from static constant values (faster).
+     *
+     * @param level
+     * @return The key pair.
+     * @throws NoSuchAlgorithmException
+     * @throws NoSuchProviderException
+     * @throws InvalidKeySpecException
+     */
+    private static KeyPair getKeyPair(int level) throws NoSuchAlgorithmException, NoSuchProviderException,
+            InvalidKeySpecException {
+        if (isGenerated) {
+            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+            keyGen.initialize(1024, secureRandom);
+            return keyGen.generateKeyPair();
+        } else {
+            return getStaticKeyPair(level);
+        }
+    }
+
+
+    /**
+     * Get a key pair generated using static key values.  This is much faster than
+     * dynamically generating key values.
+     *
+     * @param level
+     * @return The static key pair.
+     * @throws NoSuchAlgorithmException
+     * @throws NoSuchProviderException
+     * @throws InvalidKeySpecException
+     */
+    private static KeyPair getStaticKeyPair(int level) throws NoSuchAlgorithmException, NoSuchProviderException,
+            InvalidKeySpecException {
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
+
+        switch (level) {
+            case 2:
+                PrivateKey caPrivKey = keyFactory.generatePrivate(KeyPairSpec.caPrivKeySpec);
+                PublicKey caPubKey = keyFactory.generatePublic(KeyPairSpec.caPubKeySpec);
+                return new KeyPair(caPubKey, caPrivKey);
+            case 1:
+                PrivateKey intPrivKey = keyFactory.generatePrivate(KeyPairSpec.intPrivKeySpec);
+                PublicKey intPubKey = keyFactory.generatePublic(KeyPairSpec.intPubKeySpec);
+                return new KeyPair(intPubKey, intPrivKey);
+            case 0:
+            default:
+                PrivateKey privKey = keyFactory.generatePrivate(KeyPairSpec.privKeySpec);
+                PublicKey pubKey = keyFactory.generatePublic(KeyPairSpec.pubKeySpec);
+                return new KeyPair(pubKey, privKey);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/EndEntityGenerator.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/EndEntityGenerator.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/EndEntityGenerator.java
new file mode 100644
index 0000000..e2bf201
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/EndEntityGenerator.java
@@ -0,0 +1,272 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kerb.client.preauth.pkinit.certs;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.DERBMPString;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.GeneralNamesBuilder;
+import org.bouncycastle.asn1.x509.KeyPurposeId;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.jce.PrincipalUtil;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * Generates an X.509 "end entity" certificate programmatically.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+@SuppressWarnings({"PMD.UnusedPrivateField"})
+public class EndEntityGenerator {
+    /**
+     * id-pkinit-san OBJECT IDENTIFIER ::=
+     * { iso(1) org(3) dod(6) internet(1) security(5) kerberosv5(2) x509SanAN (2) }
+     */
+    private static final DERObjectIdentifier ID_PKINIT_SAN = new DERObjectIdentifier("1.3.6.1.5.2.2");
+
+    /**
+     * id-pkinit-KPClientAuth OBJECT IDENTIFIER ::=
+     * { iso(1) org(3) dod(6) internet(1) security(5) kerberosv5(2) pkinit(3) keyPurposeClientAuth(4) }
+     * -- PKINIT client authentication.
+     * -- Key usage bits that MUST be consistent:
+     * -- digitalSignature.
+     */
+    private static final DERObjectIdentifier ID_PKINIT_KPCLIENTAUTH = new DERObjectIdentifier("1.3.6.1.5.2.3.4");
+
+    /**
+     * id-pkinit-KPKdc OBJECT IDENTIFIER ::=
+     * { iso(1) org(3) dod(6) internet(1) security(5) kerberosv5(2) pkinit(3) keyPurposeKdc(5) }
+     * -- Signing KDC responses.
+     * -- Key usage bits that MUST be consistent:
+     * -- digitalSignature.
+     */
+    private static final DERObjectIdentifier ID_PKINIT_KPKDC = new DERObjectIdentifier("1.3.6.1.5.2.3.5");
+
+    private static final DERObjectIdentifier ID_MS_KP_SC_LOGON = new DERObjectIdentifier("1.3.6.1.4.1.311.20.2.2");
+
+    private static final DERObjectIdentifier ID_MS_SAN_SC_LOGON_UPN = new DERObjectIdentifier("1.3.6.1.4.1.311.20.2.3");
+
+
+    /**
+     * Generate certificate.
+     *
+     * @param issuerCert
+     * @param issuerPrivateKey
+     * @param publicKey
+     * @param dn
+     * @param validityDays
+     * @param friendlyName
+     * @return The certificate.
+     * @throws InvalidKeyException
+     * @throws SecurityException
+     * @throws SignatureException
+     * @throws NoSuchAlgorithmException
+     * @throws DataLengthException
+     * @throws CertificateException
+     */
+    public static X509Certificate generate(X509Certificate issuerCert, PrivateKey issuerPrivateKey,
+                                           PublicKey publicKey, String dn, int validityDays,
+                                           String friendlyName)
+            throws InvalidKeyException, SecurityException, SignatureException,
+            NoSuchAlgorithmException, DataLengthException, CertificateException {
+        X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+
+        // Set certificate attributes.
+        certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+
+        certGen.setIssuerDN(PrincipalUtil.getSubjectX509Principal(issuerCert));
+        certGen.setSubjectDN(new X509Principal(dn));
+
+        certGen.setNotBefore(new Date());
+
+        Calendar expiry = Calendar.getInstance();
+        expiry.add(Calendar.DAY_OF_YEAR, validityDays);
+
+        certGen.setNotAfter(expiry.getTime());
+
+        certGen.setPublicKey(publicKey);
+        certGen.setSignatureAlgorithm("SHA1WithRSAEncryption");
+
+        certGen
+                .addExtension(X509Extensions.SubjectKeyIdentifier, false,
+                        new SubjectKeyIdentifier(getDigest(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()))));
+
+        // MAY set BasicConstraints=false or not at all.
+        certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false));
+
+        certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
+                new AuthorityKeyIdentifierStructure(issuerCert));
+
+        certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature
+                | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment));
+
+        ASN1EncodableVector keyPurposeVector = new ASN1EncodableVector();
+        keyPurposeVector.add(KeyPurposeId.id_kp_smartcardlogon);
+        //keyPurposeVector.add( KeyPurposeId.id_kp_serverAuth );
+        DERSequence keyPurposeOids = new DERSequence(keyPurposeVector);
+
+        // If critical, will throw unsupported EKU.
+        certGen.addExtension(X509Extensions.ExtendedKeyUsage, false, keyPurposeOids);
+
+        ASN1EncodableVector pkinitSanVector = new ASN1EncodableVector();
+        pkinitSanVector.add(ID_PKINIT_SAN);
+        pkinitSanVector.add(new DERTaggedObject(0, new DERSequence()));
+        DERSequence pkinitSan = new DERSequence(pkinitSanVector);
+
+        String dnsName = "localhost";
+
+        GeneralName name1 = new GeneralName(GeneralName.otherName, pkinitSan);
+        GeneralName name2 = new GeneralName(GeneralName.dNSName, dnsName);
+
+        GeneralNamesBuilder genNamesBuilder = new GeneralNamesBuilder();
+
+        genNamesBuilder.addName(name1);
+        genNamesBuilder.addName(name2);
+
+        GeneralNames sanGeneralNames = genNamesBuilder.build();
+
+        certGen.addExtension(X509Extensions.SubjectAlternativeName, true, sanGeneralNames);
+
+        /*
+         * The KDC MAY require the presence of an Extended Key Usage (EKU) KeyPurposeId
+         * [RFC3280] id-pkinit-KPClientAuth in the extensions field of the client's
+         * X.509 certificate.
+         */
+
+        /*
+         * The digitalSignature key usage bit [RFC3280] MUST be asserted when the
+         * intended purpose of the client's X.509 certificate is restricted with
+         * the id-pkinit-KPClientAuth EKU.
+         */
+
+        /*
+         * KDCs implementing this requirement SHOULD also accept the EKU KeyPurposeId
+         * id-ms-kp-sc-logon (1.3.6.1.4.1.311.20.2.2) as meeting the requirement, as
+         * there are a large number of X.509 client certificates deployed for use
+         * with PKINIT that have this EKU.
+         */
+
+        // KDC
+        /*
+         * In addition, unless the client can otherwise verify that the public key
+         * used to verify the KDC's signature is bound to the KDC of the target realm,
+         * the KDC's X.509 certificate MUST contain a Subject Alternative Name extension
+         * [RFC3280] carrying an AnotherName whose type-id is id-pkinit-san (as defined
+         * in Section 3.2.2) and whose value is a KRB5PrincipalName that matches the
+         * name of the TGS of the target realm (as defined in Section 7.3 of [RFC4120]).
+         */
+
+        /*
+         * Unless the client knows by some other means that the KDC certificate is
+         * intended for a Kerberos KDC, the client MUST require that the KDC certificate
+         * contains the EKU KeyPurposeId [RFC3280] id-pkinit-KPKdc.
+         */
+
+        /*
+         * The digitalSignature key usage bit [RFC3280] MUST be asserted when the
+         * intended purpose of the KDC's X.509 certificate is restricted with the
+         * id-pkinit-KPKdc EKU.
+         */
+
+        /*
+         * If the KDC certificate contains the Kerberos TGS name encoded as an id-pkinit-san
+         * SAN, this certificate is certified by the issuing CA as a KDC certificate,
+         * therefore the id-pkinit-KPKdc EKU is not required.
+         */
+
+        /*
+         * KDC certificates issued by Windows 2000 Enterprise CAs contain a dNSName
+         * SAN with the DNS name of the host running the KDC, and the id-kp-serverAuth
+         * EKU [RFC3280].
+         */
+
+        /*
+         * KDC certificates issued by Windows 2003 Enterprise CAs contain a dNSName
+         * SAN with the DNS name of the host running the KDC, the id-kp-serverAuth
+         * EKU, and the id-ms-kp-sc-logon EKU.
+         */
+
+        /*
+         * RFC: KDC certificates with id-pkinit-san SAN as specified in this RFC.
+         * 
+         * MS:  dNSName SAN containing the domain name of the KDC
+         *      id-pkinit-KPKdc EKU
+         *      id-kp-serverAuth EKU.
+         */
+
+        /*
+         * Client certificates accepted by Windows 2000 and Windows 2003 Server KDCs
+         * must contain an id-ms-san-sc-logon-upn (1.3.6.1.4.1.311.20.2.3) SAN and
+         * the id-ms-kp-sc-logon EKU.  The id-ms-san-sc-logon-upn SAN contains a
+         * UTF8-encoded string whose value is that of the Directory Service attribute
+         * UserPrincipalName of the client account object, and the purpose of including
+         * the id-ms-san-sc-logon-upn SAN in the client certificate is to validate
+         * the client mapping (in other words, the client's public key is bound to
+         * the account that has this UserPrincipalName value).
+         */
+
+        X509Certificate cert = certGen.generate(issuerPrivateKey);
+
+        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) cert;
+
+        bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString(friendlyName));
+        bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
+                new SubjectKeyIdentifier(getDigest(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()))));
+
+        return cert;
+    }
+
+    private static byte[] getDigest(SubjectPublicKeyInfo spki) {
+        Digest digest = new SHA1Digest();
+        byte[] resBuf = new byte[digest.getDigestSize()];
+
+        byte[] bytes = spki.getPublicKeyData().getBytes();
+        digest.update(bytes, 0, bytes.length);
+        digest.doFinal(resBuf, 0);
+        return resBuf;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/IntermediateCaGenerator.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/IntermediateCaGenerator.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/IntermediateCaGenerator.java
new file mode 100644
index 0000000..ec977b0
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/IntermediateCaGenerator.java
@@ -0,0 +1,130 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.kerb.client.preauth.pkinit.certs;
+
+
+import org.bouncycastle.asn1.DERBMPString;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.jce.PrincipalUtil;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.Date;
+
+
+/**
+ * Generates an X.509 "intermediate CA" certificate programmatically.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class IntermediateCaGenerator {
+    /**
+     * Create certificate.
+     *
+     * @param issuerCert
+     * @param issuerPrivateKey
+     * @param publicKey
+     * @param dn
+     * @param validityDays
+     * @param friendlyName
+     * @return The certificate.
+     * @throws InvalidKeyException
+     * @throws SecurityException
+     * @throws SignatureException
+     * @throws NoSuchAlgorithmException
+     * @throws DataLengthException
+     * @throws CertificateException
+     */
+    public static X509Certificate generate(X509Certificate issuerCert, PrivateKey issuerPrivateKey,
+                                           PublicKey publicKey, String dn, int validityDays,
+                                           String friendlyName)
+            throws InvalidKeyException, SecurityException, SignatureException,
+            NoSuchAlgorithmException, DataLengthException, CertificateException {
+        X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+
+        // Set certificate attributes.
+        certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+
+        certGen.setIssuerDN(PrincipalUtil.getSubjectX509Principal(issuerCert));
+        certGen.setSubjectDN(new X509Principal(dn));
+
+        certGen.setNotBefore(new Date());
+
+        Calendar expiry = Calendar.getInstance();
+        expiry.add(Calendar.DAY_OF_YEAR, validityDays);
+
+        certGen.setNotAfter(expiry.getTime());
+
+        certGen.setPublicKey(publicKey);
+        certGen.setSignatureAlgorithm("SHA1WithRSAEncryption");
+
+        certGen
+                .addExtension(X509Extensions.SubjectKeyIdentifier, false,
+                        new SubjectKeyIdentifier(getDigest(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()))));
+
+        certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(0));
+
+        certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
+                new AuthorityKeyIdentifierStructure(issuerCert));
+
+        certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature
+                | KeyUsage.keyCertSign | KeyUsage.cRLSign));
+
+        X509Certificate cert = certGen.generate(issuerPrivateKey);
+
+        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) cert;
+
+        bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString(friendlyName));
+        bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
+                new SubjectKeyIdentifier(getDigest(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()))));
+
+        return cert;
+    }
+
+    private static byte[] getDigest(SubjectPublicKeyInfo spki) {
+        Digest digest = new SHA1Digest();
+        byte[] resBuf = new byte[digest.getDigestSize()];
+
+        byte[] bytes = spki.getPublicKeyData().getBytes();
+        digest.update(bytes, 0, bytes.length);
+        digest.doFinal(resBuf, 0);
+        return resBuf;
+    }
+}


Mime
View raw message