提交 7158fd0c 编写于 作者: C coffeys

Merge

/*
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -1501,7 +1501,9 @@ final public class AccessBridge extends AccessBridgeLoader {
s.indexOf(AccessibleState.MANAGES_DESCENDANTS.toDisplayString(Locale.US)) == -1) {
// Indicate whether this component manages its own
// children
AccessibleRole role = ac.getAccessibleRole();
AccessibleRole role = InvocationUtils.invokeAndWait(() -> {
return ac.getAccessibleRole();
}, ac);
if (role == AccessibleRole.LIST ||
role == AccessibleRole.TABLE ||
role == AccessibleRole.TREE) {
......@@ -1759,7 +1761,9 @@ final public class AccessBridge extends AccessBridgeLoader {
*/
private AccessibleComponent getAccessibleComponentFromContext(AccessibleContext ac) {
if (ac != null) {
AccessibleComponent acmp = ac.getAccessibleComponent();
AccessibleComponent acmp = InvocationUtils.invokeAndWait(() -> {
return ac.getAccessibleComponent();
}, ac);
if (acmp != null) {
debugString("Returning AccessibleComponent Context");
return acmp;
......
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -28,6 +28,7 @@
#include <winsock2.h> /* needed for htonl */
#include <iprtrmib.h>
#include <assert.h>
#include <limits.h>
#include "java_net_NetworkInterface.h"
#include "jni_util.h"
......@@ -70,7 +71,7 @@ void printnifs (netif *netifPP, char *str) {
#endif
static int bufsize = 1024;
static int bufsize = 4096;
/*
* return an array of IP_ADAPTER_ADDRESSES containing one element
......@@ -94,7 +95,12 @@ static int getAdapters (JNIEnv *env, IP_ADAPTER_ADDRESSES **adapters) {
ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterInfo, &len);
if (ret == ERROR_BUFFER_OVERFLOW) {
IP_ADAPTER_ADDRESSES * newAdapterInfo = (IP_ADAPTER_ADDRESSES *) realloc (adapterInfo, len);
IP_ADAPTER_ADDRESSES * newAdapterInfo = NULL;
if (len < (ULONG_MAX - bufsize)) {
len = len + bufsize;
}
newAdapterInfo =
(IP_ADAPTER_ADDRESSES *) realloc (adapterInfo, len);
if (newAdapterInfo == NULL) {
free(adapterInfo);
JNU_ThrowByName(env, "java/lang/OutOfMemoryError", "Native heap allocation failure");
......@@ -103,7 +109,6 @@ static int getAdapters (JNIEnv *env, IP_ADAPTER_ADDRESSES **adapters) {
adapterInfo = newAdapterInfo;
bufsize = len;
ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterInfo, &len);
}
......@@ -137,7 +142,12 @@ IP_ADAPTER_ADDRESSES *getAdapter (JNIEnv *env, jint index) {
flags |= GAA_FLAG_INCLUDE_PREFIX;
val = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterInfo, &len);
if (val == ERROR_BUFFER_OVERFLOW) {
IP_ADAPTER_ADDRESSES * newAdapterInfo = (IP_ADAPTER_ADDRESSES *) realloc (adapterInfo, len);
IP_ADAPTER_ADDRESSES * newAdapterInfo = NULL;
if (len < (ULONG_MAX - bufsize)) {
len = len + bufsize;
}
newAdapterInfo =
(IP_ADAPTER_ADDRESSES *) realloc (adapterInfo, len);
if (newAdapterInfo == NULL) {
free(adapterInfo);
JNU_ThrowByName(env, "java/lang/OutOfMemoryError", "Native heap allocation failure");
......@@ -146,7 +156,6 @@ IP_ADAPTER_ADDRESSES *getAdapter (JNIEnv *env, jint index) {
adapterInfo = newAdapterInfo;
bufsize = len;
val = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterInfo, &len);
}
......
......@@ -352,7 +352,8 @@ needs_jdk = \
javax/xml/bind/jxc \
javax/xml/ws/8033113 \
javax/xml/ws/clientjar/TestWsImport.java \
javax/xml/ws/ebcdic
javax/xml/ws/ebcdic \
javax/xml/bind/xjc/8145039/JaxbMarshallTest.java
# JRE adds further tests to compact3
#
......
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.String;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import static java.lang.System.out;
/*
* @test
* @bug 4686632 8048610
* @summary To verify Cipher.init will throw InvalidKeyException with
* Non-empty message when create SecretKeySpec with invalid DES key
* @author Kevin Liu
*/
public class Empty {
public static void main(String[] args) throws Exception {
try {
byte master[] = {
0, 1, 2, 3, 4
};
SecretKey key = new SecretKeySpec(master, "DES");
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
throw new RuntimeException("InvalidKeyException not thrown");
} catch (java.security.InvalidKeyException ike) {
ike.printStackTrace();
if (ike.getMessage() != null) {
out.println("Status -- Passed");
} else {
throw new RuntimeException("Error message is not expected when"
+ " InvalidKeyException is thrown");
}
}
}
}
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8048052
* @summary Test a series of methods which requires "setFactory" runtime permission
* @run main SetFactoryPermission success
* @run main/othervm/policy=policy.fail SetFactoryPermission fail
* @run main/othervm/policy=policy.success SetFactoryPermission success
*/
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.rmi.server.RMISocketFactory;
import java.security.AccessControlException;
public class SetFactoryPermission {
static boolean success = false;
interface Runner {
public void run() throws Exception;
}
public static void main (String[] args) throws Exception {
if (args.length > 0) {
success = System.getSecurityManager() == null || args[0].equals("success");
}
doTest(()->{
System.out.println("Verify URLConnection.setContentHandlerFactor()");
URLConnection.setContentHandlerFactory(null);
});
doTest(()->{
System.out.println("Verify URL.setURLStreamHandlerFactory()");
URL.setURLStreamHandlerFactory(null);
});
doTest(()->{
System.out.println("Verify ServerSocket.setSocketFactory()");
ServerSocket.setSocketFactory(null);
});
doTest(()->{
System.out.println("Verify Socket.setSocketImplFactory()");
Socket.setSocketImplFactory(null);
});
doTest(()->{
System.out.println("Verify RMISocketFactory.setSocketFactory()");
RMISocketFactory.setSocketFactory(null);
});
}
static void doTest(Runner func) throws Exception {
try {
func.run();
if (!success) {
throw new RuntimeException("AccessControlException is not thrown. Test failed");
}
} catch (SecurityException e) {
if (success) {
e.printStackTrace();
throw new RuntimeException("AccessControlException is thrown unexpectedly. Test failed");
}
}
}
}
grant {
permission java.lang.RuntimePermission "setFactory";
};
/*
* Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import static java.lang.System.out;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Base64;
import java.util.Enumeration;
/*
* @test
* @bug 8048619
* @author Bill Situ
* @summary Test converting keystore from jceks to P12 and from P12 to other
* (jceks,jks). including following test cases:
* Read jceks key store and convert to the p12 key store, then compare entries
* in the two key stores.
* Read p12 key store and convert to the jceks key store, then compare entries
* in the two key stores.
* Read p12 key store (contains only private key and a self-signed certificate)
* and convert to the jceks key store, then compare entries of two key stores.
* Read p12 key store (contains 2 entries) and convert to the jceks key store,
* then compare entries in the two key stores.
* Read p12 key store (entry password and key store password are different) and
* convert to the jceks key store, then compare entries in the two key stores.
* Read p12 key store and convert to the jks key store, then compare entries
* in the two key stores.
* Read p12 key store (contains only private key and a self-signed certificate)
* and convert to the jks key store, then compare entries in the two key stores.
* Read p12 key store (contains 2 entries) and convert to the jks key store,
* then compare entries in the two key stores.
* Read p12 key store (entry password and key store password are different) and
* convert to the jks key store, then compare entries in the two key stores.
*/
public class ConvertP12Test {
private static final String SUN_JSSE = "SunJSSE";
private static final String SUN_JCE = "SunJCE";
private static final String SUN = "SUN";
private static final String PKCS12 = "pkcs12";
private static final String JCE_KS = "JceKS";
private static final String JKS = "JKS";
public static void main(String args[]) throws Exception {
ConvertP12Test jstest = new ConvertP12Test();
jstest.driver("JceksToP12", "keystoreCA.jceks.data", JCE_KS, SUN_JCE,
"storepass", "keypass", PKCS12, SUN_JSSE);
jstest.driver("P12ToJceks_Chain", "ie_jceks_chain.pfx.data", PKCS12,
SUN_JSSE, "pass", "pass", JCE_KS, SUN_JCE);
jstest.driver("P12ToJceks_SelfSigned", "jdk_jceks_selfsigned.p12.data",
PKCS12, SUN_JSSE, "pass", "pass", JCE_KS, SUN_JCE);
jstest.driver("P12ToJceks_TwoEntry", "jdk_jceks_twoentry.p12.data",
PKCS12, SUN_JSSE, "pass", "pass", JCE_KS, SUN_JCE);
jstest.driver("P12ToJceks_TwoPass", "jdk_jceks_twopass.p12.data",
PKCS12, SUN_JSSE, "storepass", "keypass", JCE_KS, SUN_JCE);
jstest.driver("P12ToJks_Chain", "ie_jks_chain.pfx.data", PKCS12,
SUN_JSSE, "pass", "pass", JKS, SUN);
jstest.driver("P12ToJks_SelfSigned", "jdk_jks_selfsigned.p12.data",
PKCS12, SUN_JSSE, "pass", "pass", JKS, SUN);
jstest.driver("P12ToJks_TwoEntry", "jdk_jks_twoentry.p12.data", PKCS12,
SUN_JSSE, "pass", "pass", JKS, SUN);
jstest.driver("P12ToJks_TwoPass", "jdk_jks_twopass.p12.data", PKCS12,
SUN_JSSE, "storepass", "keypass", JKS, SUN);
}
private void driver(String testCase, String inKeyStore,
String inKeyStoreType, String inKeyStoreTypePrv,
String inStorePass, String inKeyPass, String outKeyStoreType,
String outKeyStorePrv) throws Exception {
String outStorePass = "pass";
String outKeyPass = "pass";
KeyStore inputKeyStore, outputKeyStore;
out.println("Testing " + testCase);
String keystorePath = System.getProperty("test.src", ".")
+ File.separator + "certs" + File.separator + "convertP12";
out.println("Output KeyStore : " + inKeyStore + ".out");
String outKeyStoreName = inKeyStore + ".out";
try (FileOutputStream fout = new FileOutputStream(outKeyStoreName);) {
inputKeyStore = KeyStore.getInstance(inKeyStoreType,
inKeyStoreTypePrv);
// KeyStore have encoded by Base64.getMimeEncoder().encode(),need
// decode first.
byte[] input = Files.readAllBytes(Paths.get(keystorePath,
inKeyStore));
ByteArrayInputStream arrayIn = new ByteArrayInputStream(Base64
.getMimeDecoder().decode(input));
out.println("Input KeyStore : " + inKeyStore);
inputKeyStore.load(arrayIn, inStorePass.toCharArray());
outputKeyStore = KeyStore.getInstance(outKeyStoreType,
outKeyStorePrv);
outputKeyStore.load(null, null);
run(inputKeyStore, outputKeyStore, inKeyPass, outKeyPass);
outputKeyStore.store(fout, outStorePass.toCharArray());
// for P12ToJks_TwoEntry test case will test includes each other,
// others just test compareKeystore
if (testCase.contains("TwoEntry")) {
compareKeyStore(inputKeyStore, outputKeyStore, inKeyPass,
outKeyPass, 2);
compareKeyStore(outputKeyStore, inputKeyStore, outKeyPass,
inKeyPass, 2);
} else {
compareKeyStore(inputKeyStore, outputKeyStore, inKeyPass,
outKeyPass, 1);
}
out.println("Test " + testCase + " STATUS: Pass!!");
} catch (Exception ex) {
out.println("Test " + testCase + " STATUS: failed with exception: "
+ ex.getMessage());
throw ex;
}
}
private void run(KeyStore inputKeyStore, KeyStore outputKeyStore,
String inKeyPass, String outKeyPass) throws Exception {
Enumeration<String> e = inputKeyStore.aliases();
String alias;
while (e.hasMoreElements()) {
alias = e.nextElement();
Certificate[] certs = inputKeyStore.getCertificateChain(alias);
boolean isCertEntry = inputKeyStore.isCertificateEntry(alias);
// Test KeyStore only contain key pair entries.
if (isCertEntry == true) {
throw new RuntimeException(
"inputKeystore should not be certEntry because test"
+ " keystore only contain key pair entries"
+ " for alias:" + alias);
}
boolean isKeyEntry = inputKeyStore.isKeyEntry(alias);
Key key = null;
if (isKeyEntry) {
key = inputKeyStore.getKey(alias, inKeyPass.toCharArray());
} else {
throw new RuntimeException("Entry type unknown for alias:"
+ alias);
}
outputKeyStore.setKeyEntry(alias, key, outKeyPass.toCharArray(),
certs);
}
}
private void compareKeyStore(KeyStore a, KeyStore b, String inKeyPass,
String outKeyPass, int keyStoreSize) throws Exception {
if (a.size() != keyStoreSize || b.size() != keyStoreSize) {
throw new RuntimeException("size not match or size not equal to "
+ keyStoreSize);
}
Enumeration<String> eA = a.aliases();
while (eA.hasMoreElements()) {
String aliasA = eA.nextElement();
if (!b.containsAlias(aliasA)) {
throw new RuntimeException("alias not match for alias:"
+ aliasA);
}
compareKeyEntry(a, b, inKeyPass, outKeyPass, aliasA);
}
}
private void compareKeyEntry(KeyStore a, KeyStore b, String aPass,
String bPass, String alias) throws KeyStoreException,
UnrecoverableKeyException, NoSuchAlgorithmException {
Certificate[] certsA = a.getCertificateChain(alias);
Certificate[] certsB = b.getCertificateChain(alias);
if (!Arrays.equals(certsA, certsB)) {
throw new RuntimeException("Certs don't match for alias:" + alias);
}
Key keyA = a.getKey(alias, aPass.toCharArray());
Key keyB = b.getKey(alias, bPass.toCharArray());
if (!keyA.equals(keyB)) {
throw new RuntimeException(
"Key don't match for alias:" + alias);
}
}
}
/*
* Copyright (c) 2003,2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import static java.lang.System.out;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Enumeration;
/*
* @test
* @bug 8048617
* @author Bill Situ
* @summary Read different types p12 key store to Check the read related APIs.
* including following test cases:
* ReadP12_IE_Chain: Read p12 key store (contains private key and associated
* certificate chain) from IE.
* ReadP12_IE_Self: Read p12 key store (contains only private key and
* self-signed certificate) from IE.
* ReadP12_JDK_Chain: Read p12 key store (contains private key and associated
* certificate chain) from JDK
* ReadP12_JDK_Self: Read p12 key store (contains only private key and
* self-signed certificate) from JDK.
* ReadP12_Mozilla_Self: Read p12 key store (contains only private key and
* self-signed certificate) from Mozilla.
* ReadP12_Mozilla_Chain: Read p12 key store (contains private key and
* associated certificate chain) from Mozilla.
* ReadP12_Mozilla_TwoEntries: Read p12 key store (contains 2 entries) from
* Mozilla.
* ReadP12_Netscape_Chain: Read p12 key store (contains private key and
* associated certificate chain) from Netscape.
* ReadP12_Netscape_Self: Read p12 key store (contains only private key and
* self-signed certificate) from Netscape.
* ReadP12_Netscape_TwoEntries: Read p12 key store (contains 2 entries) from
* Netscape.
* ReadP12_OpenSSL: Read p12 key store from OpenSSL.
*/
public class ReadP12Test {
private final static String IN_KETYSTORE_TYPE = "pkcs12";
private final static String IN_KEYSTORE_PRV = "SunJSSE";
private final static String IN_STORE_PASS = "pass";
public static void main(String args[]) throws Exception {
ReadP12Test jstest = new ReadP12Test();
String testCase = "";
try {
testCase = "ReadP12_IE_Chain";
jstest.readTest("ie_chain.pfx.data");
testCase = "ReadP12_IE_Self";
jstest.readTest("ie_self.pfx.data");
testCase = "ReadP12_JDK_Chain";
jstest.readTest("jdk_chain.p12.data");
testCase = "ReadP12_JDK_Self";
jstest.readTest("jdk_self.p12.data");
testCase = "ReadP12_Mozilla_Chain";
jstest.readTest("mozilla_chain.p12.data");
testCase = "ReadP12_Mozilla_Self";
jstest.readTest("mozilla_self.p12.data");
testCase = "ReadP12_Mozilla_TwoEntries";
jstest.readTest("mozilla_twoentries.p12.data");
testCase = "ReadP12_Netscape_Chain";
jstest.readTest("netscape_chain.p12.data");
testCase = "ReadP12_Netscape_Self";
jstest.readTest("netscape_self.p12.data");
testCase = "ReadP12_Netscape_TwoEntries";
jstest.readTest("netscape_twoentries.p12.data");
testCase = "ReadP12_openssl";
jstest.readTest("openssl.p12.data");
} catch (Exception e) {
System.err.println(testCase + ": failed with execption: "
+ e.getMessage());
throw e;
}
out.println(testCase + ": Pass!!");
}
private void readTest(String inKeyStore) throws Exception {
KeyStore inputKeyStore;
// Initialize KeyStore
String dir = System.getProperty("test.src", ".");
String keystorePath = dir + File.separator + "certs" + File.separator
+ "readP12";
inputKeyStore = KeyStore
.getInstance(IN_KETYSTORE_TYPE, IN_KEYSTORE_PRV);
// KeyStore have encoded by Base64.getMimeEncoder().encode(),need decode
// first.
byte[] input = Files.readAllBytes(Paths.get(keystorePath, inKeyStore));
ByteArrayInputStream arrayIn = new ByteArrayInputStream(Base64
.getMimeDecoder().decode(input));
inputKeyStore.load(arrayIn, IN_STORE_PASS.toCharArray());
out.println("Initialize KeyStore : " + inKeyStore + " success");
out.println("getProvider : " + inputKeyStore.getProvider());
out.println("getType : " + inputKeyStore.getType());
out.println("getDefaultType : " + KeyStore.getDefaultType());
int idx = 0;
Enumeration<String> e = inputKeyStore.aliases();
String alias;
while (e.hasMoreElements()) {
alias = e.nextElement();
out.println("Alias " + idx + " : " + alias);
if (inputKeyStore.containsAlias(alias) == false) {
throw new RuntimeException("Alias not found");
}
out.println("getCreationDate : "
+ inputKeyStore.getCreationDate(alias));
X509Certificate cert = (X509Certificate) inputKeyStore
.getCertificate(alias);
out.println("getCertificate : " + cert.getSubjectDN());
String retAlias = inputKeyStore.getCertificateAlias(cert);
if (!retAlias.equals(alias)) {
throw new RuntimeException("Alias mismatch");
}
out.println("getCertificateAlias : " + retAlias);
Certificate[] certs = inputKeyStore.getCertificateChain(alias);
for (int i = 0; i < certs.length; i++) {
out.println("getCertificateChain " + i + " : "
+ ((X509Certificate) certs[i]).getSubjectDN());
}
boolean isCertEntry = inputKeyStore.isCertificateEntry(alias);
// test KeyStore only contain key pair entries.
if (isCertEntry == true) {
throw new RuntimeException(
"inputKeystore should not be certEntry because test keystore only contain key pair entries.");
}
boolean isKeyEntry = inputKeyStore.isKeyEntry(alias);
if (isKeyEntry) {
Key key = inputKeyStore.getKey(alias,
IN_STORE_PASS.toCharArray());
out.println("Key : " + key.toString());
} else {
throw new RuntimeException("Entry type unknown\n");
}
idx++;
}
int size = inputKeyStore.size();
if (idx != size) {
throw new RuntimeException("Size not match");
}
}
}
/*
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import static java.lang.System.out;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Base64;
import java.util.Enumeration;
/*
* @test
* @bug 8048618
* @summary Write different types p12 key store to Check the write related
* APIs.
* @run main WriteP12Test
*/
public class WriteP12Test {
private static final String IN_KEYSTORE_TYPE = "jks";
private static final String IN_KEYSTORE_PRV = "SUN";
private static final String IN_KEYSTORE_ENDUSER = "keystoreEU.jks.data";
private static final String IN_KEYSTORE_CA = "keystoreCA.jks.data";
private static final String OUT_KEYSTORE = "outKeyStore.p12";
private static final String IN_STORE_PASS = "storepass";
private static final String IN_KEY_PASS = "keypass";
private static final String CERT_PATH = System.getProperty("test.src", ".")
+ File.separator + "certs" + File.separator + "writeP12"
+ File.separator;
private static final String CA_CERT_STR = "-----BEGIN CERTIFICATE-----\n"
+ "MIIDFzCCAf8CBD8+0nAwDQYJKoZIhvcNAQEFBQAwUDELMAkGA1UEBhMCV\n"
+ "VMxETAPBgNVBAoTCEphdmFTb2Z0MRUwEwYDVQQLEwxTZWN1cml0eSBTUU\n"
+ "UxFzAVBgNVBAMTDlBLQ1MxMiBUZXN0IENBMB4XDTAzMDgxNzAwNTUxMlo\n"
+ "XDTEzMDgxNDAwNTUxMlowUDELMAkGA1UEBhMCVVMxETAPBgNVBAoTCEph\n"
+ "dmFTb2Z0MRUwEwYDVQQLEwxTZWN1cml0eSBTUUUxFzAVBgNVBAMTDlBLQ\n"
+ "1MxMiBUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQ\n"
+ "EAk7Sh+K/yGsmJacZnjfkZfuWxGNJCPW0q69exwoRP+eBHMQwG00yi9aL\n"
+ "SsZAqNpJSCDvpgySOAUmBd+f8WFhHqJfRVREVfv3mradDKZCjhqtsUI7I\n"
+ "wRTYYy9clFkeeK4dHaxbuFMPpUu7yQfwSTXgvOA/UJ4kJuGtaYAdTJI4e\n"
+ "f1mUASo6+dea0UZA/FHCuV7O6z3hr5VHlyhJL2/o/8M5tGBTBISODJSnn\n"
+ "GNBvtQLNHnWYvs470UAE2BtuCGYh1V/3HAH1tRirS3MBBcb1XnIkiiXR3\n"
+ "tjaBSB+XhoCfuG8KtInXXFaAnvKfY9mYFw6VJt9JYQpY2VDC7281/Pbz0\n"
+ "dQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBzXZ8zHWrnC8/E+N/n2Czhx\n"
+ "i18YQc2LPWBDLYTTxoFEazWmYLv1k/JT7Nta1qu1quvxXJ4uV1XHbd9NF\n"
+ "AJWKwtFQEpfv4o6I7qWUPoxnfA+jyqKXxv27z25tzt+Y4xOEhqvO03G0Q\n"
+ "imhkiNt9MF7L69y2U0/U73+uFNGzdAEDiI9EibvICiOnr1TeQ5GekK3Yb\n"
+ "k5qe3lviMZPkkSXepTJI8m0AiXCji+eXj97jVLeH+RxeBchBY+uELrqUr\n"
+ "sVOVWh7IBCqC/V7FqUTkmD1IFlzkkinatpl42s1MbhJId2yQkzaeBRc\n"
+ "suE63bDEtuRWp9ynMO3QA4Yu85uBRWGzQ1Di\n"
+ "-----END CERTIFICATE-----";
private static final String LEAD_CERT = "-----BEGIN CERTIFICATE-----\n"
+ "MIICwDCCAaigAwIBAgIEPz7S1jANBgkqhkiG9w0BAQQFADBQMQswCQYDV\n"
+ "QQGEwJVUzERMA8GA1UEChMISmF2YVNvZnQxFTATBgNVBAsTDFNlY3VyaX\n"
+ "R5IFNRRTEXMBUGA1UEAxMOUEtDUzEyIFRlc3QgQ0EwHhcNMDAwODA5MDc\n"
+ "wMDAwWhcNMTAwODA3MDcwMDAwWjBSMQswCQYDVQQGEwJVUzERMA8GA1UE\n"
+ "ChMISmF2YVNvZnQxFTATBgNVBAsTDFNlY3VyaXR5IFNRRTEZMBcGA1UEA\n"
+ "xMQUEtDUzEyIFRlc3QgTGVhZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgY\n"
+ "kCgYEAzq9X2USz/WjDhT+jUyZWqB5h4A33tS11YqH5qYvqjTXjcUI6gOp\n"
+ "moXMafDG9RHRlIccvp51xLp7Ap3WMrv411lWBttqtZi5c1/DEC1cEM/Sl\n"
+ "PCk1r2zFbkJu7QKieXeMcrjZEo6LcBHMwQjIpI+up9cr3VjuyqG/olQkU\n"
+ "mXVuS0CAwEAAaMkMCIwDwYDVR0PAQH/BAUDAweAADAPBgNVHRMBAf8EBT\n"
+ "ADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBhbuim98TWmtv9vSldRE7RvQ8\n"
+ "FlS0TyZVO7kcSNtfCUE4R76J1ElN74Koc5pQnUtduLeQJs2ao/mEcCZsE\n"
+ "zVcwI3mSZrSzPhc8s7w5gOQA4TUwVLSSjKgBCaZ7R3+qJ3QeqPJ5O6sFz\n"
+ "pvBYkgSa4MWptK41jbmT8cwZQJXFCi8WxFFJ+p97F1Ppm3LgmYmtiUP4M\n"
+ "ZQwOBvpTZWXU0WrqFXpzWQx0mg4SX19fZm4nLcJAerCEUphf8ILagtpQM\n"
+ "EErT3/jg6mfCdT3Rj055QXPfF4OiRFevPF5a1fZgrCePCukRQZcd7s8K5\n"
+ "OBIaryuM0MdFtlzxi6XWeUNpVFFHURcy\n"
+ "-----END CERTIFICATE-----";
private static final String END_CERT = "-----BEGIN CERTIFICATE-----\n"
+ "MIICNjCCAZ+gAwIBAgIEPz7WtzANBgkqhkiG9w0BAQQFADBSMQswCQYDV\n"
+ "QQGEwJVUzERMA8GA1UEChMISmF2YVNvZnQxFTATBgNVBAsTDFNlY3VyaX\n"
+ "R5IFNRRTEZMBcGA1UEAxMQUEtDUzEyIFRlc3QgTGVhZDAeFw0wMDA4MDk\n"
+ "wNzAwMDBaFw0xMDA4MDcwNzAwMDBaMFgxCzAJBgNVBAYTAlVTMREwDwYD\n"
+ "VQQKEwhKYXZhU29mdDEVMBMGA1UECxMMU2VjdXJpdHkgU1FFMR8wHQYDV\n"
+ "QQDExZQS0NTMTIgVGVzdCBFbmQgVXNlciAxMIGfMA0GCSqGSIb3DQEBAQ\n"
+ "UAA4GNADCBiQKBgQDIKomSYomDzH/V63eDQEG7od0DLcnnVZ81pbWhDss\n"
+ "8gHV2m8pADdRqdihBmnSQEaMW4D3uZ4sFE1LtkQls6hjd7SdOsG5Y24L8\n"
+ "15jot9a2JcB73H8H0VKirrObL5BZdt7BtASPDnYtW4Spt++YjDoJFxyF0\n"
+ "HchkavzXaVTlexakwIDAQABoxMwETAPBgNVHQ8BAf8EBQMDB4AAMA0GCS\n"
+ "qGSIb3DQEBBAUAA4GBAIFA3JXEmb9AXn3RD7t+Mn6DoyVDIy5jsn6xOKT\n"
+ "JV25I0obpDUzgw4QaAMmM0ZvusOmZ2wZNS8MtyTUgdANyakbzn5SdxbTy\n"
+ "TLEqQsFbX8UVC38fx5ZM6ExA5YSAvgmXudZpOVC0ATccoZS3JFU8CxSfW\n"
+ "+Q3IC2MLh+QTg3hUJ5b\n-----END CERTIFICATE-----";
private final Certificate testerCert;
private final Certificate testLeadCert;
private final Certificate caCert;
WriteP12Test() throws CertificateException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
caCert = cf.generateCertificate(new ByteArrayInputStream(CA_CERT_STR
.getBytes()));
testLeadCert = cf.generateCertificate(new ByteArrayInputStream(
LEAD_CERT.getBytes()));
testerCert = cf.generateCertificate(new ByteArrayInputStream(END_CERT
.getBytes()));
}
public static void main(String[] args) throws CertificateException,
UnrecoverableKeyException, KeyStoreException,
NoSuchProviderException, NoSuchAlgorithmException, IOException {
WriteP12Test jstest = new WriteP12Test();
out.println("test WriteP12CertChain");
/*
* WriteP12CertChain: This test creates a p12 keystore contains one
* entry with private key and a certificate chains contains three
* certificates in the order of user->lead->ca. This case expects to
* pass.
*/
jstest.test(new Certificate[] { jstest.testerCert, jstest.testLeadCert,
jstest.caCert }, IN_KEYSTORE_ENDUSER, "pkcs12testenduser1",
"pass", "pass");
/*
* WriteP12CertChainBad: same as WriteP12CertChain but chains order is
* user-ca-lead, the order is wrong so expects to fail.
*/
out.println("test WriteP12CertChainBad");
try {
jstest.test(new Certificate[] { jstest.testerCert, jstest.caCert,
jstest.testLeadCert }, IN_KEYSTORE_ENDUSER,
"pkcs12testenduser1", "pass", "pass");
throw new RuntimeException(
" Certificate chain is not valid, test should not pass."
+ " Test failed.");
} catch (KeyStoreException e) {
e.printStackTrace();
out.println(" Certificate chain is not valid,exception is"
+ " expected. Test passed.");
}
/*
* WriteP12PrivateKey:This test creates a p12 contains a self-signed
* cert and private key,expects no exception
*/
out.println("test WriteP12PrivateKey");
jstest.test(null, IN_KEYSTORE_ENDUSER, "pkcs12testenduser1", "pass",
"pass");
/*
* WriteP12TwoEntry: This test creates a p12 keystore with different
* storepass and keypass, and contains two entries.
*/
out.println("test WriteP12TwoEntry");
jstest.testTwoEntry(IN_KEYSTORE_ENDUSER, IN_KEYSTORE_CA,
"pkcs12testenduser1", "pass", "pass");
/*
* WriteP12TwoPass: This test creates a p12 keystore with different
* storepass and keypass, and contains one entry with private key and a
* certificate
*/
out.println("test WriteP12TwoPass");
jstest.test(null, IN_KEYSTORE_CA, "pkcs12testCA", "storepass",
"keypass");
}
private void test(Certificate certs[], String inKeyStorePath,
String userAlias, String outStorePass, String outKeyPass)
throws KeyStoreException, NoSuchProviderException, IOException,
CertificateException, UnrecoverableKeyException,
NoSuchAlgorithmException {
// init output key store
KeyStore outputKeyStore = KeyStore.getInstance("pkcs12", "SunJSSE");
outputKeyStore.load(null, null);
try (FileOutputStream fout = new FileOutputStream(OUT_KEYSTORE)) {
// KeyStore have encoded by Base64.getMimeEncoder().encode(),need
// decode first.
byte[] input = Files.readAllBytes(Paths.get(CERT_PATH,
inKeyStorePath));
ByteArrayInputStream arrayIn = new ByteArrayInputStream(Base64
.getMimeDecoder().decode(input));
// input key store
KeyStore inputKeyStore = KeyStore.getInstance(IN_KEYSTORE_TYPE,
IN_KEYSTORE_PRV);
inputKeyStore.load(arrayIn, IN_STORE_PASS.toCharArray());
// add key/certificate to output key store
Key key = inputKeyStore
.getKey(userAlias, IN_KEY_PASS.toCharArray());
out.println("Input Key Algorithm " + key.getAlgorithm());
out.println("====Input Certs=====");
if (certs == null) {
certs = new Certificate[] { inputKeyStore
.getCertificate(userAlias) };
}
for (Certificate cert : certs) {
out.println(((X509Certificate) cert).getSubjectDN());
}
outputKeyStore.setKeyEntry(userAlias, key,
outKeyPass.toCharArray(), certs);
Certificate retCerts[] = outputKeyStore
.getCertificateChain(userAlias);
out.println("====Output Certs=====");
for (Certificate retCert : retCerts) {
out.println(((X509Certificate) retCert).getSubjectDN());
}
out.println("====Output Key Algorithm=====");
Key outKey = outputKeyStore.getKey(userAlias,
outKeyPass.toCharArray());
out.println(outKey.getAlgorithm());
if (!key.equals(outKey)) {
throw new RuntimeException("key don't match");
}
if (!Arrays.equals(certs, retCerts)) {
throw new RuntimeException("certs don't match");
}
// save output
outputKeyStore.store(fout, outStorePass.toCharArray());
// test output
testKeyStore(outputKeyStore, outKeyPass.toCharArray());
}
}
private void testTwoEntry(String inKeyStoreOnePath,
String inKeyStoreTwoPath, String userAlias, String outStorePass,
String outKeyPass) throws KeyStoreException,
NoSuchProviderException, NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableKeyException {
// initial KeyStore
KeyStore outputKeyStore = KeyStore.getInstance("pkcs12", "SunJSSE");
try (FileOutputStream fout = new FileOutputStream(OUT_KEYSTORE);) {
outputKeyStore.load(null, null);
KeyStore inputKeyStoreOne, inputKeyStoreTwo;
inputKeyStoreOne = KeyStore.getInstance(IN_KEYSTORE_TYPE,
IN_KEYSTORE_PRV);
// KeyStore have encoded by Base64.getMimeEncoder().encode(),need
// decode first.
byte[] inputBytes = Files.readAllBytes(Paths.get(CERT_PATH,
inKeyStoreOnePath));
ByteArrayInputStream arrayIn = new ByteArrayInputStream(Base64
.getMimeDecoder().decode(inputBytes));
// input key store
inputKeyStoreOne.load(arrayIn, IN_STORE_PASS.toCharArray());
inputBytes = Files.readAllBytes(Paths.get(CERT_PATH,
inKeyStoreTwoPath));
arrayIn = new ByteArrayInputStream(Base64.getMimeDecoder().decode(
inputBytes));
inputKeyStoreTwo = KeyStore.getInstance(IN_KEYSTORE_TYPE,
IN_KEYSTORE_PRV);
inputKeyStoreTwo.load(arrayIn, IN_STORE_PASS.toCharArray());
// add key/certificate to output key store
out.println("====First Entry=====");
Key inputKey = inputKeyStoreOne.getKey(userAlias,
IN_KEY_PASS.toCharArray());
Certificate cert = inputKeyStoreOne.getCertificate(userAlias);
Certificate certs[] = new Certificate[1];
certs[0] = cert;
out.println("====Input1 Key=====");
out.println(inputKey.getAlgorithm());
out.println("====Input1 Certs=====");
out.println("Certificate :");
out.println(((X509Certificate) cert).getSubjectDN());
outputKeyStore.setKeyEntry("USER", inputKey,
outKeyPass.toCharArray(), certs);
out.println("====Second Entry=====");
String caAlias = "pkcs12testca";
inputKey = inputKeyStoreTwo.getKey(caAlias,
IN_KEY_PASS.toCharArray());
cert = inputKeyStoreTwo.getCertificate(caAlias);
certs[0] = cert;
out.println("====Input2 Key=====");
out.println(inputKey.getAlgorithm());
out.println("====Input2 Certs=====");
out.println("Certificate :");
out.println(((X509Certificate) cert).getSubjectDN());
outputKeyStore.setKeyEntry("CA", inputKey,
outKeyPass.toCharArray(), certs);
// save output
outputKeyStore.store(fout, outStorePass.toCharArray());
// test output
testKeyStore(outputKeyStore, outKeyPass.toCharArray());
}
}
private void testKeyStore(KeyStore inputKeyStore, char[] keypass)
throws KeyStoreException, UnrecoverableKeyException,
NoSuchAlgorithmException {
out.println("========== Key Store ==========");
out.println("getProvider : " + inputKeyStore.getProvider());
out.println("getType : " + inputKeyStore.getType());
out.println("getDefaultType : " + KeyStore.getDefaultType());
int idx = 0;
Enumeration<String> e = inputKeyStore.aliases();
String alias;
while (e.hasMoreElements()) {
alias = e.nextElement();
if (!inputKeyStore.containsAlias(alias)) {
throw new RuntimeException("Alias not found");
}
out.println("Alias " + idx + " : " + alias);
out.println("getCreationDate : "
+ inputKeyStore.getCreationDate(alias));
X509Certificate cert = (X509Certificate) inputKeyStore
.getCertificate(alias);
out.println("getCertificate : " + cert.getSubjectDN());
String retAlias = inputKeyStore.getCertificateAlias(cert);
if (!retAlias.equals(alias)) {
throw new RuntimeException("Alias mismatch, actually "
+ retAlias + ", expected " + alias);
}
out.println("getCertificateAlias : " + retAlias);
Certificate[] certs = inputKeyStore.getCertificateChain(alias);
int i = 0;
for (Certificate certification : certs) {
out.println("getCertificateChain " + i
+ ((X509Certificate) certification).getSubjectDN());
i++;
}
if (inputKeyStore.isCertificateEntry(alias)) {
throw new RuntimeException(
"inputKeystore should not be certEntry because this"
+ " keystore only contain key pair entries.");
}
if (!inputKeyStore.isKeyEntry(alias)) {
throw new RuntimeException("Entry type unknown.");
}
idx++;
}
int size = inputKeyStore.size();
if (idx != size) {
throw new RuntimeException("Size not match, actually " + idx
+ ", expected " + size);
}
}
}
MIINXgIBAzCCDRoGCSqGSIb3DQEHAaCCDQsEgg0HMIINAzCCA7wGCSqGSIb3DQEHAaCCA60EggOp
MIIDpTCCA6EGCyqGSIb3DQEMCgECoIICtjCCArIwHAYKKoZIhvcNAQwBAzAOBAipOP0AVsizDgIC
B9AEggKQIP/YI9/C2WYbIWxKuqXMD8WPCvqj1fhHmZJ0epCzgEdOR7GT/h2Fy4/wxrthPkj4JqkS
akQog3pjOFtj9D8QtkOw/b761qsyj17TYlQS9C6qVhcddMA+Ca2NcDhKlYofQMNTuYPXkXlpCh5R
CNFgQ+PLVZwNZjqoitjv0RLQqBudhTmJSvfDlW2w+CpbziEeRNzn0pX0/Ts7KxykDscOmUCGHKic
b6FqHoioElcmBp7ae3zdXuvI1x/1Y435qju2yODPpMXEZbdsD5iL07RZyL2vm6lfQbLc37TszDBx
ZZJ7ja5F4V/j/6/AVLkcqfZxFOnXz5Ki9rQblYJbkkTpJAyiNqi8Gx+zgPGtLWvV6KRD0zmxo6q/
OmdjKz4v9aG7MDSXenoy6tPAOvAQcQYaksvFZs1FjorJJpFzasfTUfy94JzrHUzRSPzNRDANHG/6
TgxC1FMNw+iQUY9L8j4xrWsr2JN5tAgYcWz1qZrp4cx0he9cbQeqYcjv7ZvIQbIe2zxdvxh7WByy
r8hNMe3RkMOM2yuP85JuWipq+9jt4/CrimKljN1ULPw+V9FZzY8kKcEiSPD+KXdJNkrMr77/lUJz
PGNYpFBFb4natmi31ZBH2VomTeKPpeanN/ghWojft1mGd1s1nD4NelrWATMVquH2Cq6nhKLRHi4c
KbQsMo+ftLvkDHHtpYenjGHbwEFfowkwn9slsZqmPEATV6caHNITCBbhQmvvhqPDPPViV+u1M1c7
vwf/ol7IjBlubwzBJAg6f5GW0tMNHxfg5E7O27AyREyvexk0IVakzIwUuP2anPTjPW0vyeePLtiG
TXNoUe+5UIzpshnLmSlerhVGoB+HBM1yoaaJHay7sdyQbVUxgdcwEwYJKoZIhvcNAQkVMQYEBAEA
AAAwWwYJKoZIhvcNAQkUMU4eTAB7ADcAMQA5AEIAQgA4ADkANAAtADMANQA2ADEALQA0AEMAMAA0
AC0AOQBCADIAMAAtADMAOQBCAEMARAAzADQANwAzADUAMQAwAH0wYwYJKwYBBAGCNxEBMVYeVABN
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
CxwCAgfQ
\ No newline at end of file
MIINXgIBAzCCDRoGCSqGSIb3DQEHAaCCDQsEgg0HMIINAzCCA7wGCSqGSIb3DQEHAaCCA60EggOp
MIIDpTCCA6EGCyqGSIb3DQEMCgECoIICtjCCArIwHAYKKoZIhvcNAQwBAzAOBAipOP0AVsizDgIC
B9AEggKQIP/YI9/C2WYbIWxKuqXMD8WPCvqj1fhHmZJ0epCzgEdOR7GT/h2Fy4/wxrthPkj4JqkS
akQog3pjOFtj9D8QtkOw/b761qsyj17TYlQS9C6qVhcddMA+Ca2NcDhKlYofQMNTuYPXkXlpCh5R
CNFgQ+PLVZwNZjqoitjv0RLQqBudhTmJSvfDlW2w+CpbziEeRNzn0pX0/Ts7KxykDscOmUCGHKic
b6FqHoioElcmBp7ae3zdXuvI1x/1Y435qju2yODPpMXEZbdsD5iL07RZyL2vm6lfQbLc37TszDBx
ZZJ7ja5F4V/j/6/AVLkcqfZxFOnXz5Ki9rQblYJbkkTpJAyiNqi8Gx+zgPGtLWvV6KRD0zmxo6q/
OmdjKz4v9aG7MDSXenoy6tPAOvAQcQYaksvFZs1FjorJJpFzasfTUfy94JzrHUzRSPzNRDANHG/6
TgxC1FMNw+iQUY9L8j4xrWsr2JN5tAgYcWz1qZrp4cx0he9cbQeqYcjv7ZvIQbIe2zxdvxh7WByy
r8hNMe3RkMOM2yuP85JuWipq+9jt4/CrimKljN1ULPw+V9FZzY8kKcEiSPD+KXdJNkrMr77/lUJz
PGNYpFBFb4natmi31ZBH2VomTeKPpeanN/ghWojft1mGd1s1nD4NelrWATMVquH2Cq6nhKLRHi4c
KbQsMo+ftLvkDHHtpYenjGHbwEFfowkwn9slsZqmPEATV6caHNITCBbhQmvvhqPDPPViV+u1M1c7
vwf/ol7IjBlubwzBJAg6f5GW0tMNHxfg5E7O27AyREyvexk0IVakzIwUuP2anPTjPW0vyeePLtiG
TXNoUe+5UIzpshnLmSlerhVGoB+HBM1yoaaJHay7sdyQbVUxgdcwEwYJKoZIhvcNAQkVMQYEBAEA
AAAwWwYJKoZIhvcNAQkUMU4eTAB7ADcAMQA5AEIAQgA4ADkANAAtADMANQA2ADEALQA0AEMAMAA0
AC0AOQBCADIAMAAtADMAOQBCAEMARAAzADQANwAzADUAMQAwAH0wYwYJKwYBBAGCNxEBMVYeVABN
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
CxwCAgfQ
\ No newline at end of file
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
\ No newline at end of file
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
\ No newline at end of file
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
1QoCAgQA
\ No newline at end of file
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
xp4CAgQA
\ No newline at end of file
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
\ No newline at end of file
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
1QoCAgQA
\ No newline at end of file
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
FgNW1MI40XfEdWafFXeJ+yQTHPyoNdk=
\ No newline at end of file
MIINXgIBAzCCDRoGCSqGSIb3DQEHAaCCDQsEgg0HMIINAzCCA7wGCSqGSIb3DQEHAaCCA60EggOp
MIIDpTCCA6EGCyqGSIb3DQEMCgECoIICtjCCArIwHAYKKoZIhvcNAQwBAzAOBAipOP0AVsizDgIC
B9AEggKQIP/YI9/C2WYbIWxKuqXMD8WPCvqj1fhHmZJ0epCzgEdOR7GT/h2Fy4/wxrthPkj4JqkS
akQog3pjOFtj9D8QtkOw/b761qsyj17TYlQS9C6qVhcddMA+Ca2NcDhKlYofQMNTuYPXkXlpCh5R
CNFgQ+PLVZwNZjqoitjv0RLQqBudhTmJSvfDlW2w+CpbziEeRNzn0pX0/Ts7KxykDscOmUCGHKic
b6FqHoioElcmBp7ae3zdXuvI1x/1Y435qju2yODPpMXEZbdsD5iL07RZyL2vm6lfQbLc37TszDBx
ZZJ7ja5F4V/j/6/AVLkcqfZxFOnXz5Ki9rQblYJbkkTpJAyiNqi8Gx+zgPGtLWvV6KRD0zmxo6q/
OmdjKz4v9aG7MDSXenoy6tPAOvAQcQYaksvFZs1FjorJJpFzasfTUfy94JzrHUzRSPzNRDANHG/6
TgxC1FMNw+iQUY9L8j4xrWsr2JN5tAgYcWz1qZrp4cx0he9cbQeqYcjv7ZvIQbIe2zxdvxh7WByy
r8hNMe3RkMOM2yuP85JuWipq+9jt4/CrimKljN1ULPw+V9FZzY8kKcEiSPD+KXdJNkrMr77/lUJz
PGNYpFBFb4natmi31ZBH2VomTeKPpeanN/ghWojft1mGd1s1nD4NelrWATMVquH2Cq6nhKLRHi4c
KbQsMo+ftLvkDHHtpYenjGHbwEFfowkwn9slsZqmPEATV6caHNITCBbhQmvvhqPDPPViV+u1M1c7
vwf/ol7IjBlubwzBJAg6f5GW0tMNHxfg5E7O27AyREyvexk0IVakzIwUuP2anPTjPW0vyeePLtiG
TXNoUe+5UIzpshnLmSlerhVGoB+HBM1yoaaJHay7sdyQbVUxgdcwEwYJKoZIhvcNAQkVMQYEBAEA
AAAwWwYJKoZIhvcNAQkUMU4eTAB7ADcAMQA5AEIAQgA4ADkANAAtADMANQA2ADEALQA0AEMAMAA0
AC0AOQBCADIAMAAtADMAOQBCAEMARAAzADQANwAzADUAMQAwAH0wYwYJKwYBBAGCNxEBMVYeVABN
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
CxwCAgfQ
\ No newline at end of file
MIIKRgIBAzCCCgIGCSqGSIb3DQEHAaCCCfMEggnvMIIJ6zCCBgwGCSqGSIb3DQEHAaCCBf0EggX5
MIIF9TCCBfEGCyqGSIb3DQEMCgECoIIE/jCCBPowHAYKKoZIhvcNAQwBAzAOBAjnValj/qxAxQIC
B9AEggTYZZfhDgMBhoV+P+A4KJf/gnrbkgp4ZDWzXl8gOttjRXofhACETGpX5IHsE7UnXbRc4T70
ByZGlcGTDuT9VgiFnFN4VTs1tRIz2W4cs4EmogLX2sX4KP3U6lZiXugEJ36wPSSN8A1prsZsrTid
ff6bsi+sIO+OI6X8Y0degOU44huDhZe7Fmjed13aW6jLhr8SKRSoufDR/sjVnJK476qkYZc4/Dlr
sMmYftHdgxjfm1KdOVycWm+1ItRzniZemiYYYB+vkUBYPO/rMKCp717SWOQhs7SNQcG4KjdLs8vv
skiRPgRzfbt6vaEC4yKy27FDFqzx4unLRzGJ+ekbLzzZAu4TMS7j5g7Ngo0d7GS4+ZGHamOvDuXQ
x8/Itv1/0qzGMwLXyu1x4K1hTgFKD5NQoEk89qZMDVXi4ylGTWXE0x5azKCXP06LxP9/Pit/ufG9
CwUAIeDrZ4A6F0hyT4p7WMgaUqry54/YrjQBd632I7FacKnYojgcW+xQeRufxiYJ5x/6QIZ4dDmj
4rX7W4/0UOVlyFltmmoSq1HJRMaw95iUE+ft3q6vK/bvy6FZSd1CAbr0tut6HBmcW6BOdXFGmXGS
JnbwaK1IFU4c6KOVAwP16poGhOxaJqRaa4zMBaPcYg84rQ3aI00UHGKQvRb6UWYVGVayc13WYcGQ
Mm8GEjlmh4URRXaRL1qTKCjjYjG1mvTG+TOYUwGZUOGD+6fkGFKMkNXxLmaDyyT0cSoBeMl4whQx
obafWD8sMxCJb/E0eil/fbu114rmFfrrqP2evPEIuQoacNr+OUnfr+T6vbBpT79emxkgsKuG7eEt
4okYQTCp6aEpk7ZKppiZVOBvrN1lZfOH9s9qqtbx5Y+9uj5aGADHkhr6c4pYoIaa1BuUKgBiglE7
QdRFm98TFlC328BbUvBFXEAnshXeYHRPgevcapDBIqFz8ZBnJNMgY3KZ+mug0wg09vsFE8TkH86p
WwvgUzLlZDNt64OHh9E5Ql1MtnMlp0nunpLQyYcmCn8AJIcxSnaicjSlp+Z04hSWXAesbWie41H2
OA1CqsQdc9EePijdjX3QxDK9mBNhXY2ngWrvwkmlYatHdSFzXB4uzCsT5NKK0nKI/AmUN5vVVv01
CFCPHgyBBxF1wom2mA/QFv+7WX6c5Oj0WgOLe/3VySQ9xPoI53Cc1Jl7uOm6kXIMzMxCOiDDcScJ
i0LwyDtmVZlOqhiGCiguoptQnTT3zzp2uaDLVvyMy7bZP8T7G7ka7CSpWQNr0USldgpT8HOXJWgI
4il9iQlaajYO9ntwtYAlMTjsYy4NNoW0uYajdnjT1wO+0w1A0SKP7UrRnyaonf/G4z/VeNSh+yEn
7RkFErPhhvHQRFMG3Gkgw4ER+zo7Q7x2m5R7FOeCp4x0/wiI0e/SzyYxBqXSQfEWxQjb7IJVfd6a
gFT/heXJDvEGbLzUR9jhDptGggE2NlFyA4G+ol6XRRZKlBA0XUYJPHY+fyvcHikiJeMM/YRkrhCE
LOqfCNOafSSSLcQNzJjevq/cDpgybQZV+BrN3yPtQlZgZAy9jOZlULVQSak7K2dF355mEtPNQUIH
4U+GdMXitRVknz/dv7SF8tVzR/GYJEX51i0Qou+rzOFhmurxHyomRHnB5WTG8D/SbzGB3zATBgkq
hkiG9w0BCRUxBgQEAQAAADBbBgkqhkiG9w0BCRQxTh5MAHsAMwA4ADkARgA0AEMAMQA3AC0AMABC
ADQARgAtADQAOABFADYALQA5ADAARAA1AC0AMwAwAEMAMgA5AEYANAA4ADUAMAAwADUAfTBrBgkr
BgEEAYI3EQExXh5cAE0AaQBjAHIAbwBzAG8AZgB0ACAARQBuAGgAYQBuAGMAZQBkACAAQwByAHkA
cAB0AG8AZwByAGEAcABoAGkAYwAgAFAAcgBvAHYAaQBkAGUAcgAgAHYAMQAuADAwggPXBgkqhkiG
9w0BBwagggPIMIIDxAIBADCCA70GCSqGSIb3DQEHATAcBgoqhkiG9w0BDAEGMA4ECBHBi0p8N2Vx
AgIH0ICCA5A7JaVFBqZTQbSEJhtjKPmwteV230BLhKvw4qfM9vxcBNj8kBZGDJaWYZK5BatuKMu8
xd60lqzoKgUOHrN9etjMCe2h/z/RyyeVpBuEILJUQaj+SuSTSUUTpGKokKVnSQvCAD1UgJGFt/ac
elBlaIw9pRDnDZgFhzZ3AWO09vBz+GcowUlA77W173d6fHf26khicTalsdXbimnMt48/mdtKH9ep
pjgxJwhshPhgKKt5M87nEyaaGSEFfsrPgcIj79poWwj8WE6sAg+bVw3OfX9rvbpP2vWF/e6Vc2x/
QJPChQZs8LqJAUy2lI55gZgv+hTKkkBXoHG0c4248KdyvBT5DCiFufaj/dp1Eja02Qxo+BqZLJS7
EYE1YxB7Ty9CUxWvAt6aRLQ0ZLbrK28mwgCePctczxQa+tcCMCfL/JJvcZtiu5CaHObLzF7Ymbh7
/Q6u1+5u3/Im7aBdY5AksTz2Zn4JjzSAXKTdEgcadjGsxTd/lj4dUOreXN9rUgNyQzjQHpljnhxF
6ZAAbNC8FJwFIAm+ipmV7keCWT8s6MQeoQ5qr5RDhVhGUVM2CWoKRjhbKzWbnKetfmjAUnvn0+ZO
vSKmOShMG8QgyG8trDIH734jTG6u6yoe3Afjw52DtfOo1OVi0ZDsj5GviFDZYQX5Z/86N4Tx7Tol
e/Wz5918nziNoU3+07ZySsRRESuLj6p11sUmFFPj64W/CZ1YQB/H4+R+94uYyWiovHudC/69KnBg
Rfj0XpdSg0l+HKgTez7OuJrEu9/+5QF0M1unJSGxpdojVkACf+bxu2+4czzh80sL/3Yzcenvg/nt
AQGm+sX/K2oF2E0h/22RD4U2txDxORVGcF+wfNUHkl2m+rBkgsUMhECeWGwoI/y5ZBHWWcQP2ByZ
78fa8a9L1RWGvn51yLOe/cPZ0pmYBDZ9aiP1KucGBNMTzHefZiRXscXjB28u7/YxUb7fFPY3RjjO
p2Rm17rrFrvR9/oChO9ZVJrm8p2lQfKufz/t1USs8+gZPS+kLfE3wxZefo1nFmbkTWQT6hW+z4zh
0QoBNwGJVFYxPuGvyOmQ4ksEHe7G8fc7ihkiZ+A9fJROqOjVA/ThnWIuiLKbDFlW1rQbdlihQy7Z
oKbzbA9eOMUCaXMEti5CG/97pOCiElARHDactfiy0J7ykjCGLCkw1/3j5Qdr3feN4GWqOBTfn0cK
eq8RT0pBkT0wOzAfMAcGBSsOAwIaBBRW5H12kY01oB2hnm0lkT0yUbjP4wQUF7tfAlojMdSZIdKB
/FfU1hlVl8YCAgfQ
\ No newline at end of file
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
IsNed5PjTXd1/rQmWIcCDOy9lgICBAA=
\ No newline at end of file
MIIJzgIBAzCCCYgGCSqGSIb3DQEHAaCCCXkEggl1MIIJcTCCA/MGCSqGSIb3DQEHBqCCA+QwggPg
AgEAMIID2QYJKoZIhvcNAQcBMCgGCiqGSIb3DQEMAQYwGgQUAz+z63B0n0UriCxfGp3W/qYiuN4C
AgQAgIIDoH9ARpsBx2g6u9l+FNrXZa7dQZ+z2f2XhmM60HBm4m1eeZtQr2E/Yl4Yck33qT4L6cIp
6cR3cS+pHe1/jlgfP7QaWSDODj6QVU2PUZfL95/9fScYvp2DOWH9PD0qVBJVIZAh7zJb2/CXUXxF
NSSFbTMo3jaTqYHg3I4BIo5CgyN/Nj2bwrcLMBwhk6Le328bT2jApkIMUSfkWb8xTBom3PW72pLc
h7Vwgf5hrxSF29BIwGtzEnbVJMw45oDlsieQM+SICmXUh/uMNn94Z+17S0DDHZNwg//dRevbY7iF
IPSiNphFeiD3qEX+4GvZO8g3esbxUmbN7yssJ3WftOlfcAgMZClEYjXbtEnGtvFEOTIWCUuCIs5B
UgOO0ySzoXdwZx7tHfYqmkSK6APvzvUUro0ynwz4TlEL4M/+O4e/JaOKuv/O7g43wVZohS0GDu9a
S98ri5aEKm3+evPbnxclbsrGzoym7tOWCaq2RIKfbJB/ARJuIWUGWVr5LHw/ovANgGIFs0+Sla0g
sJMR06OoS+PFvvPBUoW2bEUXOf9ugposQuHIDuw8V3EP1yGuOYGTM7IZSf5cbKZ/2NAQe/oc8hkI
VqcJeYFCRwcFqacPJDa2IUaX0ZhR6WwGorzXPn3i9YDgTo55+WqOjujGlVnRW2aP6E3JbwJh3q0x
hHIlvg6rVa6MgqKFl8/mNEQPvUMiNojmo1GT1xQ6KhsIzJQee7ZyLFBg6pJU7aN5hxh6J32Y3SBd
VAauKllwSZ05jR0U+WYFF3NfTR3UgqSvtyKfLEWo4GFhEKpp5NwCTtTPHBs7Qt/CTr3QyNZ8ciRW
LpmXRIDe8UGschK30QNr1wh9DrK4LU0Rsl7oYhFUDEGE3dxIqgUifb0X5IpX4kjpK7NODmO3iUr3
IT77Mukk8LJk2pfCyerPh9NdXS4fN1r1coTWcNwXt+FF5pzC8Jw0goV8TWCk4izmpmroi64UKfam
h/4/dt+fZLRpP8joBMxXmyvPRECtTF2Osz6WxOenRnqT8CdqxOITwsuypk1ZZqIc1zbQc53ddmOQ
IWZp3EopTM6NUem4J70yiZS24Eve4UsyrkzINvKTiuGGWbiAAgJlegi7YDmkbH8nsNvDtMRgvst3
uYXazCGii7CJSNFrOSKebi1LPxISJmYWzuqiS3b/VO5VkRp0w21n+Yh4gOTsn8tTkZoWV9kOuZzt
odHgt47FwoprVX0bwJ5jk29fWsw2z9UwggV2BgkqhkiG9w0BBwGgggVnBIIFYzCCBV8wggVbBgsq
hkiG9w0BDAoBAqCCBPowggT2MCgGCiqGSIb3DQEMAQMwGgQUEyyUrMkExJyP55CaARyqFL7stccC
AgQABIIEyFyuDycIN0LU8HtRhDr3dbX0+fkXmgPAHX4CyJTS8YzV5U4r6knRH2EFx/EbJq2/ql82
OfXFg2BBvnlDdf9H1XVq6e3yr6exxRyOc6xSQiKWU1RpF8AsYIpiYfYd3MErRK/Qp2OnPAU++5S3
wog/Qda0a5tRSrxs/1M6Ts5aBDaO4YiQIu/J9VaU+FMWx8F0UmeEfm21DNUgrTQxzdB6m0dHGcXE
XHiba7VT4t4f0qPuBBji8tHziipvCer4m6KQJxPN0UhheJvhsqO9AQHSrUXeKkHBzhqojP5gPc5f
HJMGhi5zIVUgBtA9PRB/IEUZ02TFOLoMu5pTRGrVSoSjvJTDhjrutV9w1v6ILyJiqAUA1Acza9Lg
stVAA/sALJhHDXn6HcQqyT9vIG+GhswOvRWnxnDHHxTbK9dy2RyhpBgGkBA07pqmB1pOyPvoJHfj
Ix9fi2g7K1bdgERYehSfWoDPt9joYnWfg2TtpYduDD5hnJA3bqb3+pARFKe8G/4Y7WCILQKCFvQB
p48a5sq+Dd5dWlyhUNr5fxZPsVpaMa5nA/iFog/ILocBsGmCkOA/fXeO5uzTX/ze+BohE+d2Y5rp
//LZyvfxSIM90odzUNRvKowbRHKx1tKReOKXLE9UkwFCraMOXqX4DhFbuiXvLYjqs45mbfZodQ96
J+lJvPvS0w1xl7okRhvD8E5sUATgIeIAapU7sSZLfsYXoSFndNBgUfZ+7bm574sM6y2tC3Fd2Ytt
jw3/avfk3V0L4ddAfpYMCK1vJSgNiGdAwPOe8ieihn/kg3pY6glQXkXKHZHVIwg4MlGHfw7KBqry
W2rnh9TwdxUC8Z8CzEaT/8fKxYkF9ZJa8bCP5l4vyKOYdy+m4WZMb5hwZ9GxHodcuDzagRvQ5RZd
VwC1SQwXO6ad34eSizBZmHVeYJCA6JrixBRbtXfA6frk8nmKvqSO8WxM0ma+2MBS40wLvWWAPs7y
P2xKKOJJpUuNlDeRTtSbogUnB40KO5IYLqzad9LlU9V5dfluZ3GgI4fWBNZa2ykK2Q77jVTda2a5
kgiK+/OQYZuLo3gO+3Y5zAxEZ91gqibI9yRv4/XiyxGSiOBY+EUmJLTw8R9j9BJLrMckf22BUYNm
wpcHZVILWmBi7L45loni7DGvsxzibLNMn1NHL6zWZHYHEayI/HqfrRRhadPnizvTwkZOF9XYOrlv
BdVcQuhyigPbOlpplLzJQ5DPmnet216FSVk4nn82CvWZr02RlRP426elooMhABVnKgFhGWH1VtuW
ewja9X+/wznDyP0yzmPldwKn7w1UXkCwI3AdBhzDlkwOAMU1QqunDXnchDvwg0BGR8M23obINGiH
6JwQ/iWl6nUG7EVSqJcZvWnz1jQO0tCgr30YBl31jZ584bW743vANF3tFFo4bacdJlOqGkb7rNdj
KxoGE2XLyVd2rzjdLrcnr4ViwrJRZl+1XB62DVH7cr5+86lF1V7/2UJR3XzjdLV5SfSoBzexLxiA
1gPov8RE8UnVbqhZbqeaR+tQkoeuZy8vyP545+brD1BkV+b2QGJaGMP2DDJC74CpVITyiVm4C08u
2ay6G+tblKuBOVM70xBmbiwq8inFiBd3vl7aObMyj1AgPjFOMCcGCSqGSIb3DQEJFDEaHhgAcABr
AGMAcwAxADIAdABlAHMAdABjAGEwIwYJKoZIhvcNAQkVMRYEFAxeTobq6DehjSbiylxISkcqQqH6
MD0wITAJBgUrDgMCGgUABBR8ZwGkxtDxkXoUy1JndD2dkf+Q5wQUlDj7Q1RzRmooVNZ+0cEv+M/q
I6oCAgQA
\ No newline at end of file
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
\ No newline at end of file
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
0AQQxrOtDDKIzjpW4+rATcwNPAAA
\ No newline at end of file
MIACAQMwgAYJKoZIhvcNAQcBoIAkgAQBMAQBgAQBMAQBgAQBBgQBCQQJKoZIhvcNAQcBBAGgBAGA
BAEkBAGABAEEBAEBBAEwBAEEBAEDBAOCBVoEAQQEAQEEATAEAQQEAQMEA4IFVgQBBAQBAQQBBgQB
BAQBAQQBCwQBBAQBCwQLKoZIhvcNAQwKAQIEAQQEAQEEAaAEAQQEAQMEA4IE9QQBBAQBAQQBMAQB
BAQBAwQDggTxBAEEBAEBBAEwBAEEBAEBBAEjBAEEBAEBBAEGBAEEBAEBBAEKBAEEBAEKBAoqhkiG
9w0BDAEDBAEEBAEXBBcwFQQQ6T2lKjQK0wo/cjrjNyb0CwIBAQQBBAQBAQQBBAQBBAQBAwQDggTI
BAEEBAOCBMgEggTIiZ+WxZhkj+CjQo4+eTTGf/tSoJMtp85VFwcFUy2+Bz2+WIOf7abQ3yVRmkSc
MUeb/1vQDS+NXMimZDLoIIN9M1ql3Mq5Qtr++kURQeZAVh9q2ZJNDAmw2rvAWGJxdbqPrMA2WgQc
oGZVLiN2eIdWXxJkjLdNzjMWtpsh+6Yw52lwq8ZEIKgu7v23my8+HCemGCL87wf7qU0nQjq+I5jf
QZujYJL5iDNvosZLFLzBuOZKG7voDY2gJtbl+yiOUMOPBQYdDE8qiF+eHqu8WZ/JwlA/3EtnuIDz
i/9f9mIHMINC5vpvPXuo9TE0nF9PvxtL8mGmRcxG0vVro0sPEzu1f13OsN4qwDUVWJ6Xr154zkU8
9wMi8SAY+gbETvbZZQpxgHWsaEjG9r7fCjiLE/x+5VZCaPeNK8zB+cb0Z2wPGPlojV6Ggcqt/7dI
pZtRMyeSNpSCTncgZlBwh6JLKRCLFSEjZhGzKIsCjT9TpU+IzoD9Niue1cWo2ciZIuTMaSwnqYhO
KzJ/Wivqx9tjhEVI9cCuLf/JPpmFvsmrdMurjD6s4WQPMc7prK8aMJy5W3vcN32wuEQyR6KcqhDn
QxsX7KPavuIi3dTe8I4pNaMJlZ3Ubif4qvt5qJe6I3rxuie2Q1ux7clXk99W5ZvU06tCYTv/3GP4
M3TjupaWAtmgWZRLsjbTW2NBnxVP40o0AthzYCElyrqhlQPTRU/p1y3RPXyVyssZXwCtvQJe4cxb
V38+kjVSrYL+WGjVkuCQAs4KSdmsH97ShclgAAsrqhDi4+DNhaVIuoFMhkwp4DngxiWMNHWYMjAC
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
AoiyQ6u3imI281gAAA==
\ No newline at end of file
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
\ No newline at end of file
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
HwQQ6Vxq63yfCeamza5PZKytWQAA
\ No newline at end of file
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
LK2fdlxrCGzUHTgAAA==
\ No newline at end of file
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
\ No newline at end of file
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
\ No newline at end of file
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
VL+NWQOgtME=
\ No newline at end of file
/*
* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Random;
import static java.lang.System.out;
/**
* @test
* @bug 8050370
* @summary MessageDigest tests with DigestIOStream
* @author Kevin Liu
*/
enum ReadModel {
READ, BUFFER_READ, MIX_READ
}
public class TestDigestIOStream {
private static final int[] DATA_LEN_ARRAY = {
1, 50, 2500, 125000, 6250000
};
private static final String[] ALGORITHM_ARRAY = {
"MD2", "MD5", "SHA1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"
};
private static byte[] data;
private static MessageDigest md = null;
public static void main(String argv[]) throws Exception {
TestDigestIOStream test = new TestDigestIOStream();
test.run();
}
public void run() throws Exception {
for (String algorithm: ALGORITHM_ARRAY) {
md = MessageDigest.getInstance(algorithm);
for (int length: DATA_LEN_ARRAY) {
Random rdm = new Random();
data = new byte[length];
rdm.nextBytes(data);
if (!testMDChange(algorithm, length)) {
throw new RuntimeException("testMDChange failed at:"
+ algorithm + "/" + length);
}
if (!testMDShare(algorithm, length)) {
throw new RuntimeException("testMDShare failed at:"
+ algorithm + "/" + length);
}
for (ReadModel readModel: ReadModel.values()) {
// test Digest function when digest switch on
if (!testDigestOnOff(algorithm, readModel, true, length)) {
throw new RuntimeException("testDigestOn failed at:"
+ algorithm + "/" + length + "/" + readModel);
}
// test Digest function when digest switch off
if (!testDigestOnOff(algorithm, readModel, false, length)) {
throw new RuntimeException("testDigestOff failed at:"
+ algorithm + "/" + length + "/" + readModel);
}
}
}
}
int testNumber = ALGORITHM_ARRAY.length * ReadModel.values().length
* DATA_LEN_ARRAY.length * 2 + ALGORITHM_ARRAY.length
* DATA_LEN_ARRAY.length * 2;
out.println("All " + testNumber + " Tests Passed");
}
/**
* Test DigestInputStream and DigestOutputStream digest function when digest
* set on and off
*
* @param algo
* Message Digest algorithm
* @param readModel
* which read method used(READ, BUFFER_READ, MIX_READ)
* @param on
* digest switch(on and off)
* @param dataLength
* plain test data length.
* @exception Exception
* throw unexpected exception
*/
public boolean testDigestOnOff(String algo, ReadModel readModel,
boolean on, int dataLength) throws Exception {
// Generate the DigestInputStream/DigestOutputStream object
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
DigestInputStream dis = new DigestInputStream(bais,
MessageDigest.getInstance(algo));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DigestOutputStream dos = new DigestOutputStream(baos,
MessageDigest.getInstance(algo));
ByteArrayOutputStream baOut = new ByteArrayOutputStream();) {
// Perform the update using all available/possible update methods
int k = 0;
byte[] buffer = new byte[5];
boolean enDigest = true;
// Make sure the digest function is on (default)
dis.on(enDigest);
dos.on(enDigest);
switch (readModel) {
case READ: // use only read()
while ((k = dis.read()) != -1) {
if (on) {
dos.write(k);
} else {
dos.write(k);
if (enDigest) {
baOut.write(k);
}
enDigest = !enDigest;
dos.on(enDigest);
dis.on(enDigest);
}
}
break;
case BUFFER_READ: // use only read(byte[], int, int)
while ((k = dis.read(buffer, 0, buffer.length)) != -1) {
if (on) {
dos.write(buffer, 0, k);
} else {
dos.write(buffer, 0, k);
if (enDigest) {
baOut.write(buffer, 0, k);
}
enDigest = !enDigest;
dis.on(enDigest);
dos.on(enDigest);
}
}
break;
case MIX_READ: // use both read() and read(byte[], int, int)
while ((k = dis.read()) != -1) {
if (on) {
dos.write(k);
if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
dos.write(buffer, 0, k);
}
} else {
dos.write(k);
if (enDigest) {
baOut.write(k);
}
enDigest = !enDigest;
dis.on(enDigest);
dos.on(enDigest);
if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
dos.write(buffer, 0, k);
if (enDigest) {
baOut.write(buffer, 0, k);
}
enDigest = !enDigest;
dis.on(enDigest);
dos.on(enDigest);
}
}
}
break;
default:
out.println("ERROR: Invalid read/write combination choice!");
return false;
}
// Get the output and the "correct" digest values
byte[] output1 = dis.getMessageDigest().digest();
byte[] output2 = dos.getMessageDigest().digest();
byte[] standard;
if (on) {
standard = md.digest(data);
} else {
byte[] dataDigested = baOut.toByteArray();
standard = md.digest(dataDigested);
}
// Compare the output byte array value to the input data
if (!MessageDigest.isEqual(data, baos.toByteArray())) {
out.println("ERROR of " + readModel
+ ": output and input data unexpectedly changed");
return false;
}
// Compare generated digest values
if (!MessageDigest.isEqual(output1, standard)
|| !MessageDigest.isEqual(output2, standard)) {
out.println("ERROR" + readModel
+ ": generated digest data unexpectedly changed");
return false;
}
return true;
} catch (Exception ex) {
out.println("testDigestOnOff failed at:" + algo + "/" + readModel
+ "/" + dataLength + " with unexpected exception");
throw ex;
}
}
/**
* Test DigestInputStream and DigestOutputStream digest function when Swap
* the message digest engines between DigestIn/OutputStream
*
* @param algo
* Message Digest algorithm
* @param dataLength
* plain test data length.
* @exception Exception
* throw unexpected exception
*/
public boolean testMDChange(String algo, int dataLength) throws Exception {
// Generate the DigestInputStream/DigestOutputStream object
MessageDigest mdIn = MessageDigest.getInstance(algo);
MessageDigest mdOut = MessageDigest.getInstance(algo);
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
DigestInputStream dis = new DigestInputStream(bais, mdIn);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DigestOutputStream dos = new DigestOutputStream(baos, mdOut);) {
// Perform the update using all available/possible update methods
int k = 0;
byte[] buffer = new byte[10];
// use both read() and read(byte[], int, int)
while ((k = dis.read()) != -1) {
dos.write(k);
if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
dos.write(buffer, 0, k);
}
// Swap the message digest engines between
// DigestIn/OutputStream objects
dis.setMessageDigest(mdOut);
dos.setMessageDigest(mdIn);
mdIn = dis.getMessageDigest();
mdOut = dos.getMessageDigest();
}
// Get the output and the "correct" digest values
byte[] output1 = mdIn.digest();
byte[] output2 = mdOut.digest();
byte[] standard = md.digest(data);
// Compare generated digest values
return MessageDigest.isEqual(output1, standard)
&& MessageDigest.isEqual(output2, standard);
} catch (Exception ex) {
out.println("testMDChange failed at:" + algo + "/" + dataLength
+ " with unexpected exception");
throw ex;
}
}
/**
* Test DigestInputStream and DigestOutputStream digest function when use
* same message digest object.
*
* @param algo
* Message Digest algorithm
* @param dataLength
* plain test data length.
* @exception Exception
* throw unexpected exception
*/
public boolean testMDShare(String algo, int dataLength) throws Exception {
MessageDigest mdCommon = MessageDigest.getInstance(algo);
// Generate the DigestInputStream/DigestOutputStream object
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
DigestInputStream dis = new DigestInputStream(bais, mdCommon);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DigestOutputStream dos = new DigestOutputStream(baos, mdCommon);) {
// Perform the update using all available/possible update methods
int k = 0;
byte[] buffer = new byte[10];
// use both read() and read(byte[], int, int)
while (k < data.length) {
int len = dis.read(buffer, 0, buffer.length);
if (len != -1) {
k += len;
if (k < data.length) {
dos.write(data[k]);
k++;
dis.skip(1);
}
}
}
// Get the output and the "correct" digest values
byte[] output = mdCommon.digest();
byte[] standard = md.digest(data);
// Compare generated digest values
return MessageDigest.isEqual(output, standard);
} catch (Exception ex) {
out.println("TestMDShare failed at:" + algo + "/" + dataLength
+ " with unexpected exception");
throw ex;
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import jdk.testlibrary.ProcessTools;
/**
* @test
* @bug 8048360
* @summary test policy entry with signedBy alias
* @library /lib/testlibrary
* @run main/othervm SignedJarTest
*/
public class SignedJarTest {
private static final String FS = File.separator;
private static final String JAVA_HOME = System.getProperty("test.jdk");
private static final String TESTCLASSES = System.getProperty("test.classes", "");
private static final String TESTSRC = System.getProperty("test.src", "");
private static final String KEYTOOL = JAVA_HOME + FS + "bin" + FS + "keytool";
private static final String JAR = JAVA_HOME + FS + "bin" + FS + "jar";
private static final String JARSIGNER = JAVA_HOME + FS + "bin" + FS + "jarsigner";
private static final String PASSWORD = "password";
private static final String PWDFILE = "keypass";
private static final String POLICY1 = "SignedJarTest_1.policy";
private static final String POLICY2 = "SignedJarTest_2.policy";
private static final String KEYSTORE1 = "both.jks";
private static final String KEYSTORE2 = "first.jks";
public static void main(String args[]) throws Throwable {
//copy PrivilegeTest.class, policy files and keystore password file into current direcotry
Files.copy(Paths.get(TESTCLASSES, "PrivilegeTest.class"), Paths.get("PrivilegeTest.class"));
Files.copy(Paths.get(TESTSRC, POLICY1), Paths.get(POLICY1));
Files.copy(Paths.get(TESTSRC, POLICY2), Paths.get(POLICY2));
Files.copy(Paths.get(TESTSRC, PWDFILE), Paths.get(PWDFILE));
//create Jar file
ProcessTools.executeCommand(JAR, "-cvf", "test.jar", "PrivilegeTest.class");
//Creating first key , keystore both.jks
ProcessTools.executeCommand(KEYTOOL,
"-genkey",
"-alias", "first",
"-keystore", KEYSTORE1,
"-keypass", PASSWORD,
"-dname", "cn=First",
"-storepass", PASSWORD
).shouldHaveExitValue(0);
//Creating Second key, keystore both.jks
ProcessTools.executeCommand(KEYTOOL,
"-genkey",
// "-storetype","JKS",
"-alias", "second",
"-keystore", KEYSTORE1,
"-keypass", PASSWORD,
"-dname", "cn=Second",
"-storepass", PASSWORD
).shouldHaveExitValue(0);
//copy both.jks to first.jks, remove second Keypair from first.jks
Files.copy(Paths.get(KEYSTORE1), Paths.get(KEYSTORE2));
ProcessTools.executeCommand(KEYTOOL,
"-delete",
"-keystore", KEYSTORE2,
"-alias", "second",
"-storepass", PASSWORD
).shouldHaveExitValue(0);
//sign jar with first key, first.jar is only signed by first signer
ProcessTools.executeCommand(JARSIGNER,
"-keystore", KEYSTORE1,
"-storepass", PASSWORD,
"-keypass", PASSWORD,
"-signedjar", "first.jar", "test.jar",
"first").shouldHaveExitValue(0);
//sign jar with second key, both.jar is signed by first and second signer
ProcessTools.executeCommand(JARSIGNER,
"-keystore", KEYSTORE1,
"-storepass", PASSWORD,
"-keypass", PASSWORD,
"-signedjar", "both.jar", "first.jar",
"second").shouldHaveExitValue(0);
//test case 1
//setIO permission granted to code that was signed by first signer
//setFactory permission granted to code that was signed by second signer
//Keystore that contains both first and second keypairs
//code was singed by first signer
//Expect AccessControlException for setFactory permission
System.out.println("Test Case 1");
//copy policy file into current directory
String[] cmd = constructCMD("first.jar", POLICY1, "false", "true");
ProcessTools.executeTestJvm(cmd).shouldHaveExitValue(0);
//test case 2, test with both.jar
//setIO permission granted to code that was signed by first signer
//setFactory permission granted to code that was signed by second signer
//Keystore that contains both first and second keypairs
//code was singed by first signer and second signer
//Expect no AccessControlException
System.out.println("Test Case 2");
cmd = constructCMD("both.jar", POLICY1, "false", "false");
ProcessTools.executeTestJvm(cmd).shouldHaveExitValue(0);
//test case 3
//setIO permission granted to code that was signed by first signer
//setFactory permission granted to code that was signed by second signer
//Keystore that contains only first keypairs
//code was singed by first signer and second signer
//Expect AccessControlException for setFactory permission
System.out.println("Test Case 3");
cmd = constructCMD("both.jar", POLICY2, "false", "true");
ProcessTools.executeTestJvm(cmd).shouldHaveExitValue(0);
}
private static String[] constructCMD(String classpath, String policy, String arg1, String arg2) {
String[] cmd = {
"-classpath", classpath,
"-Djava.security.manager",
"-Djava.security.policy=" + policy,
"PrivilegeTest",
arg1, arg2};
return cmd;
}
}
class PrivilegeTest {
private static final Permission PERM1 = new RuntimePermission("setIO");
private static final Permission PERM2 = new RuntimePermission("setFactory");
public static void main(String args[]) {
boolean expectException1 = Boolean.parseBoolean(args[0]);
boolean expectException2 = Boolean.parseBoolean(args[1]);
test(PERM1, expectException1);
test(PERM2, expectException2);
}
public static void test(Permission perm, boolean expectException) {
boolean getException = (Boolean) AccessController.doPrivileged((PrivilegedAction) () -> {
try {
AccessController.checkPermission(perm);
return (Boolean) false;
} catch (AccessControlException ex) {
return (Boolean) true;
}
});
if (expectException ^ getException) {
String message = "Check Permission :" + perm + "\n ExpectException = "
+ expectException + "\n getException = " + getException;
throw new RuntimeException(message);
}
}
}
keystore "file:both.jks";
keystorePasswordURL "file:keypass";
grant signedBy "first" {
permission java.lang.RuntimePermission "setIO";
};
grant signedBy "second" {
permission java.lang.RuntimePermission "setFactory";
};
keystore "file:first.jks";
keystorePasswordURL "file:keypass";
grant signedBy "first" {
permission java.lang.RuntimePermission "setIO";
};
grant signedBy "second" {
permission java.lang.RuntimePermission "setFactory";
};
/*
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Random;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.interfaces.PBEKey;
import javax.crypto.spec.PBEKeySpec;
import javax.security.auth.DestroyFailedException;
import static java.lang.System.out;
/*
* @test
* @bug 8048820
* @summary The test verifies if the SecretKeyFactory.translateKey() method
* works as expected for the PBKDF2 algorithms.
*/
public class PBKDF2TranslateTest {
private static final String PASS_PHRASE = "some hidden string";
private static final int ITERATION_COUNT = 1000;
private static final int KEY_SIZE = 128;
private static final String[] TEST_ALGOS = { "PBKDF2WithHmacSHA1",
"PBKDF2WithHmacSHA224", "PBKDF2WithHmacSHA256",
"PBKDF2WithHmacSHA384", "PBKDF2WithHmacSHA512" };
private final String algoForTest;
public static void main(String[] args) throws Exception {
for (String algo : TEST_ALGOS) {
PBKDF2TranslateTest theTest = new PBKDF2TranslateTest(algo);
byte[] salt = new byte[8];
new Random().nextBytes(salt);
theTest.testMyOwnSecretKey(salt);
theTest.generateAndTranslateKey(salt);
theTest.translateSpoiledKey(salt);
}
}
public PBKDF2TranslateTest(String algo) {
algoForTest = algo;
}
/**
* The test case scenario implemented in the method: - derive PBKDF2 key
* using the given algorithm; - translate the key - check if the translated
* and original keys have the same key value.
*
*/
public void generateAndTranslateKey(byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException,
InvalidKeyException {
// derive PBKDF2 key
SecretKey key1 = getSecretKeyForPBKDF2(algoForTest, salt);
// translate key
SecretKeyFactory skf = SecretKeyFactory.getInstance(algoForTest);
SecretKey key2 = skf.translateKey(key1);
// Check if it still the same after translation
if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) {
System.out.println("Key1=" + new String(key1.getEncoded())
+ " key2=" + new String(key2.getEncoded()) + " salt="
+ new String(salt));
throw new RuntimeException(
"generateAndTranslateKey test case failed: the key1 and"
+ " key2 values in its primary encoding format are"
+ " not the same for " + algoForTest
+ " algorithm.");
}
}
/**
* The test case scenario implemented in the method: - derive Key1 for the
* given PBKDF2 algorithm - create my own secret Key2 as an instance of a
* class implements PBEKey - translate Key2 - check if the key value of the
* translated key and Key1 are the same.
*/
private void testMyOwnSecretKey(byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException,
InvalidKeyException {
SecretKey key1 = getSecretKeyForPBKDF2(algoForTest, salt);
SecretKey key2 = getMyOwnSecretKey(salt);
// Is it actually the same?
if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) {
throw new RuntimeException(
"We shouldn't be here. The key1 and key2 values in its"
+ " primary encoding format have to be the same!");
}
// translate key
SecretKeyFactory skf = SecretKeyFactory.getInstance(algoForTest);
SecretKey key3 = skf.translateKey(key2);
// Check if it still the same after translation
if (!Arrays.equals(key1.getEncoded(), key3.getEncoded())) {
System.out.println("Key1=" + new String(key1.getEncoded())
+ " key3=" + new String(key3.getEncoded()) + " salt="
+ new String(salt));
throw new RuntimeException(
"testMyOwnSecretKey test case failed: the key1 and key3"
+ " values in its primary encoding format are not"
+ " the same for " + algoForTest + " algorithm.");
}
}
/**
* The test case scenario implemented in the method: - create my own secret
* Key2 as an instance of a class implements PBEKey - spoil the key (set
* iteration count to 0, for example) - try to translate key -
* InvalidKeyException is expected.
*/
public void translateSpoiledKey(byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// derive the key
SecretKey key1 = getMyOwnSecretKey(salt);
// spoil the key
((MyPBKDF2SecretKey) key1).spoil();
// translate key
SecretKeyFactory skf = SecretKeyFactory.getInstance(algoForTest);
try {
skf.translateKey(key1);
throw new RuntimeException(
"translateSpoiledKey test case failed, should throw"
+ " InvalidKeyException when spoil the key");
} catch (InvalidKeyException ike) {
out.println("Expected exception when spoil the key");
}
}
/**
* Generate a PBKDF2 secret key using given algorithm.
*/
private SecretKey getSecretKeyForPBKDF2(String algoDeriveKey, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory skf = SecretKeyFactory.getInstance(algoDeriveKey);
PBEKeySpec spec = new PBEKeySpec(PASS_PHRASE.toCharArray(), salt,
ITERATION_COUNT, KEY_SIZE);
return skf.generateSecret(spec);
}
/**
* Generate a secrete key as an instance of a class implements PBEKey.
*/
private SecretKey getMyOwnSecretKey(byte[] salt)
throws InvalidKeySpecException, NoSuchAlgorithmException {
return new MyPBKDF2SecretKey(PASS_PHRASE, algoForTest, salt,
ITERATION_COUNT, KEY_SIZE);
}
/**
* An utility class to check the SecretKeyFactory.translateKey() method.
*/
class MyPBKDF2SecretKey implements PBEKey {
private final byte[] key;
private final byte[] salt;
private final String algorithm;
private final int keyLength;
private final String pass;
private int itereationCount;
/**
* The key is generating by SecretKeyFactory and its value just copying
* in the key field of MySecretKey class. So, this is real key derived
* using the given algo.
*/
public MyPBKDF2SecretKey(String passPhrase, String algo, byte[] salt1,
int iterationCount, int keySize)
throws InvalidKeySpecException, NoSuchAlgorithmException {
algorithm = algo;
salt = salt1;
itereationCount = iterationCount;
pass = passPhrase;
PBEKeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt,
iterationCount, keySize);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algo);
SecretKey realKey = keyFactory.generateSecret(spec);
keyLength = realKey.getEncoded().length;
key = new byte[keyLength];
System.arraycopy(realKey.getEncoded(), 0, key, 0, keyLength);
}
@Override
public String getAlgorithm() {
return algorithm;
}
@Override
public String getFormat() {
return "RAW";
}
@Override
public byte[] getEncoded() {
byte[] copy = new byte[keyLength];
System.arraycopy(key, 0, copy, 0, keyLength);
return copy;
}
@Override
public int getIterationCount() {
return itereationCount;
}
@Override
public byte[] getSalt() {
return salt;
}
@Override
public char[] getPassword() {
return pass.toCharArray();
}
/**
* Spoil the generated key (before translation) to cause an
* InvalidKeyException
*/
public void spoil() {
itereationCount = -1;
}
@Override
public void destroy() throws DestroyFailedException {
}
@Override
public boolean isDestroyed() {
return false;
}
}
}
\ No newline at end of file
/*
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.security.auth.DestroyFailedException;
/*
* @test
* @bug 8048820
* @summary The test verifies SecretKey values should remain the same after
* translation with SecretKeyFactory.translateKey().
*/
public class SecKFTranslateTest {
private static final String SUN_JCE = "SunJCE";
public static void main(String[] args) throws Exception {
SecKFTranslateTest test = new SecKFTranslateTest();
test.run();
}
private void run() throws Exception {
for (Algorithm algorithm : Algorithm.values()) {
runTest(algorithm);
}
}
private void runTest(Algorithm algo) throws NoSuchAlgorithmException,
NoSuchProviderException, InvalidKeyException,
InvalidKeySpecException, NoSuchPaddingException,
InvalidAlgorithmParameterException, ShortBufferException,
IllegalBlockSizeException, BadPaddingException {
AlgorithmParameterSpec[] aps = new AlgorithmParameterSpec[1];
byte[] plainText = new byte[800];
SecretKey key1 = algo.intSecurityKey(aps);
Random random = new Random();
// Initialization
SecretKeyFactory skf = SecretKeyFactory.getInstance(algo.toString(),
SUN_JCE);
random.nextBytes(plainText);
Cipher ci = Cipher.getInstance(algo.toString(), SUN_JCE);
// Encryption
ci.init(Cipher.ENCRYPT_MODE, key1, aps[0]);
byte[] cipherText = new byte[ci.getOutputSize(plainText.length)];
int offset = ci.update(plainText, 0, plainText.length, cipherText, 0);
ci.doFinal(cipherText, offset);
// translate key
SecretKey key2 = skf.translateKey(key1);
// Decryption
ci.init(Cipher.DECRYPT_MODE, key2, aps[0]);
byte[] recoveredText = new byte[ci.getOutputSize(plainText.length)];
ci.doFinal(cipherText, 0, cipherText.length, recoveredText);
// Comparison
if (!Arrays.equals(plainText, recoveredText)) {
System.out.println("Key1:" + new String(key1.getEncoded())
+ " Key2:" + new String(key2.getEncoded()));
throw new RuntimeException("Testing translate key failed with "
+ algo);
}
}
}
class MyOwnSecKey implements SecretKey {
private static final String DEFAULT_ALGO = "PBEWithMD5AndDES";
private final byte[] key;
private final String algorithm;
private final int keySize;
public MyOwnSecKey(byte[] key1, int offset, String algo)
throws InvalidKeyException {
algorithm = algo;
if (algo.equalsIgnoreCase("DES")) {
keySize = 8;
} else if (algo.equalsIgnoreCase("DESede")) {
keySize = 24;
} else {
throw new InvalidKeyException(
"Inappropriate key format and algorithm");
}
if (key1 == null || key1.length - offset < keySize) {
throw new InvalidKeyException("Wrong key size");
}
key = new byte[keySize];
System.arraycopy(key, offset, key, 0, keySize);
}
public MyOwnSecKey(PBEKeySpec ks) throws InvalidKeySpecException {
algorithm = DEFAULT_ALGO;
key = new String(ks.getPassword()).getBytes();
keySize = key.length;
}
@Override
public String getAlgorithm() {
return algorithm;
}
@Override
public String getFormat() {
return "RAW";
}
@Override
public byte[] getEncoded() {
byte[] copy = new byte[keySize];
System.arraycopy(key, 0, copy, 0, keySize);
return copy;
}
@Override
public void destroy() throws DestroyFailedException {
}
@Override
public boolean isDestroyed() {
return false;
}
}
enum Algorithm {
DES {
@Override
SecretKey intSecurityKey(AlgorithmParameterSpec[] spec)
throws InvalidKeyException {
int keyLength = 8;
byte[] keyVal = new byte[keyLength];
new SecureRandom().nextBytes(keyVal);
SecretKey key1 = new MyOwnSecKey(keyVal, 0, this.toString());
return key1;
}
},
DESEDE {
@Override
SecretKey intSecurityKey(AlgorithmParameterSpec[] spec)
throws InvalidKeyException {
int keyLength = 24;
byte[] keyVal = new byte[keyLength];
new SecureRandom().nextBytes(keyVal);
SecretKey key1 = new MyOwnSecKey(keyVal, 0, this.toString());
return key1;
}
},
PBEWithMD5ANDdes {
@Override
SecretKey intSecurityKey(AlgorithmParameterSpec[] spec)
throws InvalidKeySpecException {
byte[] salt = new byte[8];
int iterCnt = 6;
new Random().nextBytes(salt);
spec[0] = new PBEParameterSpec(salt, iterCnt);
PBEKeySpec pbeKS = new PBEKeySpec(
new String("So far so good").toCharArray());
SecretKey key1 = new MyOwnSecKey(pbeKS);
return key1;
}
};
abstract SecretKey intSecurityKey(AlgorithmParameterSpec[] spec)
throws InvalidKeyException, InvalidKeySpecException;
}
/**
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 only, as published by
* the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License version 2 for more
* details (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU General Public License version 2
* along with this work; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA or
* visit www.oracle.com if you need additional information or have any
* questions.
*/
import javax.security.auth.login.LoginContext;
/**
* @test
* @bug 8050461
* @summary Test should throw Configuration error if configuration file contains
* syntax error
* @build SampleLoginModule JAASConfigSyntaxTest
* @run main/othervm -Djava.security.auth.login.config=file:${test.src}/JAASSynWithOutApplication.config JAASConfigSyntaxTest
* @run main/othervm -Djava.security.auth.login.config=file:${test.src}/JAASSynWithOutBraces.config JAASConfigSyntaxTest
* @run main/othervm -Djava.security.auth.login.config=file:${test.src}/JAASSynWithOutFlag.config JAASConfigSyntaxTest
* @run main/othervm -Djava.security.auth.login.config=file:${test.src}/JAASSynWithOutLoginModule.config JAASConfigSyntaxTest
* @run main/othervm -Djava.security.auth.login.config=file:${test.src}/JAASSynWithOutSemiColen.config JAASConfigSyntaxTest
*/
public class JAASConfigSyntaxTest {
private static final String TEST_NAME = "JAASConfigSyntaxTest";
public static void main(String[] args) throws Exception {
try {
LoginContext lc = new LoginContext(TEST_NAME);
lc.login();
throw new RuntimeException("Test Case Failed, did not get "
+ "expected exception");
} catch (Exception ex) {
if (ex.getMessage().contains("java.io.IOException: "
+ "Configuration Error:")) {
System.out.println("Test case passed");
} else {
throw new RuntimeException(ex);
}
}
}
}
{
SampleLoginModule Required;
SampleLoginModule Required;
SampleLoginModule Required;
};
\ No newline at end of file
JAASConfigSyntaxTest
SampleLoginModule Required;
SampleLoginModule Required;
SampleLoginModule Required;
;
\ No newline at end of file
JAASConfigSyntaxTest{
SampleLoginModule ;
SampleLoginModule ;
SampleLoginModule ;
};
\ No newline at end of file
JAASConfigSyntaxTest{
SampleLoginModule Required;
SampleLoginModule Required
SampleLoginModule Required;
};
\ No newline at end of file
/**
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 only, as published by
* the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License version 2 for more
* details (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU General Public License version 2
* along with this work; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA or
* visit www.oracle.com if you need additional information or have any
* questions.
*/
import static java.lang.System.out;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
/**
* Login module which passes all the time
*/
public class SampleLoginModule implements LoginModule {
private final String name;
public SampleLoginModule() {
name = this.getClass().getName();
}
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
}
@Override
public boolean login() throws LoginException {
out.println(name + " Login method of AbstractLoginModule is called ");
out.println(name + ":login:PASS");
return true;
}
@Override
public boolean commit() throws LoginException {
out.println("Commit of AbstractLoginModule is called");
out.println(name + ":commit:PASS");
return true;
}
@Override
public boolean abort() throws LoginException {
out.println("Abourt is called in AbstractLoginModule");
out.println(name + ":abort:PASS");
return true;
}
@Override
public boolean logout() throws LoginException {
out.println("logout is called in AbstractLoginModule");
out.println(name + ":logout:PASS");
return true;
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.security.Principal;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.ChoiceCallback;
import javax.security.auth.callback.ConfirmationCallback;
import javax.security.auth.callback.LanguageCallback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
public class CustomLoginModule implements LoginModule {
static final String HELLO = "Hello";
private Subject subject;
private CallbackHandler callbackHandler;
private boolean loginSucceeded = false;
private String username;
private char[] password;
/*
* Initialize this LoginModule.
*/
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
// check if custom parameter is passed from comfiguration
if (options == null) {
throw new RuntimeException("options is null");
}
// read username/password from configuration
Object o = options.get("username");
if (o == null) {
throw new RuntimeException("Custom parameter not passed");
}
if (!(o instanceof String)) {
throw new RuntimeException("Password is not a string");
}
username = (String) o;
o = options.get("password");
if (o == null) {
throw new RuntimeException("Custom parameter not passed");
}
if (!(o instanceof String)) {
throw new RuntimeException("Password is not a string");
}
password = ((String) o).toCharArray();
}
/*
* Authenticate the user.
*/
@Override
public boolean login() throws LoginException {
// prompt for a user name and password
if (callbackHandler == null) {
throw new LoginException("No CallbackHandler available");
}
// standard callbacks
NameCallback name = new NameCallback("username: ", "default");
PasswordCallback passwd = new PasswordCallback("password: ", false);
LanguageCallback language = new LanguageCallback();
TextOutputCallback error = new TextOutputCallback(
TextOutputCallback.ERROR, "This is an error");
TextOutputCallback warning = new TextOutputCallback(
TextOutputCallback.WARNING, "This is a warning");
TextOutputCallback info = new TextOutputCallback(
TextOutputCallback.INFORMATION, "This is a FYI");
TextInputCallback text = new TextInputCallback("Please type " + HELLO,
"Bye");
ChoiceCallback choice = new ChoiceCallback("Choice: ",
new String[] { "pass", "fail" }, 1, true);
ConfirmationCallback confirmation = new ConfirmationCallback(
"confirmation: ", ConfirmationCallback.INFORMATION,
ConfirmationCallback.YES_NO_OPTION, ConfirmationCallback.NO);
CustomCallback custom = new CustomCallback();
Callback[] callbacks = new Callback[] {
choice, info, warning, error, name, passwd, text, language,
confirmation, custom
};
boolean uce = false;
try {
callbackHandler.handle(callbacks);
} catch (UnsupportedCallbackException e) {
Callback callback = e.getCallback();
if (custom.equals(callback)) {
uce = true;
System.out.println("CustomLoginModule: "
+ "custom callback not supported as expected");
} else {
throw new LoginException("Unsupported callback: " + callback);
}
} catch (IOException ioe) {
throw new LoginException(ioe.toString());
}
if (!uce) {
throw new RuntimeException("UnsupportedCallbackException "
+ "not thrown");
}
if (!HELLO.equals(text.getText())) {
System.out.println("Text: " + text.getText());
throw new FailedLoginException("No hello");
}
if (!Locale.GERMANY.equals(language.getLocale())) {
System.out.println("Selected locale: " + language.getLocale());
throw new FailedLoginException("Achtung bitte");
}
String readUsername = name.getName();
char[] readPassword = passwd.getPassword();
if (readPassword == null) {
// treat a NULL password as an empty password
readPassword = new char[0];
}
passwd.clearPassword();
// verify the username/password
if (!username.equals(readUsername)
|| !Arrays.equals(password, readPassword)) {
loginSucceeded = false;
throw new FailedLoginException("Username/password is not correct");
}
// check chosen option
int[] selected = choice.getSelectedIndexes();
if (selected == null || selected.length == 0) {
throw new FailedLoginException("Nothing selected");
}
if (selected[0] != 0) {
throw new FailedLoginException("Wrong choice: " + selected[0]);
}
// check confirmation
if (confirmation.getSelectedIndex() != ConfirmationCallback.YES) {
throw new FailedLoginException("Not confirmed: "
+ confirmation.getSelectedIndex());
}
loginSucceeded = true;
System.out.println("CustomLoginModule: authentication succeeded");
return true;
}
/*
* This method is called if the LoginContext's overall authentication
* succeeded.
*/
@Override
public boolean commit() throws LoginException {
if (loginSucceeded) {
// add a Principal to the Subject
Principal principal = new TestPrincipal(username);
if (!subject.getPrincipals().contains(principal)) {
subject.getPrincipals().add(principal);
}
return true;
}
return false;
}
/*
* This method is called if the LoginContext's overall authentication
* failed.
*/
@Override
public boolean abort() throws LoginException {
loginSucceeded = false;
return true;
}
/*
* Logout the user.
*/
@Override
public boolean logout() throws LoginException {
loginSucceeded = false;
boolean removed = subject.getPrincipals().remove(
new TestPrincipal(username));
if (!removed) {
throw new LoginException("Coundn't remove a principal: "
+ username);
}
return true;
}
static class TestPrincipal implements Principal {
private final String name;
public TestPrincipal(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return("TestPrincipal [name =" + name + "]");
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (!(o instanceof TestPrincipal)) {
return false;
}
TestPrincipal other = (TestPrincipal) o;
return name != null ? name.equals(other.name) : other.name == null;
}
}
static class CustomCallback implements Callback {}
}
/*
* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import javax.security.auth.login.LoginException;
/**
* Login module which passes all the time
*/
public class DummyLoginModule extends SmartLoginModule {
private final String header;
public DummyLoginModule() {
header = "DummyLoginModule: ";
}
@Override
public boolean login() throws LoginException {
System.out.println("\t\t" + header + " login method is called ");
System.out.println("\t\t" + header + " login:PASS");
return true;
}
@Override
public boolean commit() throws LoginException {
System.out.println("\t\t" + header + " commit method is called");
System.out.println("\t\t" + header + " commit:PASS");
return true;
}
@Override
public boolean abort() throws LoginException {
System.out.println("\t\t" + header + " abort method is called ");
System.out.println("\t\t" + header + " abort:PASS");
return true;
}
@Override
public boolean logout() throws LoginException {
System.out.println("\t\t" + header + " logout method is called");
System.out.println("\t\t" + header + " logout:PASS");
return true;
}
}
/*
* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
/**
* @test
* @bug 8050427 4703361
* @summary Test case for RFE: 4703361. Tests the Dynamic Configuration of
* Authentication Modules with different methods
* @compile SmartLoginModule.java DummyLoginModule.java MyConfiguration.java
* @run main/othervm DynamicConfigurationTest
*/
public class DynamicConfigurationTest {
public static void main(String... args) {
String rightConfigName = "PT";
String wrongConfigName = "NT";
char[] rightPwd = new char[]{'t', 'e', 's', 't', 'P', 'a', 's', 's',
'w', 'o', 'r', 'd', '1'};
char[] wrongPwd = new char[]{'w', 'r', 'o', 'n', 'g', 'P', 'a', 's',
's','w', 'o', 'r', 'd'};
// Test with wrong configuration name
// Expect LoginException when initiate a new LoginContext object
testConfigName(wrongConfigName, true);
System.out.println("Wrong Config Name Test passed ");
// Spedify two loginModules: SmartLoginModule and DummyLoginModule
// Flags: required-required
// Test with right password for SmartLoginModule
// No exception is expected
Configuration cf = new MyConfiguration();
testLogin(rightConfigName, rightPwd, cf, false);
System.out.println("Positive test passed");
// Spedify two loginModules: SmartLoginModule and DummyLoginModule
// Flags: required-required
// Test with wrong password for SmartLoginModule
// Expect LoginException by calling LoginContext.login() method
testLogin(rightConfigName, wrongPwd, cf, true);
System.out.println("Should fail test passed");
// Spedify two loginModules: SmartLoginModule and DummyLoginModule
// Change the flags from required-required to optional-sufficient
// Test with wrong password for SmartLoginModule, while DummyLoginModule
// always passes
// No Exception is expected
cf = new MyConfiguration(true);
testLogin(rightConfigName, wrongPwd, cf, false);
System.out.println("One module fails where are other module succeeeds "
+ "Test passed with optional-sufficient flags");
}
public static void testConfigName(String confName,
boolean expectException) {
String expectedMsg = "No LoginModules configured for " + confName;
try {
LoginContext lc = new LoginContext(confName, new Subject(),
new MyCallbackHandler(), new MyConfiguration());
if (expectException) {
throw new RuntimeException("Wrong Config Name Test failed: "
+ "expected LoginException not thrown.");
}
} catch (LoginException le) {
if (!expectException || !le.getMessage().equals(expectedMsg)) {
System.out.println("Wrong Config Name Test failed: "
+ "received Unexpected exception.");
throw new RuntimeException(le);
}
}
}
public static void testLogin(String confName, char[] passwd,
Configuration cf, boolean expectException) {
try {
CallbackHandler ch = new MyCallbackHandler("testUser", passwd);
LoginContext lc = new LoginContext(confName, new Subject(),
ch, cf);
lc.login();
if (expectException) {
throw new RuntimeException("Login Test failed: "
+ "expected LoginException not thrown");
}
} catch (LoginException le) {
if (!expectException) {
System.out.println("Login Test failed: "
+ "received Unexpected exception.");
throw new RuntimeException(le);
}
}
}
}
/**
* The application simulates the CallbackHandler. It simulates! which means all
* process to get username and password is ignored. We have to take this
* approach for automation purpose. So, this is not a real world example at all.
*/
class MyCallbackHandler implements CallbackHandler {
String userName;
char[] password;
/**
* This is simply a workaround approach for IO approach to get username and
* password. For automation purpose only.
*/
public MyCallbackHandler() {
super();
}
public MyCallbackHandler(String username, char[] password) {
super();
userName = username;
this.password = password;
}
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
NameCallback nc = (NameCallback) callback;
nc.setName(userName);
} else if (callback instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback) callback;
pc.setPassword(password);
} else {
throw new UnsupportedCallbackException(callback,
"Unrecognized Callback");
}
}
}
}
/*
* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import static javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
import static javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
import static javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
import javax.security.auth.login.Configuration;
/**
* This class is used to test LoginContext constructor API. It simply contains
* one configuration entry: PT.
*/
public class MyConfiguration extends Configuration {
private static final AppConfigurationEntry[] ptAE
= new AppConfigurationEntry[2];
private static final HashMap<String, String> map = new HashMap<>();
private boolean optionOrder = false;
public MyConfiguration() {
setupConfiguration();
}
public MyConfiguration(boolean optionOrder) {
this.optionOrder = optionOrder;
setupConfiguration();
}
private void setupConfiguration() {
ptAE[0] = new AppConfigurationEntry("SmartLoginModule",
optionOrder ? OPTIONAL : REQUIRED,
map);
ptAE[1] = new AppConfigurationEntry("DummyLoginModule",
optionOrder ? SUFFICIENT : REQUIRED,
map);
}
@Override
public AppConfigurationEntry[]
getAppConfigurationEntry(String applicationName) {
if (applicationName.equals("PT")) {
return ptAE;
} else {
return null;
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
/**
* @test
* @bug 8048138
* @summary Check if shared state is passed to login module
* @run main/othervm SharedState
*/
public class SharedState {
static final String NAME = "name";
static final String VALUE = "shared";
public static void main(String[] args) throws LoginException {
System.setProperty("java.security.auth.login.config",
System.getProperty("test.src")
+ System.getProperty("file.separator")
+ "shared.config");
new LoginContext("SharedState").login();
}
public static abstract class Module implements LoginModule {
@Override
public boolean login() throws LoginException {
return true;
}
@Override
public boolean commit() throws LoginException {
return true;
}
@Override
public boolean abort() throws LoginException {
return true;
}
@Override
public boolean logout() throws LoginException {
return true;
}
}
public static class FirstModule extends Module {
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String,?> sharedState, Map<String,?> options) {
((Map)sharedState).put(NAME, VALUE);
}
}
public static class SecondModule extends Module {
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String,?> sharedState, Map<String,?> options) {
// check shared object
Object shared = sharedState.get(NAME);
if (!VALUE.equals(shared)) {
throw new RuntimeException("Unexpected shared object: "
+ shared);
}
}
}
}
/*
* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.security.Principal;
import java.util.Arrays;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
/**
* This code was based on JAAS demo code, small modification is made for testing
* purpose.
*/
public class SmartLoginModule implements LoginModule {
// initial state
private Subject subject;
private CallbackHandler callbackHandler;
// the authentication status
private boolean succeeded = false;
private boolean commitSucceeded = false;
// username and password
private String username;
private char[] password;
// Default values for this login module. In real world,
// don't do it in this way!
private String myUsername;
private char[] myPassword;
private String header;
// testUser's SamplePrincipal
private SamplePrincipal userPrincipal;
public SmartLoginModule() {
this("testUser",
new char[]{'t', 'e', 's', 't', 'P', 'a', 's', 's',
'w', 'o', 'r', 'd', '1'},
"SmartLoginModule1: ");
}
public SmartLoginModule(String userName, char[] password, String header) {
myUsername = userName;
myPassword = password;
this.header = header;
}
@Override
public boolean abort() throws LoginException {
if (!succeeded) {
return false;
} else if (succeeded && !commitSucceeded) {
// login succeeded but overall authentication failed
succeeded = false;
username = null;
password = null;
userPrincipal = null;
} else {
// overall authentication succeeded and commit succeeded,
// but someone else's commit failed
logout();
}
return true;
}
@Override
public boolean commit() throws LoginException {
if (!succeeded) {
return false;
} else {
// add a Principal (authenticated identity) to the Subject
// assume the user we authenticated is the SamplePrincipal
userPrincipal = new SamplePrincipal(username);
if (!subject.getPrincipals().contains(userPrincipal)) {
subject.getPrincipals().add(userPrincipal);
}
// in any case, clean out state
username = null;
password = null;
commitSucceeded = true;
return true;
}
}
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
}
@Override
public boolean login() throws LoginException {
if (callbackHandler == null) {
throw new LoginException("Error: no CallbackHandler available to "
+ "garner authentication information from the user");
}
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback(header + "user name: ");
callbacks[1] = new PasswordCallback(header + "password: ", false);
try {
callbackHandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
char[] tmpPassword
= ((PasswordCallback) callbacks[1]).getPassword();
if (tmpPassword == null) {
tmpPassword = new char[0];
}
password = new char[tmpPassword.length];
System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length);
((PasswordCallback) callbacks[1]).clearPassword();
} catch (java.io.IOException ioe) {
throw (LoginException) new LoginException().initCause(ioe);
} catch (UnsupportedCallbackException uce) {
throw new LoginException("Error: " + header
+ uce.getCallback().toString()
+ " not available to garner authentication information "
+ "from the user");
}
// verify the username/password
if (username.equals(myUsername)
&& Arrays.equals(password, myPassword)) {
System.out.println("\t\t" + header + " authentication succeeded");
succeeded = true;
return true;
} else {
// authentication failed -- clean out state
System.out.println("\t\t" + header + " authentication failed");
printDebugInfo();
succeeded = false;
username = null;
password = null;
throw new FailedLoginException("User Name or Password Incorrect");
}
}
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().remove(userPrincipal);
succeeded = false;
succeeded = commitSucceeded;
username = null;
password = null;
userPrincipal = null;
return true;
}
// print debugging information
private void printDebugInfo() {
System.out.println("\t\t" + header + " correct user name: "
+ myUsername);
System.out.println("\t\t" + header + " user entered user name: "
+ username);
System.out.print("\t\t" + header + " correct password: ");
for (char c : myPassword) {
System.out.print(c);
}
System.out.println();
System.out.print("\t\t" + header + " user entered password: ");
for (char c : password) {
System.out.print(c);
}
System.out.println();
}
}
class SamplePrincipal implements Principal, java.io.Serializable {
/**
* @serial
*/
private String name;
/**
* Create a SamplePrincipal with a Sample username.
*
* @param name the Sample username for this user.
* @exception NullPointerException if the <code>name</code> is
* <code>null</code>.
*/
public SamplePrincipal(String name) {
if (name == null) {
throw new NullPointerException("illegal null input");
}
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return "SamplePrincipal: " + name;
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (this == o) {
return true;
}
if (!(o instanceof SamplePrincipal)) {
return false;
}
SamplePrincipal that = (SamplePrincipal) o;
return this.getName().equals(that.getName());
}
@Override
public int hashCode() {
return name.hashCode();
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.security.Principal;
import java.util.Arrays;
import java.util.Locale;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.ChoiceCallback;
import javax.security.auth.callback.ConfirmationCallback;
import javax.security.auth.callback.LanguageCallback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
/*
* @test
* @bug 8048138
* @summary Checks if JAAS login works fine with standard callbacks
* @compile DefaultHandlerModule.java
* @run main/othervm StandardCallbacks
*/
public class StandardCallbacks {
private static final String USERNAME = "username";
private static final char[] PASSWORD = "password".toCharArray();
public static void main(String[] args) throws LoginException {
System.setProperty("java.security.auth.login.config",
System.getProperty("test.src")
+ System.getProperty("file.separator")
+ "custom.config");
CustomCallbackHandler handler = new CustomCallbackHandler(USERNAME);
LoginContext context = new LoginContext("StandardCallbacks", handler);
handler.setPassword(PASSWORD);
System.out.println("Try to login with correct password, "
+ "successful authentication is expected");
context.login();
System.out.println("Authentication succeeded!");
Subject subject = context.getSubject();
System.out.println("Authenticated user has the following principals ["
+ subject.getPrincipals().size() + " ]:");
boolean found = true;
for (Principal principal : subject.getPrincipals()) {
System.out.println("principal: " + principal);
if (principal instanceof CustomLoginModule.TestPrincipal) {
CustomLoginModule.TestPrincipal testPrincipal =
(CustomLoginModule.TestPrincipal) principal;
if (USERNAME.equals(testPrincipal.getName())) {
System.out.println("Found test principal: "
+ testPrincipal);
found = true;
break;
}
}
}
if (!found) {
throw new RuntimeException("TestPrincipal not found");
}
// check if all expected text output callbacks have been called
if (!handler.info) {
throw new RuntimeException("TextOutputCallback.INFO not called");
}
if (!handler.warning) {
throw new RuntimeException("TextOutputCallback.WARNING not called");
}
if (!handler.error) {
throw new RuntimeException("TextOutputCallback.ERROR not called");
}
System.out.println("Authenticated user has the following public "
+ "credentials [" + subject.getPublicCredentials().size()
+ "]:");
subject.getPublicCredentials().stream().
forEach((o) -> {
System.out.println("public credential: " + o);
});
context.logout();
System.out.println("Test passed");
}
private static class CustomCallbackHandler implements CallbackHandler {
private final String username;
private char[] password;
private boolean info = false;
private boolean warning = false;
private boolean error = false;
CustomCallbackHandler(String username) {
this.username = username;
}
void setPassword(char[] password) {
this.password = password;
}
@Override
public void handle(Callback[] callbacks)
throws UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof TextOutputCallback) {
TextOutputCallback toc = (TextOutputCallback) callback;
switch (toc.getMessageType()) {
case TextOutputCallback.INFORMATION:
System.out.println("INFO: " + toc.getMessage());
info = true;
break;
case TextOutputCallback.ERROR:
System.out.println("ERROR: " + toc.getMessage());
error = true;
break;
case TextOutputCallback.WARNING:
System.out.println("WARNING: " + toc.getMessage());
warning = true;
break;
default:
throw new UnsupportedCallbackException(toc,
"Unsupported message type: "
+ toc.getMessageType());
}
} else if (callback instanceof TextInputCallback) {
TextInputCallback tic = (TextInputCallback) callback;
System.out.println(tic.getPrompt());
tic.setText(CustomLoginModule.HELLO);
} else if (callback instanceof LanguageCallback) {
LanguageCallback lc = (LanguageCallback) callback;
lc.setLocale(Locale.GERMANY);
} else if (callback instanceof ConfirmationCallback) {
ConfirmationCallback cc = (ConfirmationCallback) callback;
System.out.println(cc.getPrompt());
cc.setSelectedIndex(ConfirmationCallback.YES);
} else if (callback instanceof ChoiceCallback) {
ChoiceCallback cc = (ChoiceCallback) callback;
System.out.println(cc.getPrompt()
+ Arrays.toString(cc.getChoices()));
cc.setSelectedIndex(0);
} else if (callback instanceof NameCallback) {
NameCallback nc = (NameCallback) callback;
System.out.println(nc.getPrompt());
nc.setName(username);
} else if (callback instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback) callback;
System.out.println(pc.getPrompt());
pc.setPassword(password);
} else {
throw new UnsupportedCallbackException(callback,
"Unknown callback");
}
}
}
}
}
StandardCallbacks {
DefaultHandlerModule required;
CustomLoginModule required username="username" password="password";
};
SharedState {
SharedState$FirstModule required;
SharedState$SecondModule required;
};
\ No newline at end of file
此差异已折叠。
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"fooObject"})
@XmlRootElement(name = "Foo")
public class Foo {
protected List<Foo> fooObject;
public List<Foo> getFooObject() {
if (fooObject == null) {
fooObject = new ArrayList<Foo>();
}
return this.fooObject;
}
}
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8073872
* @summary test that stackoverflow is not observable when element
* references containing class
* @compile Foo.java
* @run testng/othervm SchemagenStackOverflow
*/
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Collectors;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
import org.testng.Assert;
import org.testng.annotations.Test;
public class SchemagenStackOverflow {
@Test
public void schemagenStackOverflowTest() throws Exception {
// Create new instance of JAXB context
JAXBContext context = JAXBContext.newInstance(Foo.class);
context.generateSchema(new TestOutputResolver());
// Read schema content from file
String content = Files.lines(resultSchemaFile).collect(Collectors.joining(""));
System.out.println("Generated schema content:" + content);
// Check if schema was generated: check class and list object names
Assert.assertTrue(content.contains("name=\"Foo\""));
Assert.assertTrue(content.contains("name=\"fooObject\""));
}
// Schemagen output resolver
class TestOutputResolver extends SchemaOutputResolver {
@Override
public Result createOutput(String namespaceUri, String fileName)
throws IOException {
return new StreamResult(resultSchemaFile.toFile());
}
}
// Schemagen output file name and path
static final String SCHEMA_FILENAME = "generatedSchema.xsd";
Path resultSchemaFile = Paths.get(System.getProperty("user.dir", "."))
.resolve(SCHEMA_FILENAME);
}
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8145039
* @summary Check that marshalling of xjc generated class doesn't throw
* ClassCast exception.
* @library /lib/testlibrary
* @run testng/othervm JaxbMarshallTest
*/
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import java.util.Arrays;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import jdk.testlibrary.JDKToolLauncher;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class JaxbMarshallTest {
@BeforeTest
public void setUp() throws IOException {
// Create test directory inside scratch
testWorkDir = Paths.get(System.getProperty("user.dir", "."));
// Save its URL
testWorkDirUrl = testWorkDir.toUri().toURL();
// Get test source directory path
testSrcDir = Paths.get(System.getProperty("test.src", "."));
// Get path of xjc result folder
xjcResultDir = testWorkDir.resolve(TEST_PACKAGE);
// Copy schema document file to scratch directory
Files.copy(testSrcDir.resolve(XSD_FILENAME), testWorkDir.resolve(XSD_FILENAME), REPLACE_EXISTING);
}
/*
* Test does the following steps to reproduce problem reported by 8145039:
* 1. Copy test schema to JTREG scratch folder
* 2. Run xjc on test schema file
* 3. Compile generated java files with test javac
* 4. Marshall the new list instance to ensure that
* ClassCastException is not thrown
*/
@Test
public void marshallClassCastExceptionTest() throws Exception {
JAXBContext jaxbContext;
Marshaller marshaller;
URLClassLoader jaxbContextClassLoader;
// Generate java classes by xjc
runXjc(XSD_FILENAME);
// Compile xjc generated java files
compileXjcGeneratedClasses();
// Create JAXB context based on xjc generated package.
// Need to create URL class loader ot make compiled classes discoverable
// by JAXB context
jaxbContextClassLoader = URLClassLoader.newInstance(new URL[] {testWorkDirUrl});
jaxbContext = JAXBContext.newInstance( TEST_PACKAGE, jaxbContextClassLoader);
// Create instance of Xjc generated data type.
// Java classes were compiled during the test execution hence reflection
// is needed here
Class classLongListClass = jaxbContextClassLoader.loadClass(TEST_CLASS);
Object objectLongListClass = classLongListClass.newInstance();
// Get 'getIn' method object
Method getInMethod = classLongListClass.getMethod( GET_LIST_METHOD, (Class [])null );
// Invoke 'getIn' method
List<Long> inList = (List<Long>)getInMethod.invoke(objectLongListClass);
// Add values into the jaxb object list
inList.add(Long.valueOf(0));
inList.add(Long.valueOf(43));
inList.add(Long.valueOf(1000000123));
// Marshall constructed complex type variable to standard output.
// In case of failure the ClassCastException will be thrown
marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(objectLongListClass, System.out);
}
// Compile schema file into java classes definitions
void runXjc(String xsdFileName) throws Exception {
// Prepare process builder to run schemagen tool and save its output
JDKToolLauncher xjcLauncher = JDKToolLauncher.createUsingTestJDK("xjc");
xjcLauncher.addToolArg(xsdFileName);
System.out.println("Executing xjc command: " + Arrays.asList(xjcLauncher.getCommand()));
ProcessBuilder pb = new ProcessBuilder(xjcLauncher.getCommand());
// Set xjc work directory with the input java file
pb.directory(testWorkDir.toFile());
pb.inheritIO();
Process p = pb.start();
p.waitFor();
p.destroy();
}
// Compile java classes with javac tool
void compileXjcGeneratedClasses() throws Exception {
JDKToolLauncher javacLauncher = JDKToolLauncher.createUsingTestJDK("javac");
javacLauncher.addToolArg(xjcResultDir.resolve("ObjectFactory.java").toString());
javacLauncher.addToolArg(xjcResultDir.resolve("TypesLongList.java").toString());
javacLauncher.addToolArg(xjcResultDir.resolve("package-info.java").toString());
System.out.println("Compiling xjc generated classes: " + Arrays.asList(javacLauncher.getCommand()));
ProcessBuilder pb = new ProcessBuilder(javacLauncher.getCommand());
pb.inheritIO();
pb.directory(testWorkDir.toFile());
Process p = pb.start();
p.waitFor();
p.destroy();
}
// Test schema filename
static final String XSD_FILENAME = "testSchema.xsd";
// Package of java classes generated by xjc
static final String TEST_PACKAGE = "testns_package";
// Name of generated java class
static final String TEST_CLASS = TEST_PACKAGE+".TypesLongList";
// Method to get the list from xjc generated class
static final String GET_LIST_METHOD = "getIn";
// Test working directory
Path testWorkDir;
// Test working directory URL
URL testWorkDirUrl;
// Directory with test src
Path testSrcDir;
// Directory with java files generated by xjc
Path xjcResultDir;
}
此差异已折叠。
此差异已折叠。
<?xml version="1.0" encoding="UTF-8"?>
<element>
<text>.</text>
</element>
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册