android - 使用 AES 算法加密和解密

标签 android encryption cryptography

我找到了有关加密和解密的教程,但在尝试实现该教程时遇到了一个小问题。我想知道它是否永久,但当我尝试将文本更改为其他文本时,有一行 String SeedValue = "This Is MySecure"; 我在解密时遇到问题。这段代码在这段代码中是不可更改的吗我在想是否可以用它来加密和解密密码。这是代码。

(使用 AESHelper 类,找到完整代码 here 。)

public class MainActivity extends Activity {

    String seedValue = "This Is MySecure";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String normalText = "VIJAY";
        String normalTextEnc;

        try{
            normalTextEnc = AESHelper.encrypt(seedValue, normalText);
            String normalTextDec = AESHelper.decrypt(seedValue, normalTextEnc);
            TextView txe = new TextView(this);
            txe.setTextSize(14);
            txe.setText("Normal Text ::" + normalText + " \n Encrypted Value :: " + normalTextEnc + " \n Decrypted value :: " + normalTextDec);
            setContentView(txe);
        }catch(Exception e){
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}


更新:

能够实现加密和解密,但我遇到了另一个问题,我不明白它为什么会发生以及如何修复它。

错误显示03-21 05:25:08.554: E/Exception(2109): pad block损坏我从这个网站here获得了代码

更新:

Pad Block 修复了另一个问题,上面写着:

03-24 02:31:33.131: E/Exception(1308): error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

更新: 这是我的代码,我按照建议进行操作,但当我尝试解密加密的代码时,反之亦然,加密解密的代码时,我会得到异常。

package com.sample.camera;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import android.util.Base64;

public class EncodeDecodeAES {



    private final static String HEX = "0123456789ABCDEF";

    private final static int JELLY_BEAN_4_2 = 17;

    private final static byte[] key = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };




    // static {

    // Security.addProvider(new BouncyCastleProvider());

    // }



    public static String encrypt(String seed, String cleartext) throws Exception {

        byte[] rawKey = getRawKey(seed.getBytes());

        byte[] result = encrypt(rawKey, cleartext.getBytes());

        String fromHex = toHex(result);

        String base64 = new String(Base64.encodeToString(fromHex.getBytes(), 0));

        return base64;


    }




    public static String decrypt(String seed, String encrypted) throws Exception {

        byte[] seedByte = seed.getBytes();

        System.arraycopy(seedByte, 0, key, 0, ((seedByte.length < 16) ? seedByte.length : 16));

        String base64 = new String(Base64.decode(encrypted, 0));

        byte[] rawKey = getRawKey(seedByte);

        byte[] enc = toByte(base64);

        byte[] result = decrypt(rawKey, enc);

        return new String(result);


    }




    public static byte[] encryptBytes(String seed, byte[] cleartext) throws Exception {

        byte[] rawKey = getRawKey(seed.getBytes());

        byte[] result = encrypt(rawKey, cleartext);

        return result;


    }




    public static byte[] decryptBytes(String seed, byte[] encrypted) throws Exception {

        byte[] rawKey = getRawKey(seed.getBytes());

        byte[] result = decrypt(rawKey, encrypted);

        return result;


    }




    private static byte[] getRawKey(byte[] seed) throws Exception {

        KeyGenerator kgen = KeyGenerator.getInstance("AES"); // , "SC");

        SecureRandom sr = null;

        if (android.os.Build.VERSION.SDK_INT >= JELLY_BEAN_4_2) {

            sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");


        } else {

            sr = SecureRandom.getInstance("SHA1PRNG");


        }

        sr.setSeed(seed);

        try {

            kgen.init(256, sr);

            // kgen.init(128, sr);


        } catch (Exception e) {

            // Log.w(LOG, "This device doesn't suppor 256bits, trying 192bits.");

            try {

                kgen.init(192, sr);


            } catch (Exception e1) {

                // Log.w(LOG, "This device doesn't suppor 192bits, trying 128bits.");

                kgen.init(128, sr);


            }

        }

        SecretKey skey = kgen.generateKey();

        byte[] raw = skey.getEncoded();

        return raw;


    }




