2

Java 使用Cipher该类作为特定密码实现的抽象。在我的项目中,我主要使用对称分组密码(AES、Twofish、3DES、...),并且我正在寻找一种方法来动态创建/初始化任何可能的对称分组密码(使用 XML 配置),因为我会喜欢使加密可配置。

例子: <transformer type="cipher" cipher="AES/GCM/NoPadding" keysize="256" iv="true" unlimitedstrength="true" />

将被翻译成:

// Create secretKey using 'keysize' ...

if (encryption.isUnlimitedCrypto()) {
    Encryption.enableUnlimitedCrypto();
}

Cipher cipher = Cipher.getInstance(encryption.getCipherStr(), Encryption.PROVIDER);

if (encryption.isIvNeeded()) {
    byte[] iv = ... 
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
}
else {
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
}

我的问题是:在 Java 中实例化任何对称分组密码需要哪些参数?

参数(已确定):

  • 密码字符串:Cipher.getInstance(..) 的字符串,例如 AES/CBC/PKCSPadding
  • 密钥大小:密钥生成(或 PBE 密钥派生函数)的密钥大小
  • IV 需要:指示是否需要 IV,例如 CBC 和 GCM 模式为“true”,但 ECB 不需要
  • IV 大小:表示 IV 的大小(现在我假设 IV 大小 = 密钥大小,对吗?)
  • 需要无限强度:指示是否需要启用无限强度策略文件
  • 其他的?

原始来源/项目:

4

1 回答 1

2

ECB需要 IV 之外的所有模式。IV 将始终等于密码的块大小(AES 为 16 字节,3DES 为 8 字节)。ECB当使用相同的密钥加密超过 1 个明文块时,模式是不安全的,如果您希望确保机密性,则不应允许该模式。

使用的算法将决定所需的密钥大小。例如,AES 需要 128、192 或 256 如果使用 Java 的标准加密 API,则需要为 192 和 256 安装无限强度策略。无限强度策略不是您可以使用代码切换的东西,它必须安装在最终用户 JRE 中。

如果您关心传输中的数据安全(并且该项目有真正的安全需求),我不能强烈建议您使用 SSL/TLS 来代替。创建一个安全的密码系统是困难的,并且批量加密(即对称密码,如 AES、3DES)本身不足以确保安全。您还需要强大的加密随机数据源、安全的密钥交换过程和完整性验证。如果不确保通常通过使用 MAC 功能提供的完整性,则很难提供机密性。在实施安全加密系统时需要避免所有陷阱,例如确保对密码和 MAC 使用不同的密钥、正确验证 MAC 以免创建定时攻击向量、使用适当的随机生成器、确保完整性以免创建填充预言机等

正如您所看到的,保护传输的数据有很多活动部件,从头开始可能会导致由错误选择或错误配置的加密原语引入的漏洞。这就是为什么经常推荐使用 TLS 的原因。

下面是一个由两个套接字建立的匿名(无身份验证)TLS 会话的示例。这个例子并不安全,因为双方都没有对另一方进行身份验证,但是建立了机密性和完整性。我在示例中使用这个不安全的密码套件的原因是很容易演示 TLS 的使用,而无需进入密钥库和信任库(用于身份验证部分)

正在使用的密码套件是TLS_ECDH_anon_WITH_AES_128_CBC_SHA,默认情况下通常不会启用,因为前面提到的缺少身份验证。下面我分解这个密码套件

  • TLS - 此密码套件由 TLS 标准引入。
  • ECDH_anon - Eliptic Cuve Diffie-Hellman 算法用于密钥协商,但密钥协商未经过身份验证。
  • AES_128_CBC - 在密码块链接模式下具有 128 位密钥长度的高级加密标准用于批量加密。
  • SHA - 安全散列算法用于确保加密数据的完整性。

示例如下。

package com.stackoverflow._19505091;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

public class AnonTLSExample {

    public static void main(String[] args) throws Exception {

        /* No certs for this example so we are using ECDH_anon exchange. */
        String[] cipherSuites = {"TLS_ECDH_anon_WITH_AES_128_CBC_SHA"};
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");

        /* No certificates, use default secure random source.
         * If we were using authentication (and you should in a real
         * system), this is where we would load 
         * keystores and truststores. */
        sslContext.init(null, null, null);

        /* Create server socket. */
        SSLServerSocket ss = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(12345);
        ss.setEnabledCipherSuites(cipherSuites);

        /*
         * Normally when authentication is used only the client authenticates
         * the server. If you want the server to also authenticate the client
         * set this to true. This will establish bidirectional trust in the session.
         */
        ss.setWantClientAuth(false);

        /* Start server thread. */
        new Thread(new Server(ss), "ServerThread").start();

        /* Create client socket. */
        SSLSocket s = (SSLSocket) sslContext.getSocketFactory().createSocket();
        s.setEnabledCipherSuites(cipherSuites);

        /* Connect to server. */
        System.out.println("Client: Connecting...");
        s.connect(new InetSocketAddress("127.0.0.1", 12345));
        System.out.println("Client: Connected");

        /* Print out some TLS info for this connection. */
        SSLSession session = s.getSession();
        System.out.println("Client: Session secured with P: " + session.getProtocol() + " CS: " + session.getCipherSuite());

        /* Send the secret message. */
        DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        String message = "Secret Message.";
        System.out.println("Client: Sending: " + message);
        dos.writeUTF(message);

        /* Wait for server to close stream. */
        System.out.println("Client: Waiting for server to close...");
        s.getInputStream().read();

        /* Close client socket. */
        s.close();
        System.out.println("Client: Done.");
    }


}

class Server implements Runnable {

    private final ServerSocket ss;

    public Server(ServerSocket ss){
        this.ss = ss;
    }

    @Override
    public void run() {
        try{
         /* Wait for client to connect. */
         System.out.println("Server: Waiting for connection...");
         Socket s = ss.accept();
         System.out.println("Server: Connected.");

         /* Read secret message. */
         DataInputStream dis = new DataInputStream(s.getInputStream());
         String message = dis.readUTF();
         System.out.println("Server: Received Message: " + message);

         /* Close our sockets. */
         s.close();
         ss.close();
         System.out.println("Server: Done.");
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}
于 2013-10-22T03:00:15.727 回答