2011-08-25 31 views
6

Mã java sau đây có đủ để xóa khóa bí mật trong bộ nhớ (thiết lập tất cả giá trị byte của nó thành 0) không?Làm cách nào để tôi không sử dụng khóa bí mật trong java?

zerorize(SecretKey key) 
{ 
    byte[] rawKey = key.getEncoded(); 
    Arrays.fill(rawKey, (byte) 0); 
} 

Phương thức getEncoded trả về bản sao hoặc tham chiếu đến khóa thực tế? Nếu một bản sao được trả lại, thì làm cách nào tôi có thể xóa khóa bí mật như một biện pháp bảo mật?

Trả lời

-1

Nói cách khác, phương thức getEncoded trả lại bản sao hoặc tham chiếu đến khóa thực tế?

key.getEncoded() sẽ trả về một tài liệu tham khảo vào một mảng.

Nếu nội dung của Điều quan trọng là loại bỏ khi bạn làm Array.fill phụ thuộc vào việc hay không chính là hậu thuẫn bởi mảng được trả về. Với tài liệu, dường như với tôi như mã hóa của khóa là một biểu diễn khác của khóa, tức là khóa đó là không phải được hỗ trợ bởi mảng được trả về.

Thật dễ dàng để tìm hiểu mặc dù. Hãy thử như sau:

byte[] rawKey = key.getEncoded(); 
Arrays.fill(rawKey, (byte) 0); 

byte[] again = key.getEncoded(); 
Log.d(Arrays.equals(rawKey, again)); 

Nếu đầu ra là false, bạn biết rằng chìa khóa vẫn còn được lưu giữ trong SecretKey.

-1

Ngoại trừ các giá trị nguyên thủy, mọi thứ khác trong Java luôn được truyền theo tham chiếu, bao gồm mảng, vì vậy có, bạn đang xóa chính xác mảng byte đã cho. Tuy nhiên, lớp SecretKey có thể vẫn giữ dữ liệu cần thiết để tạo mảng byte đó, bao gồm cả một bản sao khác của mảng byte đã cho, vì vậy bạn cũng nên điều tra cách xóa dữ liệu đó.

+4

-1: * mọi thứ khác trong Java luôn được chuyển bằng tham chiếu * - Nooo, Java là * luôn * truyền theo giá trị! Lý do bạn không thể vượt qua * một đối tượng * theo giá trị, là bởi vì không có biến nào có thể chứa một đối tượng ở vị trí đầu tiên! – aioobe

+0

@aioobe .. bạn chắc chắn chúng ta đang nói về cùng một Java? int được truyền theo giá trị, boolean được truyền theo giá trị, Integer là một tham chiếu, cũng như bất kỳ đối tượng, mảng, vv ... Java chuyển "một giá trị", mà thực sự là "tham chiếu" tới một đối tượng, do đó tài liệu tham khảo. –

+0

@ Joachim .. Tôi đã đề cập đến "mảng byte đã cho", tuy nhiên tiếp tục làm rõ rằng có thể có một bản sao khác. –

0

Tôi chắc chắn rằng việc xóa rawKey sẽ không ảnh hưởng đến dữ liệu trong key.

Tôi không nghĩ rằng có một cách chung để xóa dữ liệu trong SecretKey. Các lớp thực hiện cụ thể có thể cung cấp cho điều đó, nhưng tôi không biết bất kỳ điều gì có thể xảy ra. Trong Android, nguy cơ rời khỏi dữ liệu không rõ ràng là rất thấp. Mỗi ứng dụng chạy trong quá trình riêng của nó và bộ nhớ của nó không hiển thị từ bên ngoài.

Tôi cho rằng có một kịch bản tấn công trong đó một quá trình được quản lý gốc có thể chụp nhanh bộ nhớ và gửi chúng tới một số siêu máy tính ở đâu đó để phân tích, hy vọng phát hiện ra khóa bí mật của ai đó. Nhưng tôi đã không bao giờ nghe nói về một cuộc tấn công như vậy, và nó tấn công tôi như là không cạnh tranh với những cách khác để đạt được quyền truy cập vào một hệ thống. Có lý do nào khiến bạn lo lắng về lỗ hổng giả định cụ thể này không?

6

Trước khi cố gắng xóa khóa, bạn nên kiểm tra trước nếu triển khai giao diện SecretKey cũng thực hiện giao diện javax.security.auth.Destroyable. Nếu vậy, thích điều đó tất nhiên.

+0

Chỉ hoạt động ở mức 1.8+ và thường chỉ ném DestroyFailedException –

-2

Lấy một chiến thuật hơi khác nhau, một khi bạn đã xác định được khu vực chính xác của bộ nhớ để ghi đè lên, bạn có thể muốn làm điều đó nhiều hơn một lần:

zerorize(SecretKey key) 
{ 
    byte[] rawKey = key.getEncoded(); 
    Arrays.fill(rawKey, (byte) 0xFF); 
    Arrays.fill(rawKey, (byte) 0xAA); 
    Arrays.fill(rawKey, (byte) 0x55); 
    Arrays.fill(rawKey, (byte) 0x00); 
} 
0

Tùy thuộc vào công nghệ tạo năng lượng cho thu gom rác, bất kỳ đơn đối tượng có thể được di chuyển (tức là sao chép) trong bộ nhớ vật lý bất cứ lúc nào, vì vậy bạn không thể chắc chắn rằng bạn sẽ thực sự phá hủy khóa bằng cách zeroing một mảng - giả sử rằng bạn có thể truy cập "mảng" giữ khóa, và không phải là sao chép chúng.

