提交 7741730c 编写于 作者: V vinnie

6263419: No way to clean the memory for a java.security.Key

Reviewed-by: mullan
上级 cb5188a2
/*
* Copyright (c) 1996, 2001, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 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
......@@ -26,13 +26,22 @@
package java.security;
/**
* <p>A private key. This interface contains no methods or constants.
* It merely serves to group (and provide type safety for) all private key
* interfaces.
*
* A private key.
* The purpose of this interface is to group (and provide type safety
* for) all private key interfaces.
* <p>
* Note: The specialized private key interfaces extend this interface.
* See, for example, the DSAPrivateKey interface in
* <code>java.security.interfaces</code>.
* See, for example, the {@code DSAPrivateKey} interface in
* {@link java.security.interfaces}.
* <p>
* Implementations should override the default {@code destroy} and
* {@code isDestroyed} methods from the
* {@link javax.security.auth.Destroyable} interface to enable
* sensitive key information to be destroyed, cleared, or in the case
* where such information is immutable, unreferenced.
* Finally, since {@code PrivateKey} is {@code Serializable}, implementations
* should also override {@link java.io.ObjectOutputStream.writeObject}
* to prevent keys that have been destroyed from being serialized.
*
* @see Key
* @see PublicKey
......@@ -46,7 +55,8 @@ package java.security;
* @author Josh Bloch
*/
public interface PrivateKey extends Key {
public interface PrivateKey extends Key, javax.security.auth.Destroyable {
// Declare serialVersionUID to be compatible with JDK1.1
/**
* The class fingerprint that is set to indicate serialization
......
/*
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
......@@ -27,20 +27,27 @@ package javax.crypto;
/**
* A secret (symmetric) key.
*
* <p>This interface contains no methods or constants.
* Its only purpose is to group (and provide type safety for) secret keys.
*
* <p>Provider implementations of this interface must overwrite the
* <code>equals</code> and <code>hashCode</code> methods inherited from
* <code>java.lang.Object</code>, so that secret keys are compared based on
* The purpose of this interface is to group (and provide type safety
* for) all secret key interfaces.
* <p>
* Provider implementations of this interface must overwrite the
* {@code equals} and {@code hashCode} methods inherited from
* {@link java.lang.Object}, so that secret keys are compared based on
* their underlying key material and not based on reference.
* Implementations should override the default {@code destroy} and
* {@code isDestroyed} methods from the
* {@link javax.security.auth.Destroyable} interface to enable
* sensitive key information to be destroyed, cleared, or in the case
* where such information is immutable, unreferenced.
* Finally, since {@code SecretKey} is {@code Serializable}, implementations
* should also override {@link java.io.ObjectOutputStream.writeObject}
* to prevent keys that have been destroyed from being serialized.
*
* <p>Keys that implement this interface return the string <code>RAW</code>
* as their encoding format (see <code>getFormat</code>), and return the
* raw key bytes as the result of a <code>getEncoded</code> method call. (The
* <code>getFormat</code> and <code>getEncoded</code> methods are inherited
* from the <code>java.security.Key</code> parent interface.)
* <p>Keys that implement this interface return the string {@code RAW}
* as their encoding format (see {@code getFormat}), and return the
* raw key bytes as the result of a {@code getEncoded} method call. (The
* {@code getFormat} and {@code getEncoded} methods are inherited
* from the {@link java.security.Key} parent interface.)
*
* @author Jan Luehe
*
......@@ -49,7 +56,9 @@ package javax.crypto;
* @since 1.4
*/
public interface SecretKey extends java.security.Key {
public interface SecretKey extends
java.security.Key, javax.security.auth.Destroyable {
/**
* The class fingerprint that is set to indicate serialization
* compatibility since J2SE 1.4.
......
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 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
......@@ -42,21 +42,27 @@ public interface Destroyable {
* <code>IllegalStateException</code> being thrown.
*
* <p>
* The default implementation throws {@code DestroyFailedException}.
*
* @exception DestroyFailedException if the destroy operation fails. <p>
*
* @exception SecurityException if the caller does not have permission
* to destroy this <code>Object</code>.
*/
void destroy() throws DestroyFailedException;
public default void destroy() throws DestroyFailedException {
throw new DestroyFailedException();
}
/**
* Determine if this <code>Object</code> has been destroyed.
*
* <p>
* The default implementation returns false.
*
* @return true if this <code>Object</code> has been destroyed,
* false otherwise.
*/
boolean isDestroyed();
public default boolean isDestroyed() {
return false;
}
}
/*
* 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;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册