使用Java或Javascrip对称和非对称加密 - Airtel X Labs


在数字化的过程中,我们已经构建了很多应用程序。其中一些是供内部使用的,其中许多是面向消费者的应用程序。无论该应用程序是用于管理访问我们的Airtel办事处的访问者的简单Android应用程序(KIOSK),还是用于连接所有Airtel解决方案和服务在一个集线器下的My Airtel应用程序,开发人员有责任了解网络安全性和保护这些应用程序免受网络攻击。
一旦TLS启动并运行,我们不会坐视不管,因为95%的HTTPS服务器容易受到琐碎(Man In The Middle Attack)攻击。在本文中,我们不会讨论防止中间人攻击的不同方法。他们已经有太多了。相反,我将尝试简要介绍我们在从客户端通过线路传输任何信息或有效负载时使用的不同加密技术。这里,客户端可以是Android或IOs平台或浏览器,我们在服务器上运行JVM。作为Java开发人员,讨论将主要围绕Java进行。

加密类型
通过电汇传输机密数据(如纯文本密码或任何敏感密码)始终容易受到安全保护。有时候,即使是TLS也无济于事,因为对于TLSv1.0的继任者来说,BEAST攻击Lucky13攻击是明显的。因此,对于启用TLS的系统,甚至必须加密敏感信息。Java为此提供了多种加密算法。
有两种基本类型的加密 - 非对称和对称加密。非对称加密使用两个不同的密钥作为加密和解密过程的公钥和私钥。在非对称加密中,我们使用公钥加密敏感信息,并使用匹配的私钥对其进行解密。当涉及2个不同的端点时,如VPN客户端和服务器,SSH等,主要使用非对称加密。
对称加密使用称为私钥或密钥的单个密钥来加密和解密敏感信息。与非对称加密相比,这种类型的加密非常快,并且在诸如数据库系统的系统中使用。对称加密的一些例子是Twofish,Blowfish,3 DES,AES。

AES加密
AES代表高级加密系统,它是一种对称加密算法。它是由美国国家标准与技术研究院(NIST)在2001年建立的电子数据加密规范.AES引擎需要明文和密钥进行加密,并且需要相同的密钥才能再次解密它。密钥输入可以是128位或192位或256位,并且生成相应的密文位。
AES提供两种分组密码加密模式 - ECB和CBC模式。ECB(电子代码簿)是最简单的加密模式,不需要IV进行加密。输入的纯文本将被分成块,每个块将使用提供的密钥加密,因此相同的纯文本块被加密成相同的密文块。如果使用相同的密钥加密所有纯文本,并且如果攻击者找到此密钥,则可以以类似的方式解密所有密码。因此,ECB模式在语义上不安全。
强烈建议使用CBC(密码块链接)模式,它需要IV才能使每条消息都是唯一的。使用IV可确保创建独特的密文,即使使用相同的密钥对相同的明文进行多次加密也是如此。如果未输入IV,则此处将使用默认值作为CBC模式。
无论密钥长度是256位,192位还是128位,AES算法都具有128位块大小。当对称密码模式需要IV时,IV的长度必须等于密码的块大小。因此,您必须始终使用带有AES的128位(16字节)的IV。要快速演示,您可以访问这个免费的在线AES工具
以下是java中执行AES加密的示例程序。在这里,我们使用带CBC模式的AES来加密消息,因为ECB模式在语义上不安全。我们可以使用salt和迭代来进一步改进加密过程。

private static final String key = “aesEncryptionKey”;
private static final String initVector = “encryptionIntVec”;
public static String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(“UTF-8”));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(“UTF-8”), “AES”);
Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5PADDING”);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

以下是解密密码的相反过程。代码不言自明。

public static String decrypt(String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(“UTF-8”));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(“UTF-8”), “AES”);
Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5PADDING”);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

RSA加密
RSA(Rivest-Shamir-Adleman)加密加密是非对称加密,它使用两个不同的密钥作为公钥和私钥。在这里,您可以使用公钥加密敏感信息,并使用匹配的私钥对其进行解密。当涉及2个不同的端点时,如VPN客户端和服务器,SSH等,主要使用非对称加密。