Nói cách ngắn hơn: nếu mô hình bảo mật và ngữ cảnh gọi cho các phím zeroing, thì bạn không nên sử dụng Java ở tất cả (hoặc bất kỳ thứ gì ngoài C và assembly).

+0

Nhưng nếu bạn phải sử dụng Java, hãy loại bỏ nó nhanh chóng, trước khi GC có khả năng đóng gói lại dữ liệu hoặc hệ điều hành di chuyển nó sang hoán đổi, v.v ... –

3

getEncoded() dường như chủ yếu là trả về một bản sao của khóa (từ nguồn Oracle 1.6 của ví dụ javax.security.auth.kerberos):

public final byte[] getEncoded() { 
    if (destroyed) 
    throw new IllegalStateException("This key is no longer valid"); 
    return (byte[])keyBytes.clone(); 
} 

do đó lau dữ liệu trở lại không xóa tất cả các bản sao của khóa từ bộ nhớ.

Cách duy nhất để lau phím từ SecretKey là để cast nó để javax.security.auth.Destroyable nếu nó thực hiện giao diện và gọi destroy() phương pháp:

public void destroy() throws DestroyFailedException { 
    if (!destroyed) { 
    destroyed = true; 
    Arrays.fill(keyBytes, (byte) 0); 
    } 
} 

Lạ lùng dường như tất cả thực hiện chính không thực hiện javax.security.auth.Destroyable . com.sun.crypto.provider.DESedeKey cũng không sử dụng javax.crypto.spec.SecretKeySpec được sử dụng cho AES. Cả hai cách triển khai chính này cũng sao chép khóa trong phương thức getEncoded. Vì vậy, có vẻ như cho các thuật toán rất phổ biến 3DES và AES chúng tôi không có cách nào để xóa bộ nhớ cho khóa bí mật?

1

GetEncoded trả về một bản sao của khóa bí mật (do đó việc xóa không ảnh hưởng đến dữ liệu khóa bí mật), và phá hủy theo mặc định ném DestroyFailedException mà tệ hơn vô dụng. Nó cũng chỉ có sẵn trong 1,8+ vì vậy Android là ra khỏi may mắn. Đây là một hack sử dụng nội tâm để (1) gọi phá hủy nếu có sẵn và không ném một ngoại lệ, nếu không (2) không phải là dữ liệu quan trọng và thiết lập các tham chiếu đến null.

package kiss.cipher; 

import java.lang.reflect.Field; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import java.util.Arrays; 

import javax.crypto.spec.SecretKeySpec; 

/** 
* Created by wmacevoy on 10/12/16. 
*/ 
public class CloseableKey implements AutoCloseable { 

    // forward portable to JDK 1.8 to destroy keys 
    // but usable in older JDK's 
    static final Method DESTROY; 
    static final Field KEY; 

    static { 
     Method _destroy = null; 

     Field _key = null; 
     try { 
      Method destroy = SecretKeySpec.class.getMethod("destroy"); 
      SecretKeySpec key = new SecretKeySpec(new byte[16], "AES"); 
      destroy.invoke(key); 
      _destroy = destroy; 
     } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { 
     } 

     try { 
      _key = SecretKeySpec.class.getDeclaredField("key"); 
      _key.setAccessible(true); 
     } catch (NoSuchFieldException | SecurityException ex) { 
     } 

     DESTROY = _destroy; 
     KEY = _key; 
    } 

    static void close(SecretKeySpec secretKeySpec) { 
     if (secretKeySpec != null) { 
      if (DESTROY != null) { 
       try { 
        DESTROY.invoke(secretKeySpec); 
       } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { 
        throw new IllegalStateException("inconceivable: " + ex); 
       } 
      } else if (KEY != null) { 
       try { 
        byte[] key = (byte[]) KEY.get(secretKeySpec); 
        Arrays.fill(key, (byte) 0); 
        KEY.set(secretKeySpec, null); 
       } catch (IllegalAccessException | IllegalArgumentException ex) { 
        throw new IllegalStateException("inconceivable: " + ex); 
       } 
      } 
     } 
    } 

    public final SecretKeySpec secretKeySpec; 

    CloseableKey(SecretKeySpec _secretKeySpec) { 

     secretKeySpec = _secretKeySpec; 
    } 

    @Override 
    public void close() { 
     close(secretKeySpec); 
    } 
} 

Cách sử dụng điều này cũng giống như

try (CloseableKey key = 
     new CloseableKey(new SecretKeySpec(data, 0, 16, "AES"))) { 
    aesecb.init(Cipher.ENCRYPT_MODE, key.secretKeySpec); 
} 

tôi sử dụng giao diện có thể đóng vì thể bị phá hủy chỉ là một tính năng 1.8+. Phiên bản này hoạt động trên 1,7+ và khá hiệu quả (nó thực hiện một thử nghiệm phá hủy trên một chìa khóa để quyết định sử dụng lại nó).

+0

Đây là một hack, và GC có thể đóng gói lại bộ nhớ hoặc hệ điều hành di chuyển dữ liệu để trao đổi mà có thể rò rỉ dữ liệu quan trọng. Đóng khóa càng sớm càng tốt để giảm thiểu khả năng bị rò rỉ do hiệu ứng phụ GC hoặc OS. –

Các vấn đề liên quan