|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.sun.crypto.provider; |
|
|
|
import sun.security.util.Debug; |
|
|
|
import java.io.*; |
|
import java.util.*; |
|
import java.security.AccessController; |
|
import java.security.DigestInputStream; |
|
import java.security.DigestOutputStream; |
|
import java.security.MessageDigest; |
|
import java.security.NoSuchAlgorithmException; |
|
import java.security.Key; |
|
import java.security.PrivateKey; |
|
import java.security.PrivilegedAction; |
|
import java.security.KeyStoreSpi; |
|
import java.security.KeyStoreException; |
|
import java.security.UnrecoverableKeyException; |
|
import java.security.cert.Certificate; |
|
import java.security.cert.CertificateFactory; |
|
import java.security.cert.CertificateException; |
|
import javax.crypto.SealedObject; |
|
|
|
import sun.misc.IOUtils; |
|
import sun.misc.ObjectInputFilter; |
|
|
|
/** |
|
* This class provides the keystore implementation referred to as "jceks". |
|
* This implementation strongly protects the keystore private keys using |
|
* triple-DES, where the triple-DES encryption/decryption key is derived from |
|
* the user's password. |
|
* The encrypted private keys are stored in the keystore in a standard format, |
|
* namely the <code>EncryptedPrivateKeyInfo</code> format defined in PKCS #8. |
|
* |
|
* @author Jan Luehe |
|
* |
|
* |
|
* @see java.security.KeyStoreSpi |
|
*/ |
|
|
|
public final class JceKeyStore extends KeyStoreSpi { |
|
|
|
private static final Debug debug = Debug.getInstance("keystore"); |
|
private static final int JCEKS_MAGIC = 0xcececece; |
|
private static final int JKS_MAGIC = 0xfeedfeed; |
|
private static final int VERSION_1 = 0x01; |
|
private static final int VERSION_2 = 0x02; |
|
|
|
|
|
private static final class PrivateKeyEntry { |
|
Date date; |
|
byte[] protectedKey; |
|
Certificate[] chain; |
|
}; |
|
|
|
|
|
private static final class SecretKeyEntry { |
|
Date date; |
|
SealedObject sealedKey; |
|
|
|
// Maximum possible length of sealedKey. Used to detect malicious |
|
// input data. This field is set to the file length of the keystore |
|
// at loading. It is useless when creating a new SecretKeyEntry |
|
|
|
int maxLength; |
|
} |
|
|
|
|
|
private static final class TrustedCertEntry { |
|
Date date; |
|
Certificate cert; |
|
}; |
|
|
|
|
|
|
|
|
|
*/ |
|
private Hashtable<String, Object> entries = new Hashtable<String, Object>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Key engineGetKey(String alias, char[] password) |
|
throws NoSuchAlgorithmException, UnrecoverableKeyException |
|
{ |
|
Key key = null; |
|
|
|
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); |
|
|
|
if (!((entry instanceof PrivateKeyEntry) || |
|
(entry instanceof SecretKeyEntry))) { |
|
return null; |
|
} |
|
|
|
KeyProtector keyProtector = new KeyProtector(password); |
|
|
|
if (entry instanceof PrivateKeyEntry) { |
|
byte[] encrBytes = ((PrivateKeyEntry)entry).protectedKey; |
|
EncryptedPrivateKeyInfo encrInfo; |
|
try { |
|
encrInfo = new EncryptedPrivateKeyInfo(encrBytes); |
|
} catch (IOException ioe) { |
|
throw new UnrecoverableKeyException("Private key not stored " |
|
+ "as PKCS #8 " + |
|
"EncryptedPrivateKeyInfo"); |
|
} |
|
key = keyProtector.recover(encrInfo); |
|
} else { |
|
SecretKeyEntry ske = ((SecretKeyEntry)entry); |
|
key = keyProtector.unseal(ske.sealedKey, ske.maxLength); |
|
} |
|
|
|
return key; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Certificate[] engineGetCertificateChain(String alias) |
|
{ |
|
Certificate[] chain = null; |
|
|
|
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); |
|
|
|
if ((entry instanceof PrivateKeyEntry) |
|
&& (((PrivateKeyEntry)entry).chain != null)) { |
|
chain = ((PrivateKeyEntry)entry).chain.clone(); |
|
} |
|
|
|
return chain; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Certificate engineGetCertificate(String alias) { |
|
Certificate cert = null; |
|
|
|
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); |
|
|
|
if (entry != null) { |
|
if (entry instanceof TrustedCertEntry) { |
|
cert = ((TrustedCertEntry)entry).cert; |
|
} else if ((entry instanceof PrivateKeyEntry) && |
|
(((PrivateKeyEntry)entry).chain != null)) { |
|
cert = ((PrivateKeyEntry)entry).chain[0]; |
|
} |
|
} |
|
|
|
return cert; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Date engineGetCreationDate(String alias) { |
|
Date date = null; |
|
|
|
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); |
|
|
|
if (entry != null) { |
|
// We have to create a new instance of java.util.Date because |
|
|
|
if (entry instanceof TrustedCertEntry) { |
|
date = new Date(((TrustedCertEntry)entry).date.getTime()); |
|
} else if (entry instanceof PrivateKeyEntry) { |
|
date = new Date(((PrivateKeyEntry)entry).date.getTime()); |
|
} else { |
|
date = new Date(((SecretKeyEntry)entry).date.getTime()); |
|
} |
|
} |
|
|
|
return date; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void engineSetKeyEntry(String alias, Key key, char[] password, |
|
Certificate[] chain) |
|
throws KeyStoreException |
|
{ |
|
synchronized(entries) { |
|
try { |
|
KeyProtector keyProtector = new KeyProtector(password); |
|
|
|
if (key instanceof PrivateKey) { |
|
PrivateKeyEntry entry = new PrivateKeyEntry(); |
|
entry.date = new Date(); |
|
|
|
|
|
entry.protectedKey = keyProtector.protect((PrivateKey)key); |
|
|
|
|
|
if ((chain != null) && |
|
(chain.length !=0)) { |
|
entry.chain = chain.clone(); |
|
} else { |
|
entry.chain = null; |
|
} |
|
|
|
|
|
entries.put(alias.toLowerCase(Locale.ENGLISH), entry); |
|
|
|
} else { |
|
SecretKeyEntry entry = new SecretKeyEntry(); |
|
entry.date = new Date(); |
|
|
|
|
|
entry.sealedKey = keyProtector.seal(key); |
|
entry.maxLength = Integer.MAX_VALUE; |
|
entries.put(alias.toLowerCase(Locale.ENGLISH), entry); |
|
} |
|
|
|
} catch (Exception e) { |
|
throw new KeyStoreException(e.getMessage()); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void engineSetKeyEntry(String alias, byte[] key, |
|
Certificate[] chain) |
|
throws KeyStoreException |
|
{ |
|
synchronized(entries) { |
|
// We assume it's a private key, because there is no standard |
|
|
|
PrivateKeyEntry entry = new PrivateKeyEntry(); |
|
entry.date = new Date(); |
|
|
|
entry.protectedKey = key.clone(); |
|
if ((chain != null) && |
|
(chain.length != 0)) { |
|
entry.chain = chain.clone(); |
|
} else { |
|
entry.chain = null; |
|
} |
|
|
|
entries.put(alias.toLowerCase(Locale.ENGLISH), entry); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void engineSetCertificateEntry(String alias, Certificate cert) |
|
throws KeyStoreException |
|
{ |
|
synchronized(entries) { |
|
|
|
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); |
|
if (entry != null) { |
|
if (entry instanceof PrivateKeyEntry) { |
|
throw new KeyStoreException("Cannot overwrite own " |
|
+ "certificate"); |
|
} else if (entry instanceof SecretKeyEntry) { |
|
throw new KeyStoreException("Cannot overwrite secret key"); |
|
} |
|
} |
|
|
|
TrustedCertEntry trustedCertEntry = new TrustedCertEntry(); |
|
trustedCertEntry.cert = cert; |
|
trustedCertEntry.date = new Date(); |
|
entries.put(alias.toLowerCase(Locale.ENGLISH), trustedCertEntry); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void engineDeleteEntry(String alias) |
|
throws KeyStoreException |
|
{ |
|
synchronized(entries) { |
|
entries.remove(alias.toLowerCase(Locale.ENGLISH)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Enumeration<String> engineAliases() { |
|
return entries.keys(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean engineContainsAlias(String alias) { |
|
return entries.containsKey(alias.toLowerCase(Locale.ENGLISH)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int engineSize() { |
|
return entries.size(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean engineIsKeyEntry(String alias) { |
|
boolean isKey = false; |
|
|
|
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); |
|
if ((entry instanceof PrivateKeyEntry) |
|
|| (entry instanceof SecretKeyEntry)) { |
|
isKey = true; |
|
} |
|
|
|
return isKey; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean engineIsCertificateEntry(String alias) { |
|
boolean isCert = false; |
|
Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); |
|
if (entry instanceof TrustedCertEntry) { |
|
isCert = true; |
|
} |
|
return isCert; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String engineGetCertificateAlias(Certificate cert) { |
|
Certificate certElem; |
|
|
|
Enumeration<String> e = entries.keys(); |
|
while (e.hasMoreElements()) { |
|
String alias = e.nextElement(); |
|
Object entry = entries.get(alias); |
|
if (entry instanceof TrustedCertEntry) { |
|
certElem = ((TrustedCertEntry)entry).cert; |
|
} else if ((entry instanceof PrivateKeyEntry) && |
|
(((PrivateKeyEntry)entry).chain != null)) { |
|
certElem = ((PrivateKeyEntry)entry).chain[0]; |
|
} else { |
|
continue; |
|
} |
|
if (certElem.equals(cert)) { |
|
return alias; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void engineStore(OutputStream stream, char[] password) |
|
throws IOException, NoSuchAlgorithmException, CertificateException |
|
{ |
|
synchronized(entries) { |
|
/* |
|
* KEYSTORE FORMAT: |
|
* |
|
* Magic number (big-endian integer), |
|
* Version of this file format (big-endian integer), |
|
* |
|
* Count (big-endian integer), |
|
* followed by "count" instances of either: |
|
* |
|
* { |
|
* tag=1 (big-endian integer) |
|
* alias (UTF string) |
|
* timestamp |
|
* encrypted private-key info according to PKCS #8 |
|
* (integer length followed by encoding) |
|
* cert chain (integer count followed by certs; |
|
* for each cert: type UTF string, followed by integer |
|
* length, followed by encoding) |
|
* } |
|
* |
|
* or: |
|
* |
|
* { |
|
* tag=2 (big-endian integer) |
|
* alias (UTF string) |
|
* timestamp |
|
* cert (type UTF string, followed by integer length, |
|
* followed by encoding) |
|
* } |
|
* |
|
* or: |
|
* |
|
* { |
|
* tag=3 (big-endian integer) |
|
* alias (UTF string) |
|
* timestamp |
|
* sealed secret key (in serialized form) |
|
* } |
|
* |
|
* ended by a keyed SHA1 hash (bytes only) of |
|
* { password + whitener + preceding body } |
|
*/ |
|
|
|
|
|
if (password == null) { |
|
throw new IllegalArgumentException("password can't be null"); |
|
} |
|
|
|
byte[] encoded; |
|
|
|
MessageDigest md = getPreKeyedHash(password); |
|
DataOutputStream dos |
|
= new DataOutputStream(new DigestOutputStream(stream, md)); |
|
// NOTE: don't pass dos to oos at this point or it'll corrupt |
|
|
|
ObjectOutputStream oos = null; |
|
try { |
|
dos.writeInt(JCEKS_MAGIC); |
|
dos.writeInt(VERSION_2); |
|
|
|
dos.writeInt(entries.size()); |
|
|
|
Enumeration<String> e = entries.keys(); |
|
while (e.hasMoreElements()) { |
|
|
|
String alias = e.nextElement(); |
|
Object entry = entries.get(alias); |
|
|
|
if (entry instanceof PrivateKeyEntry) { |
|
|
|
PrivateKeyEntry pentry = (PrivateKeyEntry)entry; |
|
|
|
|
|
dos.writeInt(1); |
|
|
|
|
|
dos.writeUTF(alias); |
|
|
|
|
|
dos.writeLong(pentry.date.getTime()); |
|
|
|
|
|
dos.writeInt(pentry.protectedKey.length); |
|
dos.write(pentry.protectedKey); |
|
|
|
|
|
int chainLen; |
|
if (pentry.chain == null) { |
|
chainLen = 0; |
|
} else { |
|
chainLen = pentry.chain.length; |
|
} |
|
dos.writeInt(chainLen); |
|
for (int i = 0; i < chainLen; i++) { |
|
encoded = pentry.chain[i].getEncoded(); |
|
dos.writeUTF(pentry.chain[i].getType()); |
|
dos.writeInt(encoded.length); |
|
dos.write(encoded); |
|
} |
|
|
|
} else if (entry instanceof TrustedCertEntry) { |
|
|
|
|
|
dos.writeInt(2); |
|
|
|
|
|
dos.writeUTF(alias); |
|
|
|
|
|
dos.writeLong(((TrustedCertEntry)entry).date.getTime()); |
|
|
|
|
|
encoded = ((TrustedCertEntry)entry).cert.getEncoded(); |
|
dos.writeUTF(((TrustedCertEntry)entry).cert.getType()); |
|
dos.writeInt(encoded.length); |
|
dos.write(encoded); |
|
|
|
} else { |
|
|
|
|
|
dos.writeInt(3); |
|
|
|
|
|
dos.writeUTF(alias); |
|
|
|
|
|
dos.writeLong(((SecretKeyEntry)entry).date.getTime()); |
|
|
|
|
|
oos = new ObjectOutputStream(dos); |
|
oos.writeObject(((SecretKeyEntry)entry).sealedKey); |
|
// NOTE: don't close oos here since we are still |
|
// using dos!!! |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
byte digest[] = md.digest(); |
|
|
|
dos.write(digest); |
|
dos.flush(); |
|
} finally { |
|
if (oos != null) { |
|
oos.close(); |
|
} else { |
|
dos.close(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void engineLoad(InputStream stream, char[] password) |
|
throws IOException, NoSuchAlgorithmException, CertificateException |
|
{ |
|
synchronized(entries) { |
|
DataInputStream dis; |
|
MessageDigest md = null; |
|
CertificateFactory cf = null; |
|
Hashtable<String, CertificateFactory> cfs = null; |
|
ByteArrayInputStream bais = null; |
|
byte[] encoded = null; |
|
int trustedKeyCount = 0, privateKeyCount = 0, secretKeyCount = 0; |
|
|
|
if (stream == null) |
|
return; |
|
|
|
byte[] allData = IOUtils.readAllBytes(stream); |
|
int fullLength = allData.length; |
|
|
|
stream = new ByteArrayInputStream(allData); |
|
if (password != null) { |
|
md = getPreKeyedHash(password); |
|
dis = new DataInputStream(new DigestInputStream(stream, md)); |
|
} else { |
|
dis = new DataInputStream(stream); |
|
} |
|
// NOTE: don't pass dis to ois at this point or it'll fail to load |
|
|
|
ObjectInputStream ois = null; |
|
|
|
try { |
|
// Body format: see store method |
|
|
|
int xMagic = dis.readInt(); |
|
int xVersion = dis.readInt(); |
|
|
|
// Accept the following keystore implementations: |
|
// - JCEKS (this implementation), versions 1 and 2 |
|
// - JKS (Sun's keystore implementation in JDK 1.2), |
|
|
|
if (((xMagic != JCEKS_MAGIC) && (xMagic != JKS_MAGIC)) || |
|
((xVersion != VERSION_1) && (xVersion != VERSION_2))) { |
|
throw new IOException("Invalid keystore format"); |
|
} |
|
|
|
if (xVersion == VERSION_1) { |
|
cf = CertificateFactory.getInstance("X509"); |
|
} else { |
|
|
|
cfs = new Hashtable<String, CertificateFactory>(3); |
|
} |
|
|
|
entries.clear(); |
|
int count = dis.readInt(); |
|
|
|
for (int i = 0; i < count; i++) { |
|
int tag; |
|
String alias; |
|
|
|
tag = dis.readInt(); |
|
|
|
if (tag == 1) { |
|
privateKeyCount++; |
|
PrivateKeyEntry entry = new PrivateKeyEntry(); |
|
|
|
|
|
alias = dis.readUTF(); |
|
|
|
|
|
entry.date = new Date(dis.readLong()); |
|
|
|
|
|
entry.protectedKey = IOUtils.readExactlyNBytes(dis, dis.readInt()); |
|
|
|
|
|
int numOfCerts = dis.readInt(); |
|
List<Certificate> tmpCerts = new ArrayList<>(); |
|
for (int j = 0; j < numOfCerts; j++) { |
|
if (xVersion == 2) { |
|
// read the certificate type, and instantiate a |
|
// certificate factory of that type (reuse |
|
|
|
String certType = dis.readUTF(); |
|
if (cfs.containsKey(certType)) { |
|
|
|
cf = cfs.get(certType); |
|
} else { |
|
|
|
cf = CertificateFactory.getInstance( |
|
certType); |
|
// store the certificate factory so we can |
|
|
|
cfs.put(certType, cf); |
|
} |
|
} |
|
|
|
encoded = IOUtils.readExactlyNBytes(dis, dis.readInt()); |
|
bais = new ByteArrayInputStream(encoded); |
|
tmpCerts.add(cf.generateCertificate(bais)); |
|
} |
|
entry.chain = tmpCerts.toArray( |
|
new Certificate[numOfCerts]); |
|
|
|
|
|
entries.put(alias, entry); |
|
|
|
} else if (tag == 2) { |
|
trustedKeyCount++; |
|
TrustedCertEntry entry = new TrustedCertEntry(); |
|
|
|
|
|
alias = dis.readUTF(); |
|
|
|
|
|
entry.date = new Date(dis.readLong()); |
|
|
|
|
|
if (xVersion == 2) { |
|
// read the certificate type, and instantiate a |
|
// certificate factory of that type (reuse |
|
|
|
String certType = dis.readUTF(); |
|
if (cfs.containsKey(certType)) { |
|
|
|
cf = cfs.get(certType); |
|
} else { |
|
|
|
cf = CertificateFactory.getInstance(certType); |
|
// store the certificate factory so we can |
|
|
|
cfs.put(certType, cf); |
|
} |
|
} |
|
encoded = IOUtils.readExactlyNBytes(dis, dis.readInt()); |
|
bais = new ByteArrayInputStream(encoded); |
|
entry.cert = cf.generateCertificate(bais); |
|
|
|
|
|
entries.put(alias, entry); |
|
|
|
} else if (tag == 3) { |
|
secretKeyCount++; |
|
SecretKeyEntry entry = new SecretKeyEntry(); |
|
|
|
|
|
alias = dis.readUTF(); |
|
|
|
|
|
entry.date = new Date(dis.readLong()); |
|
|
|
|
|
try { |
|
ois = new ObjectInputStream(dis); |
|
final ObjectInputStream ois2 = ois; |
|
|
|
AccessController.doPrivileged( |
|
(PrivilegedAction<Void>)() -> { |
|
ObjectInputFilter.Config.setObjectInputFilter( |
|
ois2, new DeserializationChecker(fullLength)); |
|
return null; |
|
}); |
|
entry.sealedKey = (SealedObject)ois.readObject(); |
|
entry.maxLength = fullLength; |
|
// NOTE: don't close ois here since we are still |
|
// using dis!!! |
|
} catch (ClassNotFoundException cnfe) { |
|
throw new IOException(cnfe.getMessage()); |
|
} catch (InvalidClassException ice) { |
|
throw new IOException("Invalid secret key format"); |
|
} |
|
|
|
|
|
entries.put(alias, entry); |
|
|
|
} else { |
|
throw new IOException("Unrecognized keystore entry: " + |
|
tag); |
|
} |
|
} |
|
|
|
if (debug != null) { |
|
debug.println("JceKeyStore load: private key count: " + |
|
privateKeyCount + ". trusted key count: " + |
|
trustedKeyCount + ". secret key count: " + |
|
secretKeyCount); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (password != null) { |
|
byte[] computed = md.digest(); |
|
byte[] actual = IOUtils.readExactlyNBytes(dis, computed.length); |
|
if (!MessageDigest.isEqual(computed, actual)) { |
|
throw new IOException( |
|
"Keystore was tampered with, or " |
|
+ "password was incorrect", |
|
new UnrecoverableKeyException( |
|
"Password verification failed")); |
|
} |
|
} |
|
} finally { |
|
if (ois != null) { |
|
ois.close(); |
|
} else { |
|
dis.close(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private MessageDigest getPreKeyedHash(char[] password) |
|
throws NoSuchAlgorithmException, UnsupportedEncodingException { |
|
int i, j; |
|
|
|
MessageDigest md = MessageDigest.getInstance("SHA"); |
|
byte[] passwdBytes = new byte[password.length * 2]; |
|
for (i=0, j=0; i<password.length; i++) { |
|
passwdBytes[j++] = (byte)(password[i] >> 8); |
|
passwdBytes[j++] = (byte)password[i]; |
|
} |
|
md.update(passwdBytes); |
|
for (i=0; i<passwdBytes.length; i++) |
|
passwdBytes[i] = 0; |
|
md.update("Mighty Aphrodite".getBytes("UTF8")); |
|
return md; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static class DeserializationChecker implements ObjectInputFilter { |
|
|
|
// Full length of keystore, anything inside a SecretKeyEntry should not |
|
|
|
private final int fullLength; |
|
|
|
public DeserializationChecker(int fullLength) { |
|
this.fullLength = fullLength; |
|
} |
|
|
|
@Override |
|
public ObjectInputFilter.Status |
|
checkInput(ObjectInputFilter.FilterInfo info) { |
|
|
|
if (info.arrayLength() > fullLength) { |
|
return Status.REJECTED; |
|
} |
|
|
|
Class<?> clazz = info.serialClass(); |
|
switch((int)info.depth()) { |
|
case 1: |
|
if (clazz != SealedObjectForKeyProtector.class) { |
|
return Status.REJECTED; |
|
} |
|
break; |
|
case 2: |
|
if (clazz != null && clazz != SealedObject.class |
|
&& clazz != byte[].class) { |
|
return Status.REJECTED; |
|
} |
|
break; |
|
default: |
|
if (clazz != null && clazz != Object.class) { |
|
return Status.REJECTED; |
|
} |
|
break; |
|
} |
|
|
|
|
|
ObjectInputFilter defaultFilter = |
|
ObjectInputFilter.Config.getSerialFilter(); |
|
if (defaultFilter != null) { |
|
return defaultFilter.checkInput(info); |
|
} |
|
|
|
return Status.UNDECIDED; |
|
} |
|
} |
|
} |