我需要用Java (在安卓上)和SJCL来加密和解密数据(我可以合理地切换到另一个JS加密库,但我熟悉SJCL,所以如果可能的话,我更愿意坚持使用它)。
我让SJCL端工作得很好,但是在Java端,我不确定需要使用什么参数来设置密钥生成器和密码。到目前为止,我解密的代码是:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1024, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8");
return plaintext;其中,salt、iv和密文作为字符串从SJCL产生的JSON对象中提取出来,然后使用Base64解码器解码为字节数组。
不幸的是,我在这方面有一些问题,上面的代码不能工作。
我遇到的第一个问题是,PBKDF2WithHmacSHA256似乎不是一个公认的密钥生成算法。我不能完全确定这就是我想要的,但根据阅读SJCL文档,它似乎是正确的?Java可以识别PBKDF2WithHmacSHA1,但这似乎不是SJCL实现的算法。
其次,如果我尝试使用SHA1密钥算法,我得到一个关于无效密钥大小的错误。我需要安装一些东西来启用256位密钥的AES吗?告诉密钥工厂生成128位密钥可以正常工作(尽管显然与使用256位密钥的SJCL不兼容)。
第三,我应该使用哪种加密模式?我很确定CBC是不对的..。SJCL的文档同时提到了CCM和OCB,但是Java似乎不支持开箱即用的任何一种--同样,我需要安装一些东西才能使其工作吗?SJCL默认使用哪一个?
最后,即使我选择了使Java不会抱怨缺少算法的参数,它也会抱怨通过解码SJCL输出提供的IV长度错误,这看起来肯定是错误的:结果输出中有17个字节,而不是AES显然要求的16个字节。我只是忽略最后一个字节吗?
发布于 2012-04-07 02:32:10
我还没有尝试过(最后我放弃了使用Javascript crypto,转而使用带有bouncycastle的嵌入式java applet来处理通信),但是GnuCrypto (一个bouncycastle )支持PBKDFWithHmacSHA256。SJCL中的固定字符编码处理可能修复了IV (?)的意外长度,因此这将离开密码模式。从这一点上看,最简单的方法似乎是实现一个相对简单的密码模式(例如CTR)作为SJCL的附加组件,这应该只需要几个小时的工作,即使是对代码不熟悉的人,然后它只是编码和解码SJCL使用的JSON编码的数据包(这应该是微不足道的)。
作为另一种选择,当然可以为Java实现OCB模式,尽管该算法是专有的,因为在通用公共许可证(http://www.cs.ucdavis.edu/~rogaway/ocb/grant.htm)下分发的软件有公共专利授权。
有趣的是,我想知道GnuCrypto是否会接受OCB模式支持的补丁?GnuCrypto是在GPL-with-libraries exemption下分发的,这看起来像是“自由软件基金会发布的GNU通用公共许可证的任何版本”,所以至少在理论上这应该是可能的。
发布于 2020-04-03 11:05:02
java中的SJCL AES
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.json.JSONObject;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.*;
import java.util.HashMap;
import java.util.Map;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
*
* SJCL 1.0.8
*
* dependencies:
* compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.64'
* compile group: 'org.json', name: 'json', version: '20190722'
*
* ref: https://blog.degering.name/posts/java-sjcl
*/
public class AesSJCL {
// Simply prints out the decoded string.
public static void main(String[] args) throws Exception {
String password = "password";
String plainText = "Who am I?";
// encryption
Map<String, Object> result = new AesSJCL().encrypt( password, plainText);
String json = new JSONObject(result).toString();
System.out.printf("encrypted output:\n%s\n", json);
System.out.printf("\njavascript testing code:\nsjcl.decrypt(\"%s\", '%s')\n", password, json);
// decryption
String decryptedText = new AesSJCL().decrypt(password, json);
System.out.printf("\ndecrypted output: \n%s\n", decryptedText);
}
/**
*
* @param password - password
* @param encryptedText - {"cipher":"aes","mode":"ccm","ct":"r7U/Gp2r8LVNQR7kl5qLNd8=","salt":"VwSOS3jCn6M=","v":1,"ks":128,"iter":10000,"iv":"5OEwQPtHK2ej1mHwvOf57A==","adata":"","ts":64}
* @return
* @throws Exception
*/
public String decrypt(String password, String encryptedText) throws Exception {
Decoder d = Base64.getDecoder();
// Decode the encoded JSON and create a JSON Object from it
JSONObject j = new JSONObject(new String(encryptedText));
// We need the salt, the IV and the cipher text;
// all of them need to be Base64 decoded
byte[] salt=d.decode(j.getString("salt"));
byte[] iv=d.decode(j.getString("iv"));
byte[] cipherText=d.decode(j.getString("ct"));
// Also, we need the keySize and the iteration count
int keySize = j.getInt("ks"), iterations = j.getInt("iter");
// https://github.com/bitwiseshiftleft/sjcl/blob/master/core/ccm.js#L60
int lol = 2;
if (cipherText.length >= 1<<16) lol++;
if (cipherText.length >= 1<<24) lol++;
// Cut the IV to the appropriate length, which is 15 - L
iv = Arrays.copyOf(iv, 15-lol);
// Crypto stuff.
// First, we need the secret AES key,
// which is generated from password and salt
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(),
salt, iterations, keySize);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
// Now it's time to decrypt.
Cipher cipher = Cipher.getInstance("AES/CCM/NoPadding",
new BouncyCastleProvider());
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
// Return the final result after converting it to a string.
return new String(cipher.doFinal(cipherText));
}
/**
*
* @param password
* @param plainText
* @return
* @throws Exception
*/
public Map<String, Object> encrypt(String password, String plainText) throws Exception {
int iterations = 10000; // default in SJCL
int keySize = 128;
// https://github.com/bitwiseshiftleft/sjcl/blob/master/core/convenience.js#L321
// default salt bytes are 8 bytes
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] salt = new byte[8];
sr.nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, keySize);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
// https://github.com/bitwiseshiftleft/sjcl/blob/master/core/random.js#L87
// default iv bytes are 16 bytes
SecureRandom randomSecureRandom = SecureRandom.getInstanceStrong();
byte[] iv = new byte[16];
randomSecureRandom.nextBytes(iv);
int ivl = iv.length;
if (ivl < 7) {
throw new RuntimeException("ccm: iv must be at least 7 bytes");
}
// compute the length of the length
int ol=plainText.length();
int L=2;
for (; L<4 && ( ol >>> 8*L ) > 0; L++) {}
if (L < 15 - ivl) { L = 15-ivl; }
byte[] shortIV = Arrays.copyOf(iv, 15-L);
// Now it's time to decrypt.
Cipher cipher = Cipher.getInstance("AES/CCM/NoPadding", new BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(shortIV));
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(UTF_8));
Encoder encoder = Base64.getEncoder();
Map<String, Object> map = new HashMap<>();
map.put("iv", encoder.encodeToString(iv));
map.put("iter", iterations);
map.put("ks", keySize);
map.put("salt", encoder.encodeToString(salt));
map.put("ct", encoder.encodeToString(encryptedBytes));
map.put("cipher", "aes");
map.put("mode", "ccm");
map.put("adata", "");
map.put("v", 1); // I don't know what it is.
map.put("ts", 64); // I don't know what it is.
return map;
}
}发布于 2012-02-26 09:58:25
您可能必须使用BouncyCastle来获取SJCL中使用的所有加密功能。确保base64正确地解码了所有内容,并且SJCL没有添加长度指示符或类似的指示符。
https://stackoverflow.com/questions/9442342
复制相似问题