blob: 71570807e4aab2c97640aaa820cdf28b4d88d6ed [file] [log] [blame]
/*
* Copyright (c) 2013, 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 6263419
* @summary No way to clean the memory for a java.security.Key
*/
import java.security.*;
import java.util.*;
import javax.crypto.*;
import javax.security.auth.Destroyable;
import javax.security.auth.DestroyFailedException;
public class KeyDestructionTest {
public static void main(String[] args) throws Exception {
KeyPair keypair = generateKeyPair("RSA", 1024);
// Check keys that support and have implemented key destruction
testKeyDestruction(new MyDestroyableSecretKey());
testKeyDestruction(new MyDestroyablePrivateKey());
// Check keys that support but have not implemented key destruction
testNoKeyDestruction(generateSecretKey("AES", 128));
testNoKeyDestruction(keypair.getPrivate());
// Check keys that do not support key destruction
try {
testKeyDestruction(keypair.getPublic());
} catch (UnsupportedOperationException uoe) {
// not an error
System.out.println(keypair.getPublic().getClass().getName() +
" keys do not support key destruction");
}
System.out.println("PASSED.");
}
// Check the behaviour of a key that implements key destruction
private static void testKeyDestruction(Key key) throws Exception {
String klass = key.getClass().getName();
boolean hasUsable = key instanceof Usable;
try {
key.getAlgorithm();
key.getFormat();
if (allZero(key.getEncoded())) {
throw new Exception("error: key destroyed prematurely");
}
} catch (IllegalStateException ise) {
throw new Exception("error: unexpected ISE", ise);
}
if (hasUsable) {
((Usable) key).useKey();
}
destroyKey(key);
try {
if (hasUsable) {
((Usable) key).useKey();
}
} catch (IllegalStateException ise) {
// not an error
}
try {
key.getAlgorithm();
key.getFormat();
if (!allZero(key.getEncoded())) {
throw new Exception("error: key destroyed incorrectly");
}
} catch (IllegalStateException ise) {
// not an error
}
System.out.println("A " + klass +
" key has been successfully destroyed");
}
// Check the behaviour of a key that does not implement key destruction
private static void testNoKeyDestruction(Destroyable key)
throws Exception {
String klass = key.getClass().getName();
if (key.isDestroyed()) {
throw new Exception("error: a " + klass +
" key has been unexpectedly destroyed");
}
try {
key.destroy();
} catch (DestroyFailedException dfe) {
// not an error
if (key.isDestroyed()) {
throw new Exception("error: a " + klass +
" key has been unexpectedly destroyed");
}
System.out.println(klass + " keys are not destroyable");
return;
}
throw new Exception("error: key may been unexpectedly destroyed");
}
private static KeyPair generateKeyPair(String algorithm, int size)
throws NoSuchAlgorithmException {
KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithm);
generator.initialize(size);
return generator.genKeyPair();
}
private static SecretKey generateSecretKey(String algorithm, int size)
throws NoSuchAlgorithmException {
KeyGenerator generator = KeyGenerator.getInstance(algorithm);
generator.init(size);
return generator.generateKey();
}
private static void destroyKey(Key key) throws Exception {
String klass = key.getClass().getName();
if (!(key instanceof Destroyable)) {
throw new UnsupportedOperationException();
}
Destroyable dKey = (Destroyable) key;
if (dKey.isDestroyed()) {
throw new Exception("error: a " + klass +
" key has already been destroyed");
}
dKey.destroy();
if (!dKey.isDestroyed()) {
throw new Exception("error: a " + klass +
" key has NOT been destroyed");
}
}
private static boolean allZero(byte[] bytes) {
int count = 0;
for (byte b : bytes) {
if (b == 0x00) {
count++;
}
}
return (bytes.length == count);
}
}
interface Usable {
public void useKey();
}
class MyDestroyableSecretKey implements SecretKey, Usable {
private byte[] encoded = new byte[]{0x0F, 0x1F, 0x2F, 0x3F}; // non-zero
private boolean isDestroyed = false;
@Override
public void useKey() {
if (isDestroyed) {
throw new IllegalStateException();
}
}
@Override
public String getAlgorithm() {
return "MyDestroyableSecretKey algorithm";
}
@Override
public String getFormat() {
return "MyDestroyableSecretKey format";
}
@Override
public byte[] getEncoded() {
return this.encoded;
}
@Override
public void destroy() throws DestroyFailedException {
if (!this.isDestroyed) {
Arrays.fill(encoded, (byte) 0);
this.isDestroyed = true;
}
}
@Override
public boolean isDestroyed() {
return this.isDestroyed;
}
}
class MyDestroyablePrivateKey implements PrivateKey, Usable {
private byte[] encoded = new byte[]{0x4F, 0x5F, 0x6F, 0x7F}; // non-zero
private boolean isDestroyed = false;
@Override
public void useKey() {
if (isDestroyed) {
throw new IllegalStateException();
}
}
@Override
public String getAlgorithm() {
return "MyDestroyablePrivateKey algorithm";
}
@Override
public String getFormat() {
return "MyDestroyablePrivateKey format";
}
@Override
public byte[] getEncoded() {
return this.encoded;
}
@Override
public void destroy() throws DestroyFailedException {
if (!this.isDestroyed) {
Arrays.fill(encoded, (byte) 0);
this.isDestroyed = true;
}
}
@Override
public boolean isDestroyed() {
return this.isDestroyed;
}
}