    private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {

        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");

        //Cipher cipher = Cipher.getInstance("AES"); // /ECB/PKCS7Padding", "SC");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);

        byte[] encrypted = cipher.doFinal(clear);

        return encrypted;


    }




    private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {

        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // /ECB/PKCS7Padding", "SC");

        cipher.init(Cipher.DECRYPT_MODE, skeySpec);

        byte[] decrypted = cipher.doFinal(encrypted);

        return decrypted;


    }




    public static String toHex(String txt) {

        return toHex(txt.getBytes());


    }




    public static String fromHex(String hex) {

        return new String(toByte(hex));


    }




    public static byte[] toByte(String hexString) {

        int len = hexString.length() / 2;

        byte[] result = new byte[len];

        for (int i = 0; i < len; i++)

            result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();

        return result;

    }



    public static String toHex(byte[] buf) {
        if (buf == null)

            return "";

        StringBuffer result = new StringBuffer(2 * buf.length);

        for (int i = 0; i < buf.length; i++) {

            appendHex(result, buf[i]);

        }
        return result.toString();

    }




    private static void appendHex(StringBuffer sb, byte b) {

        sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));


    }




}

最佳答案

除非我错过了一些东西,否则你的问题在于数据输入的实际填充过程。

AES 是一种分组密码,这意味着它一次适用于数据 block 。对于 AES, block 大小恰好是 128 位或 16 字节。如果您的数据不是 16 字节的倍数,则当 AES 尝试处理最后一个 block 时,您将收到异常。

为了解决这个问题,发明了密码填充方案。密码填充方案本质上是以某种方式“填充”数据,使其成为 16 字节的倍数,从而允许 AES 毫无问题地处理最后一个 block 。 AES 的事实上标准填充方案是 PKCS#5 或 PKCS#7 填充(它们是相同的东西,但名称不同)。

问题本质上在于:

Cipher cipher = Cipher.getInstance("AES");

来自 AESHelper 类。

仅使用算法名称(而不是算法/模式/填充)调用 getInstance() 方法会导致 java 推断特定的密码模式和填充方案。这可以在Java 加密体系结构 (JCA) 引用指南中的创建 Cipher 对象下看到,如下所示:

If just a transformation name is specified, the system will determine if there is an implementation of the requested transformation available in the environment, and if there is more than one, returns there is a preferred one.

If no mode or padding is specified, provider-specific default values for the mode and padding scheme are used. For example, the SunJCE provider uses ECB as the default mode, and PKCS5Padding as the default padding scheme for DES, DES-EDE and Blowfish ciphers.

同样,在 android Cipher javadoc 页面中可以看到相同的语句:

If mode and/or padding values are omitted, provider specific default values will be used.

您想要的是:

AES/ECB/PKCS5Padding

尽管可以说我会说上面的内容并不安全,应该使用以下内容:

AES/CBC/PKCS5Padding

加上随机 IV 生成,但那是另一个故事了。

更改行:

Cipher cipher = Cipher.getInstance("AES");

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

应该可以解决您的问题。

关于android - 使用 AES 算法加密和解密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22549251/

相关文章:

java - Xposed模块,ClassNotFoundException动态加载类

android - 无法解决:de.hdodenhof:circleimageview:2.4.0

c# - 在 iOS 中实现加密逻辑的最佳实践是什么?

c# - 有谁知道如何在 C# 中实现安全的 CNG key 存储?

Android CoordinatorLayout - Android 版本之间的不一致

java - Activity onCreate 方法的最佳实践是什么?

c++ - 如何在 Qt/C++ 中存储加密设置?

c - 微 Controller 上的 Ascii 字符串加密

encryption - OpenSSL 解密错误 - 填充与原始

java - 安全非重复随机字母数字 URL slug