【问题标题】:RSA Encryption Javascript and Decrypt JavaRSA 加密 Javascript 和解密 Java
【发布时间】:2014-07-12 04:48:22
【问题描述】:

用不同的组合花了将近 2 天的时间。我正在使用 RSA 算法在 java 中生成一个非对称密钥对(公共和私有),并尝试使用 javascript 中的公钥来加密一些文本并在服务器端的 java 中解密。我在尝试解密用 javascript 加密的字符串时收到“javax.crypto.IllegalBlockSizeException:数据不能超过 128 字节”异常。希望能得到一些帮助...

使用 thi Javascript 库进行加密。

https://github.com/wwwtyro/cryptico

var publicKeyString = ""//java生成的base64编码的公钥字符串

这是我的 javascript 代码

var EncryptionResult = cryptico.encrypt("somestring", publicKeyString);
console.log("Encrypted status-"+EncryptionResult.status);
console.log("Encrypted String-"+EncryptionResult.cipher);

字符串加密成功。

Java 密钥生成和解密

Cipher cipher = Cipher.getInstance("RSA");  
KeyFactory fact = KeyFactory.getInstance("RSA"); 
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024); // 1024 used for normal

KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();

FileOutputStream fos = null;
ObjectOutputStream oos = null;

将私钥存储在文件中的代码,用于解密方法中的解密。

  RSAPrivateKeySpec rsaPrivKeySpec = fact.getKeySpec(privateKey, 
                                                     RSAPrivateKeySpec.class);
 System.out.println("Writing private key...");
 fos = new FileOutputStream(PRIVATE_KEY_FILE);
 oos = new ObjectOutputStream(new BufferedOutputStream(fos));
 oos = new ObjectOutputStream(new BufferedOutputStream(fos));
 oos.writeObject(rsaPrivKeySpec.getModulus());
 oos.writeObject(rsaPrivKeySpec.getPrivateExponent());
 oos.close();

解密方法

public String decrypt(String ciphertext)   
      throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException   
       {  
             if (ciphertext.length() == 0) return null;  
             byte[] dec = org.apache.commons.codec.binary.Base64.decodeBase64(ciphertext);  
             try {
             System.out.println("Private Key file name----"+PRIVATE_KEY_FILE);
         privateKey = readPrivateKeyFromFile(PRIVATE_KEY_FILE);
        } catch (IOException e) {
        e.printStackTrace();
        }
             cipher.init(Cipher.DECRYPT_MODE, privateKey);  
             byte[] decrypted = cipher.doFinal(dec);  
             return new String(decrypted, PLAIN_TEXT_ENCODING);  
  } 

 //reading private key from file

public PrivateKey readPrivateKeyFromFile(String fileName)
    throws IOException {
  FileInputStream fis = null;
  ObjectInputStream ois = null;
   try {
    fis = new FileInputStream(new File(fileName));
    ois = new ObjectInputStream(fis);
    System.out.println("Private Key file-"+fileName);

    BigInteger modulus = (BigInteger) ois.readObject();
    BigInteger exponent = (BigInteger) ois.readObject();

    // Get Private Key
    RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus, exponent);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PrivateKey privateKey = fact.generatePrivate(rsaPrivateKeySpec);
    return privateKey;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (ois != null) {
            ois.close();
            if (fis != null) {
                fis.close();
            }
        }
    }
    return null;
 }    

【问题讨论】:

标签: java javascript encryption


【解决方案1】:

Cryptico 文档看来,这不是简单的 RSA 加密,而是生成 AES 密钥、用 RSA 加密、用 AES 加密数据并输出加密的 AES 密钥和加密数据的串联的复杂操作.如果你想用 Java 解密它,你必须检查 Cryptico 源代码并用 Java 重新实现它。

至于你当前的尝试和javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes 错误:

当您未指定完整转换时,RSA 的默认 JCE 转换为 RSA/ECB/PKCS1Padding

在这种模式下,RSA加密或解密长度不大于密钥大小的单个数据块(更具体地说,如果输入的字节序列被解释为一个大整数,它的值应该小于RSA 使用的模数)。您可以在thisthis 问题中找到更多信息。

密钥大小为 1024 位,最大数据大小为 128 字节,这正是异常所说的,因为Cryptico 的输出显然不是单个 RSA 块,其长度大于“普通“ RSA。在这种情况下,尝试在 Java 中使用其他密码模式或填充模式也无济于事。

