除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();
}
}
}