生成RSA公钥 - 私钥
我们可以使用工厂方法使用KeyPairGenerator生成这些键。出于演示目的,我们使用的密钥大小为1024.默认情况下,私钥以PKCS#8格式生成,公钥以X.509格式生成。生成这些密钥后,您可以将这些密钥写入文件并与客户端共享包含公钥的文件。请记住,公钥在文本文件中以X.509格式写入。此外,我们可以执行base64编码以使其可读并与客户端共享字符串。
在下面的示例中,我们将对公钥和私钥进行Base64编码,以简化这些密钥的共享。
RSAKeyPairGenerator.java

public class RSAKeyPairGenerator {
private PrivateKey privateKey;
private PublicKey publicKey;
public RSAKeyPairGenerator() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(“RSA”);
keyGen.initialize(1024);
KeyPair pair = keyGen.generateKeyPair();
this.privateKey = pair.getPrivate();
this.publicKey = pair.getPublic();
}
public void writeToFile(String path, byte[] key) throws IOException {
File f = new File(path);
f.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(f);
fos.write(key);
fos.flush();
fos.close();
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public PublicKey getPublicKey() {
return publicKey;
}
public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
RSAKeyPairGenerator keyPairGenerator = new RSAKeyPairGenerator();
keyPairGenerator.writeToFile(“RSA/publicKey”, keyPairGenerator.getPublicKey().getEncoded());
keyPairGenerator.writeToFile(“RSA/privateKey”, keyPairGenerator.getPrivateKey().getEncoded());
System.out.println(Base64.getEncoder().encodeToString(keyPairGenerator.getPublicKey().getEncoded()));
System.out.println(Base64.getEncoder().encodeToString(keyPairGenerator.getPrivateKey().getEncoded()));
}
}

Java中的RSA加密
现在我们有了RSAUtil.java,它具有为Java中的RSA加密和解密定义的方法。我们先讨论加密。如上所述,生成的公钥是X.509格式,我们使用公钥进行加密。因此,我们需要X509EncodedKeySpec类将其再次转换为RSA公钥。请记住,我们有base64编码的公钥。因此,首先让我们首先解码Base64并生成公钥。
RSAUtil.java

public static PublicKey getPublicKey(String base64PublicKey){
PublicKey publicKey = null;
try{
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance(“RSA”);
publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return publicKey;
}

现在,我们有一个简单的方法encrypt(),它接受字符串加密和Base64编码的RSA密钥进行加密。这里,getPublicKey()是我们在上面定义的方法。

public static byte[] encrypt(String data, String publicKey) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
Cipher cipher = Cipher.getInstance(“RSA/ECB/PKCS1Padding”);
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));
return cipher.doFinal(data.getBytes());
}

Java中的RSA解密
对于解密,我们将使用私钥,我们在上面讨论过私钥是以PKCS#8格式生成的。因此,以下是使用PKCS8EncodedKeySpec从base64编码的字符串生成私钥的代码。

public staticPrivateKey getPrivateKey(String base64PrivateKey){
PrivateKey privateKey = null;
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes()));
KeyFactory keyFactory = null;
try {
keyFactory = KeyFactory.getInstance(“RSA”);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
privateKey = keyFactory.generatePrivate(keySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return privateKey;
}

以下是解密方法,它接受RSA加密字符串和Base64编码的RSA私钥进行解密。

public static String decrypt(String data, String base64PrivateKey) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
return decrypt(Base64.getDecoder().decode(data.getBytes()), getPrivateKey(base64PrivateKey));
}

将RSA和AES放在一起
每增加一倍的RSA密钥长度,解密速度就会慢6到7倍。因此,当存在用于RSA加密的大消息时,性能会降低。在这种情况下,我们首先对消息进行AES加密,并使用用于AES加密的密钥进行RSA加密并发送到服务器。Javascript或Android客户端可以使用此技术将敏感有效负载发送到服务器。