You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by pl...@apache.org on 2015/09/24 09:07:10 UTC
[2/2] directory-kerby git commit: DIRKRB-415 Consolidate and merge
the existing kerberos-pkinit implementation in directory to Kerby.
DIRKRB-415 Consolidate and merge the existing kerberos-pkinit implementation in directory to Kerby.
Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/commit/c98ab976
Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/c98ab976
Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/c98ab976
Branch: refs/heads/pkinit-support
Commit: c98ab976a7f384959fe2dd3107b325afbe23d844
Parents: 42bcec8
Author: plusplusjiajia <ji...@intel.com>
Authored: Thu Sep 24 15:12:24 2015 +0800
Committer: plusplusjiajia <ji...@intel.com>
Committed: Thu Sep 24 15:12:24 2015 +0800
----------------------------------------------------------------------
kerby-kerb/kerb-client/pom.xml | 10 +
.../preauth/pkinit/ClientConfiguration.java | 149 ++++++++++
.../kerb/client/preauth/pkinit/DhClient.java | 135 +++++++++
.../kerb/client/preauth/pkinit/DhGroup.java | 142 ++++++++++
.../kerb/client/preauth/pkinit/DhServer.java | 142 ++++++++++
.../preauth/pkinit/EnvelopedDataEngine.java | 130 +++++++++
.../client/preauth/pkinit/OctetString2Key.java | 103 +++++++
.../preauth/pkinit/ServerConfiguration.java | 153 ++++++++++
.../client/preauth/pkinit/SignedDataEngine.java | 205 ++++++++++++++
.../pkinit/certs/CertificateChainFactory.java | 283 +++++++++++++++++++
.../pkinit/certs/EndEntityGenerator.java | 257 +++++++++++++++++
.../pkinit/certs/IntermediateCaGenerator.java | 116 ++++++++
.../preauth/pkinit/certs/KeyPairSpec.java | 114 ++++++++
.../pkinit/certs/TrustAnchorGenerator.java | 110 +++++++
.../pkinit/CertificateChainFactoryTest.java | 119 ++++++++
.../kerb/client/preauth/pkinit/DhGroupTest.java | 55 ++++
.../preauth/pkinit/DhKeyAgreementTest.java | 109 +++++++
.../preauth/pkinit/EnvelopedDataEngineTest.java | 133 +++++++++
.../preauth/pkinit/OctetString2KeyTest.java | 230 +++++++++++++++
.../preauth/pkinit/SignedDataEngineTest.java | 135 +++++++++
.../certs/CertificateChainFactoryTest.java | 116 ++++++++
21 files changed, 2946 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/kerby-kerb/kerb-client/pom.xml
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/pom.xml b/kerby-kerb/kerb-client/pom.xml
index d75eaea..fbad8cc 100644
--- a/kerby-kerb/kerb-client/pom.xml
+++ b/kerby-kerb/kerb-client/pom.xml
@@ -46,5 +46,15 @@
<artifactId>kerb-util</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15</artifactId>
+ <version>1.38</version>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcmail-jdk15</artifactId>
+ <version>1.38</version>
+ </dependency>
</dependencies>
</project>
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/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..5350c02
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ClientConfiguration.java
@@ -0,0 +1,149 @@
+/*
+ * 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 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/c98ab976/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhClient.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhClient.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhClient.java
new file mode 100644
index 0000000..20ca0c6
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhClient.java
@@ -0,0 +1,135 @@
+/*
+ * 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 javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.X509EncodedKeySpec;
+
+
+/**
+ * The client-side of Diffie-Hellman key agreement for Kerberos PKINIT.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class DhClient
+{
+ private static AlgorithmParameterSpec AES_IV = new IvParameterSpec( new byte[16] );
+
+ private KeyAgreement clientKeyAgree;
+ private SecretKey clientAesKey;
+
+
+ byte[] init( DHParameterSpec dhParamSpec ) throws Exception
+ {
+ // The client creates its own DH key pair, using the DH parameters from above.
+ KeyPairGenerator clientKpairGen = KeyPairGenerator.getInstance( "DH" );
+ clientKpairGen.initialize( dhParamSpec );
+ KeyPair clientKpair = clientKpairGen.generateKeyPair();
+
+ // The client creates and initializes its DH KeyAgreement object.
+ clientKeyAgree = KeyAgreement.getInstance( "DH" );
+ clientKeyAgree.init( clientKpair.getPrivate() );
+
+ // The client encodes its public key, and sends it over to the server.
+ return clientKpair.getPublic().getEncoded();
+ }
+
+
+ void doPhase( byte[] serverPubKeyEnc ) throws Exception
+ {
+ /*
+ * The client uses the server's public key for the first (and only) phase
+ * of its version of the DH protocol. Before it can do so, it has to
+ * instantiate a DH public key from the server's encoded key material.
+ */
+ KeyFactory clientKeyFac = KeyFactory.getInstance( "DH" );
+ X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec( serverPubKeyEnc );
+ PublicKey serverPubKey = clientKeyFac.generatePublic( x509KeySpec );
+
+ clientKeyAgree.doPhase( serverPubKey, true );
+ }
+
+
+ byte[] generateKey( byte[] clientDhNonce, byte[] serverDhNonce )
+ {
+ // ZZ length will be same as public key.
+ byte[] dhSharedSecret = clientKeyAgree.generateSecret();
+ byte[] x = dhSharedSecret;
+
+ if ( ( clientDhNonce != null && clientDhNonce.length > 0 )
+ && ( serverDhNonce != null && serverDhNonce.length > 0 ) )
+ {
+ x = concatenateBytes( dhSharedSecret, clientDhNonce );
+ x = concatenateBytes( x, serverDhNonce );
+ }
+
+ byte[] secret = OctetString2Key.kTruncate( dhSharedSecret.length, x );
+ clientAesKey = new SecretKeySpec( secret, 0, 16, "AES" );
+
+ return clientAesKey.getEncoded();
+ }
+
+
+ /**
+ * Decrypt using AES in CTS mode.
+ *
+ * @param cipherText
+ * @return
+ * @throws Exception
+ */
+ byte[] decryptAes( byte[] cipherText ) throws Exception
+ {
+ // Use the secret key to encrypt/decrypt data.
+ Cipher serverCipher = Cipher.getInstance( "AES/CTS/NoPadding" );
+ serverCipher.init( Cipher.DECRYPT_MODE, clientAesKey, AES_IV );
+
+ return serverCipher.doFinal( cipherText );
+ }
+
+
+ byte[] concatenateBytes( byte[] array1, byte[] array2 )
+ {
+ byte concatenatedBytes[] = new byte[array1.length + array2.length];
+
+ for ( int i = 0; i < array1.length; i++ )
+ {
+ concatenatedBytes[i] = array1[i];
+ }
+
+ for ( int j = array1.length; j < concatenatedBytes.length; j++ )
+ {
+ concatenatedBytes[j] = array2[j - array1.length];
+ }
+
+ return concatenatedBytes;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhGroup.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhGroup.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhGroup.java
new file mode 100644
index 0000000..6fb0f51
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhGroup.java
@@ -0,0 +1,142 @@
+/*
+ * 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 javax.crypto.spec.DHParameterSpec;
+import java.math.BigInteger;
+
+
+/**
+ * "When using the Diffie-Hellman key agreement method, implementations MUST
+ * support Oakley 1024-bit Modular Exponential (MODP) well-known group 2
+ * [RFC2412] and Oakley 2048-bit MODP well-known group 14 [RFC3526] and
+ * SHOULD support Oakley 4096-bit MODP well-known group 16 [RFC3526]."
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhGroup
+{
+ /**
+ * From:
+ * The OAKLEY Key Determination Protocol
+ * http://www.ietf.org/rfc/rfc2412.txt
+ *
+ * Well-Known Group 2: A 1024 bit prime
+ * This group is assigned id 2 (two).
+ * The prime is 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
+ * The generator is 2 (decimal)
+ */
+ public static final DHParameterSpec MODP_GROUP2;
+
+ static
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" );
+ sb.append( "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" );
+ sb.append( "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" );
+ sb.append( "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" );
+ sb.append( "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" );
+ sb.append( "FFFFFFFFFFFFFFFF" );
+
+ BigInteger prime = new BigInteger( sb.toString(), 16 );
+ BigInteger generator = BigInteger.valueOf( 2 );
+
+ MODP_GROUP2 = new DHParameterSpec( prime, generator );
+ }
+
+ /**
+ * From:
+ * More Modular Exponential (MODP) Diffie-Hellman groups for Internet Key Exchange (IKE)
+ * http://www.ietf.org/rfc/rfc3526.txt
+ *
+ * 2048-bit MODP Group
+ * This group is assigned id 14.
+ * This prime is: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
+ * The generator is: 2.
+ */
+ public static final DHParameterSpec MODP_GROUP14;
+
+ static
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" );
+ sb.append( "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" );
+ sb.append( "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" );
+ sb.append( "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" );
+ sb.append( "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" );
+ sb.append( "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" );
+ sb.append( "83655D23DCA3AD961C62F356208552BB9ED529077096966D" );
+ sb.append( "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" );
+ sb.append( "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" );
+ sb.append( "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" );
+ sb.append( "15728E5A8AACAA68FFFFFFFFFFFFFFFF" );
+
+ BigInteger prime = new BigInteger( sb.toString(), 16 );
+ BigInteger generator = BigInteger.valueOf( 2 );
+
+ MODP_GROUP14 = new DHParameterSpec( prime, generator );
+ }
+
+ /**
+ * From:
+ * More Modular Exponential (MODP) Diffie-Hellman groups for Internet Key Exchange (IKE)
+ * http://www.ietf.org/rfc/rfc3526.txt
+ *
+ * 4096-bit MODP Group
+ * This group is assigned id 16.
+ * This prime is: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
+ * The generator is: 2.
+ */
+ public static final DHParameterSpec MODP_GROUP16;
+
+ static
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" );
+ sb.append( "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" );
+ sb.append( "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" );
+ sb.append( "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" );
+ sb.append( "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" );
+ sb.append( "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" );
+ sb.append( "83655D23DCA3AD961C62F356208552BB9ED529077096966D" );
+ sb.append( "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" );
+ sb.append( "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" );
+ sb.append( "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" );
+ sb.append( "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" );
+ sb.append( "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" );
+ sb.append( "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" );
+ sb.append( "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" );
+ sb.append( "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" );
+ sb.append( "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" );
+ sb.append( "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" );
+ sb.append( "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" );
+ sb.append( "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" );
+ sb.append( "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" );
+ sb.append( "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" );
+ sb.append( "FFFFFFFFFFFFFFFF" );
+
+ BigInteger prime = new BigInteger( sb.toString(), 16 );
+ BigInteger generator = BigInteger.valueOf( 2 );
+
+ MODP_GROUP16 = new DHParameterSpec( prime, generator );
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhServer.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhServer.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhServer.java
new file mode 100644
index 0000000..a2e4a27
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/DhServer.java
@@ -0,0 +1,142 @@
+/*
+ * 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 java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+
+/**
+ * The server-side of Diffie-Hellman key agreement for Kerberos PKINIT.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class DhServer
+{
+ private static AlgorithmParameterSpec AES_IV = new IvParameterSpec( new byte[16] );
+
+ private KeyAgreement serverKeyAgree;
+ private SecretKey serverAesKey;
+
+
+ byte[] initAndDoPhase( byte[] clientPubKeyEnc ) throws Exception
+ {
+ /*
+ * The server has received the client's public key in encoded format. The
+ * server instantiates a DH public key from the encoded key material.
+ */
+ KeyFactory serverKeyFac = KeyFactory.getInstance( "DH" );
+ X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec( clientPubKeyEnc );
+ PublicKey clientPubKey = serverKeyFac.generatePublic( x509KeySpec );
+
+ /*
+ * The server gets the DH parameters associated with the client's public
+ * key. The server must use the same parameters when it generates its own key pair.
+ */
+ DHParameterSpec dhParamSpec = ( ( DHPublicKey ) clientPubKey ).getParams();
+
+ // The server creates its own DH key pair.
+ KeyPairGenerator serverKpairGen = KeyPairGenerator.getInstance( "DH" );
+ serverKpairGen.initialize( dhParamSpec );
+ KeyPair serverKpair = serverKpairGen.generateKeyPair();
+
+ // The server creates and initializes its DH KeyAgreement object.
+ serverKeyAgree = KeyAgreement.getInstance( "DH" );
+ serverKeyAgree.init( serverKpair.getPrivate() );
+
+ /*
+ * The server uses the client's public key for the only phase of its
+ * side of the DH protocol.
+ */
+ serverKeyAgree.doPhase( clientPubKey, true );
+
+ // The server encodes its public key, and sends it over to the client.
+ return serverKpair.getPublic().getEncoded();
+ }
+
+
+ byte[] generateKey( byte[] clientDhNonce, byte[] serverDhNonce )
+ {
+ // ZZ length will be same as public key.
+ byte[] dhSharedSecret = serverKeyAgree.generateSecret();
+ byte[] x = dhSharedSecret;
+
+ if ( ( clientDhNonce != null && clientDhNonce.length > 0 )
+ && ( serverDhNonce != null && serverDhNonce.length > 0 ) )
+ {
+ x = concatenateBytes( dhSharedSecret, clientDhNonce );
+ x = concatenateBytes( x, serverDhNonce );
+ }
+
+ byte[] secret = OctetString2Key.kTruncate( dhSharedSecret.length, x );
+ serverAesKey = new SecretKeySpec( secret, 0, 16, "AES" );
+
+ return serverAesKey.getEncoded();
+ }
+
+
+ /**
+ * Encrypt using AES in CTS mode.
+ *
+ * @param cleartext
+ * @return The cipher text.
+ * @throws Exception
+ */
+ byte[] encryptAes( byte[] clearText ) throws Exception
+ {
+ // Use the secret key to encrypt/decrypt data.
+ Cipher serverCipher = Cipher.getInstance( "AES/CTS/NoPadding" );
+ serverCipher.init( Cipher.ENCRYPT_MODE, serverAesKey, AES_IV );
+
+ return serverCipher.doFinal( clearText );
+ }
+
+
+ byte[] concatenateBytes( byte[] array1, byte[] array2 )
+ {
+ byte concatenatedBytes[] = new byte[array1.length + array2.length];
+
+ for ( int i = 0; i < array1.length; i++ )
+ {
+ concatenatedBytes[i] = array1[i];
+ }
+
+ for ( int j = array1.length; j < concatenatedBytes.length; j++ )
+ {
+ concatenatedBytes[j] = array2[j - array1.length];
+ }
+
+ return concatenatedBytes;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/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..523711e
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/EnvelopedDataEngine.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;
+
+
+import org.bouncycastle.cms.CMSEnvelopedData;
+import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.KeyTransRecipientInformation;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Collections;
+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.
+ *
+ * '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 NoSuchAlgorithmException
+ * @throws IOException
+ * @throws CMSException
+ * @throws NoSuchProviderException
+ */
+ public static byte[] getEnvelopedReplyKeyPack( byte[] dataToEnvelope, X509Certificate certificate )
+ throws NoSuchAlgorithmException, IOException, CMSException, NoSuchProviderException
+ {
+ CMSProcessableByteArray content = new CMSProcessableByteArray( dataToEnvelope );
+ String algorithm = CMSEnvelopedDataGenerator.DES_EDE3_CBC;
+
+ CMSEnvelopedDataGenerator envelopeGenerator = new CMSEnvelopedDataGenerator();
+ envelopeGenerator.addKeyTransRecipient( certificate );
+ CMSEnvelopedData envdata = envelopeGenerator.generate( content, algorithm, "BC" );
+
+ 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 certificate
+ * @param privateKey
+ * @return The recovered (decrypted) data bytes.
+ * @throws NoSuchProviderException
+ * @throws InvalidAlgorithmParameterException
+ * @throws CMSException
+ * @throws NoSuchAlgorithmException
+ * @throws CertStoreException
+ */
+ @SuppressWarnings("unchecked")
+ public static byte[] getUnenvelopedData( byte[] envelopedDataBytes, X509Certificate certificate,
+ PrivateKey privateKey ) throws NoSuchProviderException, InvalidAlgorithmParameterException, CMSException,
+ NoSuchAlgorithmException, CertStoreException
+ {
+ CMSEnvelopedData envelopedData = new CMSEnvelopedData( envelopedDataBytes );
+
+ // Set up to iterate through the recipients.
+ RecipientInformationStore recipients = envelopedData.getRecipientInfos();
+ CertStore certStore = CertStore.getInstance( "Collection", new CollectionCertStoreParameters( Collections
+ .singleton( certificate ) ), "BC" );
+ Iterator<RecipientInformation> it = recipients.getRecipients().iterator();
+
+ while ( it.hasNext() )
+ {
+ RecipientInformation recipient = it.next();
+ if ( recipient instanceof KeyTransRecipientInformation )
+ {
+ // Match the recipient ID.
+ Collection<? extends Certificate> matches = certStore.getCertificates( recipient.getRID() );
+
+ if ( !matches.isEmpty() )
+ {
+ // Decrypt the data.
+ return recipient.getContent( privateKey, "BC" );
+ }
+ }
+ }
+
+ return new byte[0];
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/OctetString2Key.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/OctetString2Key.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/OctetString2Key.java
new file mode 100644
index 0000000..ccfab38
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/OctetString2Key.java
@@ -0,0 +1,103 @@
+/*
+ * 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 java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+
+/**
+ * From RFC 4556:
+ *
+ * Define the function octetstring2key() as follows:
+ *
+ * octetstring2key(x) == random-to-key(K-truncate(
+ * SHA1(0x00 | x) |
+ * SHA1(0x01 | x) |
+ * SHA1(0x02 | x) |
+ * ...
+ * ))
+ *
+ * where x is an octet string; | is the concatenation operator; 0x00,
+ * 0x01, 0x02, etc. are each represented as a single octet; random-
+ * to-key() is an operation that generates a protocol key from a
+ * bitstring of length K; and K-truncate truncates its input to the
+ * first K bits. Both K and random-to-key() are as defined in the
+ * kcrypto profile [RFC3961] for the enctype of the AS reply key.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class OctetString2Key
+{
+ /**
+ * Performs the function K-truncate to generate the AS reply key k.
+ *
+ * @param k
+ * @param x
+ * @return The AS reply key value.
+ */
+ public static byte[] kTruncate( int k, byte[] x )
+ {
+ int numberOfBytes = k / 8;
+ byte[] result = new byte[numberOfBytes];
+
+ int count = 0;
+ byte[] filler = calculateIntegrity( ( byte ) count, x );
+
+ int position = 0;
+
+ for ( int i = 0; i < numberOfBytes; i++ )
+ {
+ if ( position < filler.length )
+ {
+ result[i] = filler[position];
+ position++;
+ }
+ else
+ {
+ count++;
+ filler = calculateIntegrity( ( byte ) count, x );
+ position = 0;
+ result[i] = filler[position];
+ position++;
+ }
+ }
+
+ return result;
+ }
+
+
+ private static byte[] calculateIntegrity( byte count, byte[] data )
+ {
+ try
+ {
+ MessageDigest digester = MessageDigest.getInstance( "SHA1" );
+ digester.update( count );
+
+ return digester.digest( data );
+ }
+ catch ( NoSuchAlgorithmException nsae )
+ {
+ return new byte[0];
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/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..44635d9
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/ServerConfiguration.java
@@ -0,0 +1,153 @@
+/*
+ * 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.spec.KerberosTime;
+
+import javax.crypto.spec.DHParameterSpec;
+
+/**
+ * Server configuration settings.
+ *
+ * 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/c98ab976/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..5a5a09a
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/SignedDataEngine.java
@@ -0,0 +1,205 @@
+/*
+ * 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.spec.pa.pkinit.AuthPack;
+import org.apache.kerby.kerberos.kerb.spec.pa.pkinit.KdcDHKeyInfo;
+import org.apache.kerby.kerberos.kerb.spec.pa.pkinit.ReplyKeyPack;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+
+/**
+ * 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 NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ * @throws InvalidAlgorithmParameterException
+ * @throws CertStoreException
+ * @throws CMSException
+ * @throws IOException
+ */
+ public static byte[] getSignedAuthPack(PrivateKey privateKey, X509Certificate certificate, AuthPack authPack)
+ throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
+ CertStoreException, 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 NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ * @throws InvalidAlgorithmParameterException
+ * @throws CertStoreException
+ * @throws CMSException
+ * @throws IOException
+ */
+ public static byte[] getSignedKdcDhKeyInfo(PrivateKey privateKey, X509Certificate certificate,
+ KdcDHKeyInfo kdcDhKeyInfo) throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidAlgorithmParameterException, CertStoreException, 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 NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ * @throws InvalidAlgorithmParameterException
+ * @throws CertStoreException
+ * @throws CMSException
+ * @throws IOException
+ */
+ public static byte[] getSignedReplyKeyPack(PrivateKey privateKey, X509Certificate certificate,
+ ReplyKeyPack replyKeyPack) throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidAlgorithmParameterException, CertStoreException, CMSException, IOException {
+ return getSignedData(privateKey, certificate, replyKeyPack.encode(), ID_PKINIT_RKEYDATA);
+ }
+
+
+ static byte[] getSignedData(PrivateKey privateKey, X509Certificate certificate, byte[] dataToSign,
+ String eContentType) throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidAlgorithmParameterException, CertStoreException, CMSException, IOException {
+ CMSSignedDataGenerator signedGenerator = new CMSSignedDataGenerator();
+ signedGenerator.addSigner(privateKey, certificate, CMSSignedGenerator.DIGEST_SHA1);
+
+ Collection<X509Certificate> certList = Collections.singletonList(certificate);
+
+ CertStore certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), "BC");
+ signedGenerator.addCertificatesAndCRLs(certStore);
+
+ CMSProcessableByteArray cmsByteArray = new CMSProcessableByteArray(dataToSign);
+ CMSSignedData signedData = signedGenerator.generate(eContentType, cmsByteArray, true, "BC");
+
+ return signedData.getEncoded();
+ }
+
+
+ /**
+ * Validates a CMS SignedData using the public key corresponding to the private
+ * key used to sign the structure.
+ *
+ * @param signedData
+ * @return true if the signature is valid.
+ * @throws Exception
+ */
+ @SuppressWarnings("unchecked")
+ public static boolean validateSignedData(CMSSignedData signedData) throws Exception {
+ CertStore certs = signedData.getCertificatesAndCRLs("Collection", "BC");
+
+ SignerInformationStore signers = signedData.getSignerInfos();
+ Collection<SignerInformation> c = signers.getSigners();
+ Iterator<SignerInformation> it = c.iterator();
+
+ while (it.hasNext()) {
+ final SignerInformation signer = it.next();
+
+ Collection<? extends Certificate> certCollection = certs.getCertificates( signer.getSID() );
+ /*Collection<? extends Certificate> certCollection = certs
+ .getCertificates(new CertSelector() {
+ @Override
+ public boolean match(Certificate cert) {
+ return false; // check cert and signer
+ }
+ });
+ */
+ Iterator<? extends Certificate> certIt = certCollection.iterator();
+
+ X509Certificate cert = ( X509Certificate ) certIt.next();
+
+ if ( signer.verify( cert.getPublicKey(), "BC" ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/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..0eac8c6
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/CertificateChainFactory.java
@@ -0,0 +1,283 @@
+/*
+ * 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 logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(CertificateChainFactory.class);
+
+ private static int TRUST_ANCHOR_LEVEL = 2;
+
+ private static int INTERMEDIATE_LEVEL = 1;
+
+ private static int END_ENTITY_LEVEL = 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(TRUST_ANCHOR_LEVEL);
+ PrivateKey trustAnchorPrivateKey = keyPair.getPrivate();
+ PublicKey trustAnchorPublicKey = keyPair.getPublic();
+
+ X509Certificate trustAnchorCert = TrustAnchorGenerator.generate(trustAnchorPublicKey, trustAnchorPrivateKey,
+ dn, validityDays, friendlyName);
+
+ trustAnchorCert.checkValidity();
+ trustAnchorCert.verify(trustAnchorPublicKey);
+
+ logger.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(INTERMEDIATE_LEVEL);
+ PrivateKey clientCaPrivateKey = keyPair.getPrivate();
+ PublicKey clientCaPublicKey = keyPair.getPublic();
+
+ X509Certificate clientCaCert = IntermediateCaGenerator.generate(trustAnchorCert, trustAnchorPrivateKey,
+ clientCaPublicKey, dn, validityDays, friendlyName);
+
+ clientCaCert.checkValidity();
+ clientCaCert.verify(trustAnchorPublicKey);
+
+ logger.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(END_ENTITY_LEVEL);
+ clientPrivateKey = keyPair.getPrivate();
+ PublicKey clientPublicKey = keyPair.getPublic();
+
+ X509Certificate clientCert = EndEntityGenerator.generate(clientCaCert, clientCaPrivateKey, clientPublicKey,
+ dn, validityDays, friendlyName);
+
+ clientCert.checkValidity();
+ clientCert.verify(clientCaPublicKey);
+
+ logger.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(TRUST_ANCHOR_LEVEL);
+ PrivateKey trustAnchorPrivateKey = keyPair.getPrivate();
+ PublicKey trustAnchorPublicKey = keyPair.getPublic();
+
+ X509Certificate trustAnchorCert = TrustAnchorGenerator.generate(trustAnchorPublicKey, trustAnchorPrivateKey,
+ dn, validityDays, friendlyName);
+
+ trustAnchorCert.checkValidity();
+ trustAnchorCert.verify(trustAnchorPublicKey);
+
+ logger.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(INTERMEDIATE_LEVEL);
+ PrivateKey kdcCaPrivateKey = keyPair.getPrivate();
+ PublicKey kdcCaPublicKey = keyPair.getPublic();
+
+ X509Certificate kdcCaCert = IntermediateCaGenerator.generate(trustAnchorCert, trustAnchorPrivateKey,
+ kdcCaPublicKey, dn, validityDays, friendlyName);
+
+ kdcCaCert.checkValidity();
+ kdcCaCert.verify(trustAnchorPublicKey);
+
+ logger.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(END_ENTITY_LEVEL);
+ kdcPrivateKey = keyPair.getPrivate();
+ PublicKey kdcPublicKey = keyPair.getPublic();
+
+ X509Certificate kdcCert = EndEntityGenerator.generate(kdcCaCert, kdcCaPrivateKey, kdcPublicKey, dn,
+ validityDays, friendlyName);
+
+ kdcCert.checkValidity();
+ kdcCert.verify(kdcCaPublicKey);
+
+ logger.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/c98ab976/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..1836273
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/EndEntityGenerator.java
@@ -0,0 +1,257 @@
+/*
+ * 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.apache.kerby.kerberos.kerb.spec.pa.pkinit.Krb5PrincipalName;
+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.KeyPurposeId;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.crypto.DataLengthException;
+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 org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
+
+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$
+ */
+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 SubjectKeyIdentifierStructure( publicKey ) );
+
+ // 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 );
+
+ Krb5PrincipalName principalName = new Krb5PrincipalName();
+ ASN1EncodableVector pkinitSanVector = new ASN1EncodableVector();
+ pkinitSanVector.add( ID_PKINIT_SAN );
+ pkinitSanVector.add( new DERTaggedObject( 0, new DERSequence()));
+ DERSequence pkinitSan = new DERSequence( pkinitSanVector );
+
+ String dnsName = "localhost";
+
+ ASN1EncodableVector sanVector = new ASN1EncodableVector();
+ sanVector.add( new GeneralName( GeneralName.otherName, pkinitSan ) );
+ sanVector.add( new GeneralName( GeneralName.dNSName, dnsName ) );
+ DERSequence san = new DERSequence( sanVector );
+
+ GeneralNames sanGeneralNames = new GeneralNames( san );
+
+ 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 SubjectKeyIdentifierStructure(
+ publicKey ) );
+
+ return cert;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/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..4364647
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/IntermediateCaGenerator.java
@@ -0,0 +1,116 @@
+/*
+ * 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.X509Extensions;
+import org.bouncycastle.crypto.DataLengthException;
+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 org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
+
+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 SubjectKeyIdentifierStructure( publicKey ) );
+
+ 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 SubjectKeyIdentifierStructure(
+ publicKey ) );
+
+ return cert;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/KeyPairSpec.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/KeyPairSpec.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/KeyPairSpec.java
new file mode 100644
index 0000000..12cf8f9
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/KeyPairSpec.java
@@ -0,0 +1,114 @@
+/*
+ * 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 java.math.BigInteger;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+
+
+/**
+ * Specifications for asymmetric key pairs.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class KeyPairSpec
+{
+ // End-entity keys.
+ static RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(
+ new BigInteger(
+ "b4a7e46170574f16a97082b22be58b6a2a629798419be12872a4bdba626cfae9900f76abfb12139dce5de56564fab2b6543165a040c606887420e33d91ed7ed7",
+ 16 ), new BigInteger( "11", 16 ) );
+
+ static RSAPrivateCrtKeySpec privKeySpec = new RSAPrivateCrtKeySpec(
+ new BigInteger(
+ "b4a7e46170574f16a97082b22be58b6a2a629798419be12872a4bdba626cfae9900f76abfb12139dce5de56564fab2b6543165a040c606887420e33d91ed7ed7",
+ 16 ),
+ new BigInteger( "11", 16 ),
+ new BigInteger(
+ "9f66f6b05410cd503b2709e88115d55daced94d1a34d4e32bf824d0dde6028ae79c5f07b580f5dce240d7111f7ddb130a7945cd7d957d1920994da389f490c89",
+ 16 ), new BigInteger( "c0a0758cdf14256f78d4708c86becdead1b50ad4ad6c5c703e2168fbf37884cb", 16 ),
+ new BigInteger( "f01734d7960ea60070f1b06f2bb81bfac48ff192ae18451d5e56c734a5aab8a5", 16 ), new BigInteger(
+ "b54bb9edff22051d9ee60f9351a48591b6500a319429c069a3e335a1d6171391", 16 ), new BigInteger(
+ "d3d83daf2a0cecd3367ae6f8ae1aeb82e9ac2f816c6fc483533d8297dd7884cd", 16 ), new BigInteger(
+ "b8f52fc6f38593dabb661d3f50f8897f8106eee68b1bce78a95b132b4e5b5d19", 16 ) );
+
+ // Intermediate keys.
+ static RSAPublicKeySpec intPubKeySpec = new RSAPublicKeySpec(
+ new BigInteger(
+ "8de0d113c5e736969c8d2b047a243f8fe18edad64cde9e842d3669230ca486f7cfdde1f8eec54d1905fff04acc85e61093e180cadc6cea407f193d44bb0e9449b8dbb49784cd9e36260c39e06a947299978c6ed8300724e887198cfede20f3fbde658fa2bd078be946a392bd349f2b49c486e20c405588e306706c9017308e69",
+ 16 ), new BigInteger( "ffff", 16 ) );
+
+ static RSAPrivateCrtKeySpec intPrivKeySpec = new RSAPrivateCrtKeySpec(
+ new BigInteger(
+ "8de0d113c5e736969c8d2b047a243f8fe18edad64cde9e842d3669230ca486f7cfdde1f8eec54d1905fff04acc85e61093e180cadc6cea407f193d44bb0e9449b8dbb49784cd9e36260c39e06a947299978c6ed8300724e887198cfede20f3fbde658fa2bd078be946a392bd349f2b49c486e20c405588e306706c9017308e69",
+ 16 ),
+ new BigInteger( "ffff", 16 ),
+ new BigInteger(
+ "7deb1b194a85bcfd29cf871411468adbc987650903e3bacc8338c449ca7b32efd39ffc33bc84412fcd7df18d23ce9d7c25ea910b1ae9985373e0273b4dca7f2e0db3b7314056ac67fd277f8f89cf2fd73c34c6ca69f9ba477143d2b0e2445548aa0b4a8473095182631da46844c356f5e5c7522eb54b5a33f11d730ead9c0cff",
+ 16 ),
+ new BigInteger(
+ "ef4cede573cea47f83699b814de4302edb60eefe426c52e17bd7870ec7c6b7a24fe55282ebb73775f369157726fcfb988def2b40350bdca9e5b418340288f649",
+ 16 ),
+ new BigInteger(
+ "97c7737d1b9a0088c3c7b528539247fd2a1593e7e01cef18848755be82f4a45aa093276cb0cbf118cb41117540a78f3fc471ba5d69f0042274defc9161265721",
+ 16 ),
+ new BigInteger(
+ "6c641094e24d172728b8da3c2777e69adfd0839085be7e38c7c4a2dd00b1ae969f2ec9d23e7e37090fcd449a40af0ed463fe1c612d6810d6b4f58b7bfa31eb5f",
+ 16 ),
+ new BigInteger(
+ "70b7123e8e69dfa76feb1236d0a686144b00e9232ed52b73847e74ef3af71fb45ccb24261f40d27f98101e230cf27b977a5d5f1f15f6cf48d5cb1da2a3a3b87f",
+ 16 ),
+ new BigInteger(
+ "e38f5750d97e270996a286df2e653fd26c242106436f5bab0f4c7a9e654ce02665d5a281f2c412456f2d1fa26586ef04a9adac9004ca7f913162cb28e13bf40d",
+ 16 ) );
+
+ // Trust anchor keys.
+ static RSAPublicKeySpec caPubKeySpec = new RSAPublicKeySpec(
+ new BigInteger(
+ "b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5",
+ 16 ), new BigInteger( "11", 16 ) );
+
+ static RSAPrivateCrtKeySpec caPrivKeySpec = new RSAPrivateCrtKeySpec(
+ new BigInteger(
+ "b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5",
+ 16 ),
+ new BigInteger( "11", 16 ),
+ new BigInteger(
+ "92e08f83cc9920746989ca5034dcb384a094fb9c5a6288fcc4304424ab8f56388f72652d8fafc65a4b9020896f2cde297080f2a540e7b7ce5af0b3446e1258d1dd7f245cf54124b4c6e17da21b90a0ebd22605e6f45c9f136d7a13eaac1c0f7487de8bd6d924972408ebb58af71e76fd7b012a8d0e165f3ae2e5077a8648e619",
+ 16 ),
+ new BigInteger(
+ "f75e80839b9b9379f1cf1128f321639757dba514642c206bbbd99f9a4846208b3e93fbbe5e0527cc59b1d4b929d9555853004c7c8b30ee6a213c3d1bb7415d03",
+ 16 ),
+ new BigInteger(
+ "b892d9ebdbfc37e397256dd8a5d3123534d1f03726284743ddc6be3a709edb696fc40c7d902ed804c6eee730eee3d5b20bf6bd8d87a296813c87d3b3cc9d7947",
+ 16 ),
+ new BigInteger(
+ "1d1a2d3ca8e52068b3094d501c9a842fec37f54db16e9a67070a8b3f53cc03d4257ad252a1a640eadd603724d7bf3737914b544ae332eedf4f34436cac25ceb5",
+ 16 ),
+ new BigInteger(
+ "6c929e4e81672fef49d9c825163fec97c4b7ba7acb26c0824638ac22605d7201c94625770984f78a56e6e25904fe7db407099cad9b14588841b94f5ab498dded",
+ 16 ),
+ new BigInteger(
+ "dae7651ee69ad1d081ec5e7188ae126f6004ff39556bde90e0b870962fa7b926d070686d8244fe5a9aa709a95686a104614834b0ada4b10f53197a5cb4c97339",
+ 16 ) );
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/c98ab976/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/TrustAnchorGenerator.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/TrustAnchorGenerator.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/TrustAnchorGenerator.java
new file mode 100644
index 0000000..f3152d9
--- /dev/null
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/preauth/pkinit/certs/TrustAnchorGenerator.java
@@ -0,0 +1,110 @@
+/*
+ * 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.X509Extensions;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
+
+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 "trust anchor" certificate programmatically.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TrustAnchorGenerator
+{
+ /**
+ * Create CA certificate.
+ *
+ * @param publicKey
+ * @param privateKey
+ * @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( PublicKey publicKey, PrivateKey privateKey, 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() ) );
+
+ X509Principal x509Principal = new X509Principal( dn );
+ certGen.setIssuerDN( x509Principal );
+ certGen.setSubjectDN( x509Principal );
+
+ 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 SubjectKeyIdentifierStructure( publicKey ) );
+
+ certGen.addExtension( X509Extensions.BasicConstraints, true, new BasicConstraints( 1 ) );
+
+ certGen.addExtension( X509Extensions.KeyUsage, true, new KeyUsage( KeyUsage.digitalSignature
+ | KeyUsage.keyCertSign | KeyUsage.cRLSign ) );
+
+ X509Certificate cert = certGen.generate( privateKey );
+
+ PKCS12BagAttributeCarrier bagAttr = ( PKCS12BagAttributeCarrier ) cert;
+
+ bagAttr.setBagAttribute( PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString( friendlyName ) );
+ bagAttr.setBagAttribute( PKCSObjectIdentifiers.pkcs_9_at_localKeyId, new SubjectKeyIdentifierStructure(
+ publicKey ) );
+
+ return cert;
+ }
+}