You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
12 KiB
282 lines
12 KiB
/* |
|
* Copyright (c) 2001, 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.io.BufferedInputStream; |
|
import java.io.ByteArrayInputStream; |
|
import java.io.ByteArrayOutputStream; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.security.KeyFactory; |
|
import java.security.KeyStore; |
|
import java.security.KeyStoreException; |
|
import java.security.NoSuchProviderException; |
|
import java.security.PrivateKey; |
|
import java.security.UnrecoverableKeyException; |
|
import java.security.cert.Certificate; |
|
import java.security.cert.CertificateFactory; |
|
import java.security.spec.KeySpec; |
|
import java.security.spec.PKCS8EncodedKeySpec; |
|
import java.util.Base64; |
|
|
|
/* |
|
* @test |
|
* @bug 8048621 8133090 |
|
* @summary Test basic operations with keystores (jks, jceks, pkcs12) |
|
* @author Yu-Ching Valerie PENG |
|
*/ |
|
public class TestKeyStoreBasic { |
|
|
|
private static final String PRIVATE_KEY_PKCS8_BASE64 = "" |
|
+ "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCpyz97liuWPDYcLH9TX8BiT78o" |
|
+ "lCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgKmLhuczF3M9VIcWr+" |
|
+ "JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz7leikne7KmclHvTfvFd0WDI7Gb9v" |
|
+ "o4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXRv5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfF" |
|
+ "e1DDsMg/KpKGiILYZ+g2qtVMZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e" |
|
+ "+sO6H24w2F19AgMBAAECggEBAId/12187dO6wUPCjumuJA1QrrBnbKdKONyai36uoc1Od4s5QFj7" |
|
+ "+hEIeS7rbGNYQuBvnkgusAbzkW0FIpxpHce3EJez/emux6pEOKoP77BwMt9gy+txyu0+BHi91FQg" |
|
+ "AGvrnQDO5EYVY4Cz/WjOsJzKu8zVLg+DS0Toa2qRFwmUe9mVAXPNOCZ3Oae/Q6tCDsaINNw0fmjj" |
|
+ "jn6uohPbS+n6xENG3FkQXB36getXy310xTGED2J27cmAQH6gLR6Kl2iROzNPbbpBqbuemI9kbcld" |
|
+ "EwBS1jRfZWeaPstYA1niVrE9UgUBzemnoh4TDkG076sYthHMr5QFGjPswnwtJ4ECgYEA0sURQ5+v" |
|
+ "baH4tdaemI3qpnknXTlzSpuZZmAoyvY0Id0mlduwKwmZ3Y5989wHfnnhFfyNO4IkTKjI2Wp97qP5" |
|
+ "4eqUNpA7FtNU7KUzMcFDTtwtNZuRYMrKlqo2lLbA+gVrAYpYZFL4b7tcwtX4DnYorDsmude6W8sG" |
|
+ "4Mx2VdFJC9UCgYEAzjsdXCYH5doWUHb0dvn9ID7IikffEMRM720MRjrnnnVbpzx6ACntkPDNZg7p" |
|
+ "TRE/mx7iBz81ZaUWE+V0wd0JvCHEdpAz3mksyvDFhU4Bgs6xzf2pSul5muhsx3hHcvvPezz5Bnxs" |
|
+ "faJlzkxfwotyGmvWN15GA/pyfsZjsbbTpwkCgYAO6NnbysQCIV8SnegCKqfatt9N/O5m7LLhRxQb" |
|
+ "p2bwrlA4cZ34rWkw/w9x3LK7A6wkfgUPnJkswxPSLXJTG05l6M4rPfCwIKr1Qopojp9QSMr569NQ" |
|
+ "4YeLOOc7heIIzbFQHpU6I5Rncv2Q2sn9W+ZsqJKIuvX34FjQNiZ406EzMQKBgHSxOGS61D84DuZK" |
|
+ "2Ps1awhC3kB4eHzJRms3vflDPWoJJ+pSKwpKrzUTPHXiPBqyhtYkPGszVeiE6CAr9sv3YZnFVaBs" |
|
+ "6hyQUJsob+uE/w/gGvXe8VsFDx0bJOodYfhrCbTHBHWqE81nBcocpxayxsayfAzqWB3KKd0YLrMR" |
|
+ "K2PZAoGAcZa8915R2m0KZ6HVJUt/JDR85jCbN71kcVDFY2XSFkOJvOdFoHNfRckfLzjq9Y2MSSTV" |
|
+ "+QDWbDo2doUQCejJUTaN8nP79tfyir24X5uVPvQaeVoGTKYb+LfUqK0F60lStmjuddIGSZH55y3v" |
|
+ "+9XjmxbVERtd1lqgQg3VlmKlEXY="; |
|
|
|
/* |
|
* Certificate: |
|
* Data: |
|
* Version: 3 (0x2) |
|
* Serial Number: 7 (0x7) |
|
* Signature Algorithm: sha512WithRSAEncryption |
|
* Issuer: CN=Root |
|
* Validity |
|
* Not Before: Sep 1 18:03:59 2015 GMT |
|
* Not After : Jan 17 18:03:59 2043 GMT |
|
* Subject: CN=EE |
|
*/ |
|
private static final String CERTIFICATE = "" |
|
+ "-----BEGIN CERTIFICATE-----\n" |
|
+ "MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290\n" |
|
+ "MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw\n" |
|
+ "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi\n" |
|
+ "T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK\n" |
|
+ "mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz\n" |
|
+ "7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR\n" |
|
+ "v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM\n" |
|
+ "ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19\n" |
|
+ "AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m\n" |
|
+ "b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s\n" |
|
+ "ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G\n" |
|
+ "CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/\n" |
|
+ "gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc\n" |
|
+ "iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn\n" |
|
+ "ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1\n" |
|
+ "/a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL\n" |
|
+ "hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY\n" |
|
+ "-----END CERTIFICATE-----\n"; |
|
|
|
private static final char[] PASSWD2 = new char[] { |
|
'b', 'o', 'r', 'e', 'd' |
|
}; |
|
private static final char[] PASSWDK = "cannot be null" |
|
.toCharArray(); |
|
private static final String[] KS_Type = { |
|
"jks", "jceks", "pkcs12", "PKCS11KeyStore" |
|
}; |
|
private static final String[] PROVIDERS = { |
|
"SUN", "SunJCE", "SunJSSE", "SunPKCS11-Solaris" |
|
}; |
|
private static final String ALIAS_HEAD = "test"; |
|
|
|
public static void main(String args[]) throws Exception { |
|
TestKeyStoreBasic jstest = new TestKeyStoreBasic(); |
|
jstest.run(); |
|
} |
|
|
|
public void run() throws Exception { |
|
for (String provider : PROVIDERS) { |
|
try { |
|
runTest(provider); |
|
System.out.println("Test with provider " + provider + "passed"); |
|
} catch (java.security.KeyStoreException e) { |
|
if (provider.equals("SunPKCS11-Solaris")) { |
|
System.out.println("KeyStoreException is expected: " |
|
+ "PKCS11KeyStore is invalid keystore type: " + e); |
|
} else { |
|
throw e; |
|
} |
|
} catch (NoSuchProviderException e) { |
|
String osName = System.getProperty("os.name"); |
|
if (provider.equals("SunPKCS11-Solaris") |
|
&& !osName.equals("SunOS")) { |
|
System.out.println("Skip SunPKCS11-Solaris provider on " |
|
+ osName); |
|
} else { |
|
throw e; |
|
} |
|
} |
|
} |
|
} |
|
|
|
public void runTest(String provider) throws Exception { |
|
|
|
// load private key |
|
// all keystore types should support private keys |
|
KeySpec spec = new PKCS8EncodedKeySpec( |
|
Base64.getMimeDecoder().decode(PRIVATE_KEY_PKCS8_BASE64)); |
|
PrivateKey privateKey = KeyFactory.getInstance("RSA") |
|
.generatePrivate(spec); |
|
|
|
// load x509 certificate |
|
Certificate cert; |
|
try (InputStream is = new BufferedInputStream( |
|
new ByteArrayInputStream(CERTIFICATE.getBytes()))) { |
|
cert = CertificateFactory.getInstance("X.509") |
|
.generateCertificate(is); |
|
} |
|
|
|
int numEntries = 5; |
|
String type = null; |
|
for (int i = 0; i < PROVIDERS.length; i++) { |
|
if (provider.compareTo(PROVIDERS[i]) == 0) { |
|
type = KS_Type[i]; |
|
break; |
|
} |
|
} |
|
|
|
System.out.printf("Test %s provider and %s keystore%n", provider, type); |
|
KeyStore ks = KeyStore.getInstance(type, provider); |
|
KeyStore ks2 = KeyStore.getInstance(type, ks.getProvider().getName()); |
|
|
|
// create an empty key store |
|
ks.load(null, null); |
|
|
|
// store the secret keys |
|
for (int j = 0; j < numEntries; j++) { |
|
ks.setKeyEntry(ALIAS_HEAD + j, privateKey, PASSWDK, |
|
new Certificate[] { cert }); |
|
} |
|
|
|
// initialize the 2nd key store object with the 1st one |
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
ks.store(baos, PASSWDK); |
|
byte[] bArr = baos.toByteArray(); |
|
ByteArrayInputStream bais = new ByteArrayInputStream(bArr); |
|
ks2.load(bais, null); |
|
|
|
// check 2nd key store type |
|
checkType(ks2, type); |
|
// check the existing aliases for the 2nd key store |
|
checkAlias(ks2, numEntries); |
|
|
|
// compare the creation date of the 2 key stores for all aliases |
|
compareCreationDate(ks, ks2, numEntries); |
|
// remove the last entry from the 2nd key store |
|
numEntries--; |
|
ks2.deleteEntry(ALIAS_HEAD + numEntries); |
|
|
|
// re-initialize the 1st key store with the 2nd key store |
|
baos.reset(); |
|
ks2.store(baos, PASSWD2); |
|
bais = new ByteArrayInputStream(baos.toByteArray()); |
|
try { |
|
// expect an exception since the password is incorrect |
|
ks.load(bais, PASSWDK); |
|
throw new RuntimeException( |
|
"ERROR: passed the loading with incorrect password"); |
|
} catch (IOException ex) { |
|
System.out.println("Expected exception: " + ex); |
|
if (!causedBy(ex, UnrecoverableKeyException.class)) { |
|
ex.printStackTrace(System.out); |
|
throw new RuntimeException("Unexpected cause"); |
|
} |
|
System.out.println("Expected cause: " |
|
+ UnrecoverableKeyException.class.getName()); |
|
|
|
bais.reset(); |
|
ks.load(bais, PASSWD2); |
|
bais.reset(); |
|
ks.load(bais, null); |
|
} |
|
|
|
// check key store type |
|
checkType(ks, type); |
|
|
|
// check the existing aliases |
|
checkAlias(ks, numEntries); |
|
|
|
// compare the creation date of the 2 key stores for all aliases |
|
compareCreationDate(ks, ks2, numEntries); |
|
|
|
} |
|
|
|
// check key store type |
|
private void checkType(KeyStore obj, String type) { |
|
if (!obj.getType().equals(type)) { |
|
throw new RuntimeException("ERROR: wrong key store type"); |
|
} |
|
} |
|
|
|
// check the existing aliases |
|
private void checkAlias(KeyStore obj, int range) throws KeyStoreException { |
|
for (int k = 0; k < range; k++) { |
|
if (!obj.containsAlias(ALIAS_HEAD + k)) { |
|
throw new RuntimeException("ERROR: alias (" + k |
|
+ ") should exist"); |
|
} |
|
} |
|
} |
|
|
|
// compare the creation dates - true if all the same |
|
private void compareCreationDate(KeyStore o1, KeyStore o2, int range) |
|
throws KeyStoreException { |
|
String alias; |
|
for (int k = 0; k < range; k++) { |
|
alias = ALIAS_HEAD + k; |
|
if (!o1.getCreationDate(alias).equals(o2.getCreationDate(alias))) { |
|
throw new RuntimeException("ERROR: entry creation time (" + k |
|
+ ") differs"); |
|
} |
|
} |
|
} |
|
|
|
// checks if an exception was caused by specified exception class |
|
private static boolean causedBy(Exception e, Class klass) { |
|
Throwable cause = e; |
|
while ((cause = cause.getCause()) != null) { |
|
if (cause.getClass().equals(klass)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
}
|
|
|