【讨论】:

    【解决方案2】:

    感谢 Oleg 提供详细信息。我一定会看看的。

    现在我切换到 jsencrypt,它似乎工作正常。

    https://github.com/travist/jsencrypt

    编辑

    如何获取 js 加密的编码公钥?

    【讨论】:

    • @Savagewood:这个库对在 textarea 控件中定义的每个事务使用每个定义的密钥对。有没有办法每次生成不同的密钥对..?
    【解决方案3】:

    这里是JS数据加密和Java解密(服务器端)的解决方案。我使用 Cryptico js 库进行加密(http://wwwtyro.github.io/cryptico/)。

    首先,我们必须从您的本地系统生成 java Keystore 文件。不要使用其他 Keystore 文件,例如在线 Keystore。要创建 java Keystore(JKS),您可以使用 KeyStore Explorer 工具。

    以下是我使用的配置,使用 KeyStore Explorer 工具

    1. 密钥库类型 - JKS
    2. RSA 算法 - 密钥大小 1024
    3. 版本 - 版本 3
    4. 签名算法 - 带有 RSA 的 SHA256
    5. 有效期 - 99 年(根据您的要求)
    6. 名称已归档 - 填写所有必填字段 - 记住您在此处输入的“别名”和“密码”。

    最后,在本地系统上将文件另存为 .jks。

    第一步

    我们必须在 java 端使用这个 Keystore 文件,并将公钥发送到前端。

    我创建了负责从密钥库文件路径(字符串)、密钥对和解密加载密钥库的服务类。您必须提供别名、密码、密钥库类型。

    public KeyPair getExistingKeyStoreKeyPair(String keystorePath){
            KeyPair generateKeyPair = null
            try {
                File file = new File(keystorePath)
                KeyStore keyStore = loadKeyStore(file, "password", "JKS")
                generateKeyPair = getKeyPair(keyStore, "fin360", "password")
            } catch (Exception ex){
                println(ex)
            }
            return generateKeyPair
        }
    
        public KeyStore loadKeyStore(final File keystoreFile, final String password, final String keyStoreType) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
            if (null == keystoreFile) {
                throw new IllegalArgumentException("Keystore url may not be null")
            }
            final URI keystoreUri = keystoreFile.toURI()
            final URL keystoreUrl = keystoreUri.toURL()
            final KeyStore keystore = KeyStore.getInstance(keyStoreType)
            InputStream is = null
            try {
                is = keystoreUrl.openStream();
                keystore.load(is, null == password ? null : password.toCharArray())
            } finally {
                if (null != is) {
                    is.close()
                }
            }
            return keystore;
        }
    
        public KeyPair getKeyPair(final KeyStore keystore, final String alias, final String password) {
            PublicKey publicKey
            PrivateKey privateKey
            Key key
            KeyPair keyPair
            try {
                key = (PrivateKey) keystore.getKey(alias, password.toCharArray())
                final Certificate cert = keystore.getCertificate(alias)
                publicKey = cert.getPublicKey()
                privateKey = key
                keyPair = new KeyPair(publicKey, privateKey)
            } catch (Exception ex){
                println(ex)
            }
    
            return keyPair;
        }
    
        public decryptData(String data, String keystorePath) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException{
            try {
                byte[] dectyptedText = new byte[1]
                byte[] byteArray = new byte[256]
                BigInteger passwordInt = new BigInteger(data, 16)
                if (passwordInt.toByteArray().length > 256) {
                    for (int i=1; i<257; i++) {
                        byteArray[i-1] = passwordInt.toByteArray()[i]
                    }
                } else {
                    byteArray = passwordInt.toByteArray();
                }
    
                KeyPair generateKeyPair = getExistingKeyStoreKeyPair(keystorePath)
    
                PrivateKey privateKey = generateKeyPair.getPrivate()
                Cipher cipher = Cipher.getInstance("RSA")
                cipher.init(Cipher.DECRYPT_MODE, privateKey)
                dectyptedText = cipher.doFinal(byteArray)
                String txt2 = new String(dectyptedText)
                return txt2
            }
            catch (Exception ex){
                println(ex)
                return null
            }
        }
    

    decryptData() 方法将在这里发挥主要作用。当您将值 data.getBytes() 直接发送到 dycrypt 方法 cipher.doFinal(byteArray) 时,您会得到异常 - IllegalBlockSizeException 大小不应超过 128 个字节。所以我们已经摆脱了我在这里得到解决方法的问题 - [Getting 1 byte extra in the modulus RSA Key and sometimes for exponents also 基本上,当我们将数据从 BigInteger 转换为 byteArray 时,它会添加零。所以我从数组中删除了零。

    让我们开始使用服务类来获取键值。

    String publicKey= null
    String keystorePath = your file path
                KeyPair generateKeyPair = encryptDecryptService.getExistingKeyStoreKeyPair(keystorePath)
                PublicKey publicKey1 = generateKeyPair.getPublic()
                KeyFactory keyFactory;
                RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(BigInteger.ZERO, BigInteger.ZERO)
                try {
                    keyFactory = KeyFactory.getInstance("RSA")
                    rsaPublicKeySpec = keyFactory.getKeySpec(publicKey1, RSAPublicKeySpec.class)
                } catch(NoSuchAlgorithmException e1) {
                    println(e1)
                } catch(InvalidKeySpecException e) {
                    println(e)
                }
                String testPublicKey = rsaPublicKeySpec.getModulus().toString(16)
                publicKey = testPublicKey
    

    将你的 publicKey 发送给 JS。 在您的 HTML 或 servlet 中导入所有必需的 js 和 jar 文件(您将从 cryptico js 库中获得它)。

    try{
                        var rsa = new RSAKey();
                        rsa.setPublic(pub, "10001");
                        password = rsa.encrypt(password);
                        formdata = "password="+password+"&dataEncrypt=true";
                    }
                    catch (error){
                        console.log(error);
                    }
    

    上面我直接使用了new RSA() 实例(在cryptico 库中它会有所不同。内部库使用相同)并将公钥设置为实例。我们必须使用十六进制字符串值是'10001'。使用我们发送到服务器的加密数据形成查询字符串。表单数据保存加密数据以及“dataEncrypt”键值。我曾经检查数据是否加密。

    最后在服务器端,您将获得请求参数,下面是解密代码。

    Boolean isDataEncrypted = false
            String decryptedPassword = null
            isDataEncrypted = params.containsKey("dataEncrypt")
            if(params.containsKey("password")){
                if(isDataEncrypted) {
                    String keystorePath = helperService.fetchKeystoreFilePath()
                    decryptedPassword = encryptDecryptService.decryptData(params.password, keystorePath)
    
                    // update decrypted data into request params
                    params.password = decryptedPassword
    
                }
            }
            println("Data decrypted => " + decryptedPassword)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-31
      • 2014-06-02
      • 1970-01-01
      • 2010-10-11
      • 1970-01-01
      相关资源
      最近更新 更多