Repository: knox Updated Branches: refs/heads/master 616df9984 -> 02d5a14b0
KNOX-422 - provide support for IBM JVM - via Pascal Oliva Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/02d5a14b Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/02d5a14b Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/02d5a14b Branch: refs/heads/master Commit: 02d5a14b0a3d167b37315e314749850e64538b16 Parents: 616df99 Author: Larry McCay <lmc...@hortonworks.com> Authored: Fri Oct 17 01:15:06 2014 -0400 Committer: Larry McCay <lmc...@hortonworks.com> Committed: Fri Oct 17 01:15:06 2014 -0400 ---------------------------------------------------------------------- .../security/impl/DefaultKeystoreService.java | 5 +- .../hadoop/gateway/i18n/GatewaySpiMessages.java | 4 + .../security/impl/BaseKeystoreService.java | 47 +--- .../security/impl/CMFKeystoreService.java | 3 +- .../security/impl/X509CertificateUtil.java | 259 +++++++++++++++++++ 5 files changed, 269 insertions(+), 49 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/02d5a14b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java index be56a60..6f5cd48 100644 --- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java +++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java @@ -26,6 +26,7 @@ import org.apache.hadoop.gateway.services.Service; import org.apache.hadoop.gateway.services.ServiceLifecycleException; import org.apache.hadoop.gateway.services.security.KeystoreService; import org.apache.hadoop.gateway.services.security.KeystoreServiceException; +import org.apache.hadoop.gateway.services.security.impl.X509CertificateUtil; import java.io.File; import java.io.IOException; @@ -105,11 +106,11 @@ public class DefaultKeystoreService extends BaseKeystoreService implements Keyst X509Certificate cert = null; if(hostname.equals(CERT_GEN_MODE_HOSTNAME)) { String dn = buildDistinguishedName(InetAddress.getLocalHost().getHostName()); - cert = generateCertificate(dn, KPair, 365, "SHA1withRSA"); + cert = X509CertificateUtil.generateCertificate(dn, KPair, 365, "SHA1withRSA"); } else { String dn = buildDistinguishedName(hostname); - cert = generateCertificate(dn, KPair, 365, "SHA1withRSA"); + cert = X509CertificateUtil.generateCertificate(dn, KPair, 365, "SHA1withRSA"); } KeyStore privateKS = getKeystoreForGateway(); http://git-wip-us.apache.org/repos/asf/knox/blob/02d5a14b/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java index ac6483a..9a8ebaf 100644 --- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java +++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java @@ -75,4 +75,8 @@ public interface GatewaySpiMessages { @Message( level = MessageLevel.ERROR, text = "Gateway has failed to start. Unable to prompt user for master secret setup. Please consider using knoxcli.sh create-master" ) void unableToPromptForMasterUseKnoxCLI(); + + @Message( level = MessageLevel.ERROR, text = "Error in generating certificate: {0}" ) + void failedToGenerateCertificate( @StackTrace( level = MessageLevel.ERROR ) Exception e ); + } http://git-wip-us.apache.org/repos/asf/knox/blob/02d5a14b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/BaseKeystoreService.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/BaseKeystoreService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/BaseKeystoreService.java index a33bc7d..cf61ba8 100644 --- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/BaseKeystoreService.java +++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/BaseKeystoreService.java @@ -22,7 +22,6 @@ import org.apache.hadoop.gateway.i18n.GatewaySpiMessages; import org.apache.hadoop.gateway.i18n.messages.MessagesFactory; import org.apache.hadoop.gateway.services.security.KeystoreServiceException; import org.apache.hadoop.gateway.services.security.MasterService; -import sun.security.x509.*; import javax.crypto.spec.SecretKeySpec; import java.io.File; @@ -30,21 +29,15 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.math.BigInteger; -import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Date; public class BaseKeystoreService { private static GatewaySpiMessages LOG = MessagesFactory.get( GatewaySpiMessages.class ); @@ -75,44 +68,6 @@ public class BaseKeystoreService { return keyStore; } - /** - * Create a self-signed X.509 Certificate - * @param dn the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB" - * @param pair the KeyPair - * @param days how many days from now the Certificate is valid for - * @param algorithm the signing algorithm, eg "SHA1withRSA" - */ - protected X509Certificate generateCertificate(String dn, KeyPair pair, int days, - String algorithm) throws GeneralSecurityException, IOException { - PrivateKey privkey = pair.getPrivate(); - X509CertInfo info = new X509CertInfo(); - Date from = new Date(); - Date to = new Date(from.getTime() + days * 86400000l); - CertificateValidity interval = new CertificateValidity(from, to); - BigInteger sn = new BigInteger(64, new SecureRandom()); - X500Name owner = new X500Name(dn); - - info.set(X509CertInfo.VALIDITY, interval); - info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); - info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); - info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); - info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); - info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); - AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); - info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); - - // Sign the cert to identify the algorithm that's used. - X509CertImpl cert = new X509CertImpl(info); - cert.sign(privkey, algorithm); - - // Update the algorith, and resign. - algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG); - info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo); - cert = new X509CertImpl(info); - cert.sign(privkey, algorithm); - return cert; - } - private static FileOutputStream createKeyStoreFile( String fileName ) throws IOException { File file = new File( fileName ); if( file.exists() ) { @@ -278,4 +233,4 @@ public class BaseKeystoreService { public void setMasterService(MasterService ms) { this.masterService = ms; } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/knox/blob/02d5a14b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFKeystoreService.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFKeystoreService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFKeystoreService.java index ad9fbec..119eff5 100644 --- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFKeystoreService.java +++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFKeystoreService.java @@ -34,6 +34,7 @@ import org.apache.hadoop.gateway.i18n.GatewaySpiMessages; import org.apache.hadoop.gateway.i18n.messages.MessagesFactory; import org.apache.hadoop.gateway.services.ServiceLifecycleException; import org.apache.hadoop.gateway.services.security.KeystoreServiceException; +import org.apache.hadoop.gateway.services.security.impl.X509CertificateUtil; public class CMFKeystoreService extends BaseKeystoreService { @@ -70,7 +71,7 @@ public class CMFKeystoreService extends BaseKeystoreService { keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(1024); KeyPair KPair = keyPairGenerator.generateKeyPair(); - X509Certificate cert = generateCertificate(TEST_CERT_DN, KPair, 365, "SHA1withRSA"); + X509Certificate cert = X509CertificateUtil.generateCertificate(TEST_CERT_DN, KPair, 365, "SHA1withRSA"); KeyStore privateKS = getKeystore(); privateKS.setKeyEntry(alias, KPair.getPrivate(), http://git-wip-us.apache.org/repos/asf/knox/blob/02d5a14b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/X509CertificateUtil.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/X509CertificateUtil.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/X509CertificateUtil.java new file mode 100644 index 0000000..fd4f700 --- /dev/null +++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/X509CertificateUtil.java @@ -0,0 +1,259 @@ +/** + * 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.hadoop.gateway.services.security.impl; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.Date; + +import org.apache.hadoop.gateway.i18n.messages.MessagesFactory; +import org.apache.hadoop.gateway.i18n.GatewaySpiMessages; + +public class X509CertificateUtil { + + private static GatewaySpiMessages LOG = MessagesFactory.get(GatewaySpiMessages.class); + + /** + * Create a self-signed X.509 Certificate + * @param dn the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB" + * @param pair the KeyPair + * @param days how many days from now the Certificate is valid for + * @param algorithm the signing algorithm, eg "SHA1withRSA" + */ + public static X509Certificate generateCertificate(String dn, KeyPair pair, + int days, String algorithm) throws GeneralSecurityException, IOException { + + PrivateKey privkey = pair.getPrivate(); + Object x509CertImplObject = null; + try { + Date from = new Date(); + Date to = new Date(from.getTime() + days * 86400000l); + + Class<?> certInfoClass = Class.forName(getX509CertInfoModuleName()); + Constructor<?> certInfoConstr = certInfoClass.getConstructor(); + Object certInfoObject = certInfoConstr.newInstance(); + + // CertificateValidity interval = new CertificateValidity(from, to); + Class<?> certValidityClass = Class.forName(getX509CertifValidityModuleName()); + Constructor<?> certValidityConstr = certValidityClass + .getConstructor(new Class[] { Date.class, Date.class }); + Object certValidityObject = certValidityConstr.newInstance(from, to); + + BigInteger sn = new BigInteger(64, new SecureRandom()); + + // X500Name owner = new X500Name(dn); + Class<?> x500NameClass = Class.forName(getX509X500NameModuleName()); + Constructor<?> x500NameConstr = x500NameClass + .getConstructor(new Class[] { String.class }); + Object x500NameObject = x500NameConstr.newInstance(dn); + + Method methodSET = certInfoObject.getClass().getMethod("set", String.class, Object.class); + + // info.set(X509CertInfo.VALIDITY, interval); + methodSET.invoke(certInfoObject, getSetField(certInfoObject, "VALIDITY"),certValidityObject); + + // info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); + Class<?> certificateSerialNumberClass = Class.forName(getCertificateSerialNumberModuleName()); + Constructor<?> certificateSerialNumberConstr = certificateSerialNumberClass + .getConstructor(new Class[] { BigInteger.class }); + Object certificateSerialNumberObject = certificateSerialNumberConstr + .newInstance(sn); + methodSET.invoke(certInfoObject, getSetField(certInfoObject, "SERIAL_NUMBER"), + certificateSerialNumberObject); + + // info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); + Class<?> certificateSubjectNameClass = Class.forName(getCertificateSubjectNameModuleName()); + Constructor<?> certificateSubjectNameConstr = certificateSubjectNameClass + .getConstructor(new Class[] { x500NameClass }); + Object certificateSubjectNameObject = certificateSubjectNameConstr + .newInstance(x500NameObject); + methodSET.invoke(certInfoObject, getSetField(certInfoObject, "SUBJECT"), + certificateSubjectNameObject); + + // info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); + Class<?> certificateIssuerNameClass = Class.forName(getCertificateIssuerNameModuleName()); + Constructor<?> certificateIssuerNameConstr = certificateIssuerNameClass + .getConstructor(new Class[] { x500NameClass }); + Object certificateIssuerNameObject = certificateIssuerNameConstr + .newInstance(x500NameObject); + methodSET.invoke(certInfoObject, getSetField(certInfoObject, "ISSUER"), + certificateIssuerNameObject); + + // info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); + Class<?> certificateX509KeyClass = Class.forName(getCertificateX509KeyModuleName()); + Constructor<?> certificateX509KeyConstr = certificateX509KeyClass + .getConstructor(new Class[] { PublicKey.class }); + Object certificateX509KeyObject = certificateX509KeyConstr + .newInstance(pair.getPublic()); + methodSET.invoke(certInfoObject, getSetField(certInfoObject, "KEY"), + certificateX509KeyObject); + // info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); + Class<?> certificateVersionClass = Class.forName(getCertificateVersionModuleName()); + Constructor<?> certificateVersionConstr = certificateVersionClass + .getConstructor(new Class[] { int.class }); + Constructor<?> certificateVersionConstr0 = certificateVersionClass + .getConstructor(); + Object certInfoObject0 = certificateVersionConstr0.newInstance(); + Field v3IntField = certInfoObject0.getClass() + .getDeclaredField("V3"); + v3IntField.setAccessible(true); + int fValue = (int) v3IntField.getInt(certInfoObject0); + Object certificateVersionObject = certificateVersionConstr + .newInstance(fValue); + methodSET.invoke(certInfoObject, getSetField(certInfoObject, "VERSION"), + certificateVersionObject); + + // AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); + Class<?> algorithmIdClass = Class.forName(getAlgorithmIdModuleName()); + Field md5WithRSAField = algorithmIdClass + .getDeclaredField("md5WithRSAEncryption_oid"); + md5WithRSAField.setAccessible(true); + Class<?> objectIdentifierClass = Class.forName(getObjectIdentifierModuleName()); + + Object md5WithRSAValue = md5WithRSAField.get(algorithmIdClass); + + Constructor<?> algorithmIdConstr = algorithmIdClass + .getConstructor(new Class[] { objectIdentifierClass }); + Object algorithmIdObject = algorithmIdConstr.newInstance(md5WithRSAValue); + + // info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); + Class<?> certificateAlgorithmIdClass = Class.forName(getCertificateAlgorithmIdModuleName()); + Constructor<?> certificateAlgorithmIdConstr = certificateAlgorithmIdClass + .getConstructor(new Class[] { algorithmIdClass }); + Object certificateAlgorithmIdObject = certificateAlgorithmIdConstr + .newInstance(algorithmIdObject); + methodSET.invoke(certInfoObject, getSetField(certInfoObject, "ALGORITHM_ID"), + certificateAlgorithmIdObject); + + // Sign the cert to identify the algorithm that's used. + // X509CertImpl cert = new X509CertImpl(info); + Class<?> x509CertImplClass = Class.forName(getX509CertImplModuleName()); + Constructor<?> x509CertImplConstr = x509CertImplClass + .getConstructor(new Class[] { certInfoClass }); + x509CertImplObject = x509CertImplConstr.newInstance(certInfoObject); + + // cert.sign(privkey, algorithm); + Method methoSIGN = x509CertImplObject.getClass().getMethod("sign", + PrivateKey.class, String.class); + methoSIGN.invoke(x509CertImplObject, privkey, algorithm); + + // Update the algorith, and resign. + // algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG); + Method methoGET = x509CertImplObject.getClass().getMethod("get", String.class); + String sig_alg = getSetField(x509CertImplObject, "SIG_ALG"); + + String certAlgoIdNameValue = getSetField(certificateAlgorithmIdObject, "NAME"); + String certAlgoIdAlgoValue = getSetField(certificateAlgorithmIdObject, "ALGORITHM"); + // info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo); + methodSET.invoke(certInfoObject, certAlgoIdNameValue + "." + + certAlgoIdAlgoValue, + methoGET.invoke(x509CertImplObject, sig_alg)); + + // cert = new X509CertImpl(info); + x509CertImplObject = x509CertImplConstr.newInstance(certInfoObject); + // cert.sign(privkey, algorithm); + methoSIGN.invoke(x509CertImplObject, privkey, algorithm); + } catch (SecurityException e) { + LOG.failedToGenerateCertificate(e); + } catch (RuntimeException e) { + LOG.failedToGenerateCertificate(e); + } catch (ReflectiveOperationException e) { + LOG.failedToGenerateCertificate(e); + } + // return cert; + return (X509Certificate) x509CertImplObject; + } + + private static String getX509CertInfoModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.X509CertInfo" + : "sun.security.x509.X509CertInfo"; + } + + private static String getX509CertifValidityModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.CertificateValidity" + : "sun.security.x509.CertificateValidity"; + } + + private static String getX509X500NameModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.X500Name" + : "sun.security.x509.X500Name"; + } + + private static String getCertificateSerialNumberModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.CertificateSerialNumber" + : "sun.security.x509.CertificateSerialNumber"; + } + + private static String getCertificateSubjectNameModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.CertificateSubjectName" + : "sun.security.x509.CertificateSubjectName"; + } + + private static String getCertificateIssuerNameModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.CertificateIssuerName" + : "sun.security.x509.CertificateIssuerName"; + } + + private static String getCertificateX509KeyModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.CertificateX509Key" + : "sun.security.x509.CertificateX509Key"; + } + + private static String getCertificateVersionModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.CertificateVersion" + : "sun.security.x509.CertificateVersion"; + } + + private static String getAlgorithmIdModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.AlgorithmId" + : "sun.security.x509.AlgorithmId"; + } + + private static String getObjectIdentifierModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.util.ObjectIdentifier" + : "sun.security.util.ObjectIdentifier"; + } + + private static String getCertificateAlgorithmIdModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.CertificateAlgorithmId" + : "sun.security.x509.CertificateAlgorithmId"; + } + + private static String getX509CertImplModuleName() { + return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.x509.X509CertImpl" + : "sun.security.x509.X509CertImpl"; + } + + private static String getSetField(Object obj, String setString) + throws ReflectiveOperationException, SecurityException { + Field privateStringField = obj.getClass().getDeclaredField(setString); + privateStringField.setAccessible(true); + String fieldValue = (String) privateStringField.get(obj); + return fieldValue; + } +} +