【问题标题】:Encrypting text in Java using the RSA algorithm使用 RSA 算法在 Java 中加密文本
【发布时间】:2013-08-10 17:57:05
【问题描述】:

我正在尝试在 Java 中使用 RSA 加密。

我正在生成一个公钥并使用它来加密文本。我的问题是,当我两次传入相同的文本和相同的密钥时,加密的结果是不同的。这意味着我不能使用我的加密来测试输入的文本是否等于先前加密的存储结果。

这是我的加密类:

   import java.security.InvalidKeyException;
   import java.security.KeyPair;
   import java.security.KeyPairGenerator;
   import java.security.NoSuchAlgorithmException;
   import java.security.PublicKey;
   import java.util.Arrays;

   import javax.crypto.BadPaddingException;
   import javax.crypto.Cipher;
   import javax.crypto.IllegalBlockSizeException;
   import javax.crypto.NoSuchPaddingException;
   /**
    * The class encrypts text using an RSA algorithm.
    *
    */
   public class RSAEncryption {
       //RSA algorithm
       private final String ALGORITHM = "RSA";

/**
 * The generateKey method generates a public key for use in RSA encryption.
 * @return key a PublicKey for use in RSA encryption.
 */
public PublicKey generateKey(){
    KeyPair key = null;
    KeyPairGenerator keyGen;
    try {
        keyGen = KeyPairGenerator.getInstance(ALGORITHM); //gets instance of the alogrithm
        keyGen.initialize(1024); //a 1021 bit key
        key = keyGen.generateKeyPair(); //makes a pair
    } catch (NoSuchAlgorithmException e) {

        e.printStackTrace();
    }
    return key.getPublic(); //returns the public key. Private key never stored.
}

    /**
    * The encrypt method takes in text and a key and encrypts the text using the RSA encryption algorithm.
    * @params text a String, the text to encrypt.
    * @params key a PublicKey to use in encryption.
    * @returns encryptedText a byte array representing the result of the encryption.

public byte[] encrypt(String text, PublicKey key){
    byte[] encryptedText = null;
    Cipher cipher;
    try {
        cipher = Cipher.getInstance(ALGORITHM); //gets instance of RSA
        cipher.init(Cipher.ENCRYPT_MODE, key); //in encryption mode with the key
        encryptedText = cipher.doFinal(text.getBytes()); //carry out the encryption
    } catch (NoSuchAlgorithmException e) {          
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {            
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {         
        e.printStackTrace();
    } catch (BadPaddingException e) {           
        e.printStackTrace();
    } catch (InvalidKeyException e) {       
        e.printStackTrace();
    }
    return encryptedText; //return encrypted result
}

/**
 * The authenticate method checks if entered text, once encrypted, matches the stored byte[].
 * @param text a String, the text to encrypt.
 * @param stored a byte[], the result of a prior encryption.
 * @param key a PublicKey, a result of the generateKey method.
 * @return boolean, true if the text is valid, false otherwise.
 */

public boolean authenticate(String text, byte[] stored, PublicKey key){
    byte[] encryptText = encrypt(text,key); //encrypt the entered text
    return Arrays.equals(stored, encryptText); //check if the stored and entered byte[] are the same.
}
} 

我为此编写了 JUnit 测试:

import static org.junit.Assert.*;

import java.security.PublicKey;
import java.util.Arrays;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;


public class RSAEncryptionTest {

RSAEncryption cipher;
String text;

@Before
public void setUp(){
    cipher = new RSAEncryption();
    text = "text";
}

@Test
public void testEncryptionGenerateKeyGeneratesANewKeyWhenCalled(){

    PublicKey key = cipher.generateKey();

    assertEquals(false,key.equals(cipher.generateKey()));
}

@Test
public void testEncryptionEncryptMethodRepeatablyEncrypts(){

    PublicKey key = cipher.generateKey();

    byte[] encrypted = cipher.encrypt(text,key);
    Assert.assertArrayEquals(encrypted, cipher.encrypt(text,key));
    //test fails
}


@Test
public void testEncryptionAuthenticateMethodReturnsTrueWhenValidTextPassedIn(){

    PublicKey key = cipher.generateKey();

    byte[] encrypted = cipher.encrypt(text,key);

    assertEquals(true,cipher.authenticate(text,encrypted,key));
            //test fails
}


@Test
public void testEncryptionAuthenticateMethodReturnsFalseWhenInvalidTextPassedIn(){

    PublicKey key = cipher.generateKey();

    byte[] encrypted = cipher.encrypt(text,key);

    assertEquals(false,cipher.authenticate("text1",encrypted,key)); 
} 

}

第二次和第三次测试失败。

任何想法如何使用 RSA 重复加密文本?

【问题讨论】:

    标签: java encryption junit public-key-encryption


    【解决方案1】:

    RSA 是一种公钥加密方案。听起来您实际上想要使用散列算法(例如 SHA-256 或 SHA-512)。我这样说是因为你说:

    这意味着我不能使用我的加密来测试输入的文本是否相等 到先前加密的存储结果。

    如果这是您的目标,您应该使用散列算法。按照设计,RSA 加密应该包括一个填充步骤,以确保密文不同:

    为了避免这些问题,实际的 RSA 实现通常会在加密之前将某种形式的结构化随机填充嵌入到值 m 中。这种填充确保 m 不会落入不安全的明文范围内,并且给定的消息一旦填充,将加密为大量不同的可能密文之一。

    -- http://en.wikipedia.org/wiki/RSA_%28algorithm%29

    【讨论】:

    • 谢谢。我使用 SHA1 编写了一个类似的类,但想尝试 RSA。我想我可能对算法的工作原理有一个基本的误解,谢谢你的解释。
    • 公钥方案通常是最耗费资源的,只有在绝对必要时才应使用它们。为 RSA 测试/理解编写的一组好的类类似于数字签名。
    • 我很确定您对算法有一个根本性的误解。看起来您正在寻找 RSA 签名生成。我建议您阅读 PKCS#1 规范。它们并不难阅读,如果您想围绕 RSA 创建自己的协议,您需要理解它们。
    • @mikey 如果您使用 PKCS#1 v1.5 签名,那么对于相同的消息,您将获得相同的结果。然而,这或多或少是使用较旧的 v1.5 签名方案的副作用。如果您使用 PKCS#1 PSS 方案,那么签名每次都会不同。
    【解决方案2】:

    当使用适当的填充方案(通常是 PKCS#1 或 OAEP 填充)时,对于给定的明文,RSA 密码的输出每次都不相同。加密给定的明文每次都会产生不同的密文。如果密码每次为给定输入生成相同的输出,这将是一个安全漏洞。

    话虽如此,您可以通过对Cipher.getInstance(String) 使用规范“RSA/ECB/NOPADDING”来强制 Java 使用非填充 RSA 密码。这样做会导致您的测试通过,但正如我之前所说,这不是很安全。

    【讨论】:

      猜你喜欢
      • 2019-08-12
      • 2013-11-23
      • 2019-05-04
      • 1970-01-01
      • 2019-07-22
      • 2011-05-27
      • 2015-06-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多