【问题标题】:Android encrypt 3DES ECBAndroid 加密 3DES ECB
【发布时间】:2015-11-30 13:09:36
【问题描述】:

我需要使用从密码生成的自定义密钥来加密一些数据。

必须是: 3DES 密码模式:ECB 填充模式:零

我找不到任何代码来做到这一点。谁能给我一个例子?

我试过了,它显示了以下错误

ERROR: java.security.InvalidAlgorithmParameterException: expected IV length of 0


import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;

import javax.crypto.Cipher;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Encryption {

public static int MAX_KEY_LENGTH = DESedeKeySpec.DES_EDE_KEY_LEN;
private static String ENCRYPTION_KEY_TYPE = "DESede";
private static String ENCRYPTION_ALGORITHM = "DESede/ECB/PKCS5Padding";
private final SecretKeySpec keySpec;

public Encryption(String passphrase) {
    byte[] key;
    try {
        // get bytes representation of the password
        key = passphrase.getBytes("UTF8");
    } catch (UnsupportedEncodingException e) {
        throw new IllegalArgumentException(e);
    }

    key = padKeyToLength(key, MAX_KEY_LENGTH);
    keySpec = new SecretKeySpec(key, ENCRYPTION_KEY_TYPE);
}

// !!! - see post below
private byte[] padKeyToLength(byte[] key, int len) {
    byte[] newKey = new byte[len];
    System.arraycopy(key, 0, newKey, 0, Math.min(key.length, len));
    return newKey;
}

// standard stuff
public byte[] encrypt(byte[] unencrypted) throws GeneralSecurityException {
    return doCipher(unencrypted, Cipher.ENCRYPT_MODE);
}

public byte[] decrypt(byte[] encrypted) throws GeneralSecurityException {
    return doCipher(encrypted, Cipher.DECRYPT_MODE);
}

private byte[] doCipher(byte[] original, int mode) throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
    // IV = 0 is yet another issue, we'll ignore it here
    IvParameterSpec iv = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 });
    cipher.init(mode, keySpec, iv);
    return cipher.doFinal(original);
}
}

编辑: 现在我有了这个,我将自定义密钥(密钥大小=8)作为“密码”参数传递 我调用的php不接受加密结果(不正确)。

正确的加密是这个网络将算法“三重”和模式:ECB https://www.tools4noobs.com/online_tools/encrypt/

我还不知道我做错了什么......

public class Encryption {

public static int MAX_KEY_LENGTH = DESedeKeySpec.DES_EDE_KEY_LEN;
private static String ENCRYPTION_KEY_TYPE = "DESede";
private static String ENCRYPTION_ALGORITHM = "DESede/ECB/NoPadding";
private final SecretKeySpec keySpec; 

public Encryption(String passphrase) {
        byte[] key;
    try {
        // get bytes representation of the password
        key = passphrase.getBytes("UTF8");
    } catch (UnsupportedEncodingException e) {
        throw new IllegalArgumentException(e);
    }

    key = padKeyToLength(key, MAX_KEY_LENGTH);
    keySpec = new SecretKeySpec(key, ENCRYPTION_KEY_TYPE);
}


private byte[] padKeyToLength(byte[] key, int len) {
    byte[] newKey = new byte[len];
    System.arraycopy(key, 0, newKey, 0, Math.min(key.length, len));
    return newKey;
}

// standard stuff
public byte[] encrypt(byte[] unencrypted) throws GeneralSecurityException {
    return doCipher(unencrypted, Cipher.ENCRYPT_MODE);
}

public byte[] decrypt(byte[] encrypted) throws GeneralSecurityException {
    return doCipher(encrypted, Cipher.DECRYPT_MODE);
}

private byte[] doCipher(byte[] original, int mode) throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
    int bs = cipher.getBlockSize();
    byte[] padded = new byte[original.length + bs - original.length % bs];
    System.arraycopy(original, 0, padded, 0, original.length);
    cipher.init(mode, keySpec);
    return cipher.doFinal(padded);
}

【问题讨论】:

  • 您刚刚在互联网上发布了用于加密的密钥...
  • 错误很明显:“预期 IV 长度为 0”。 ECB 模式不使用 IV,但您确实不应该使用 ECB。它在语义上不安全。对于每个加密,至少使用带有随机 IV 的 CBC 模式。 IV 不必是秘密的,因此您可以将其与密文一起发送,但它必须是不可预测的。
  • @Relequestual 我发布的密钥是一个示例,它不是真正的密钥。我不知道为什么有人对这个问题投了反对票。
  • @ArtjomB。我必须使用 ECB,因为我必须调用需要以 3DES 加密的数据的 php。
  • 很公平。与您的其他评论有关。您无法控制您呼叫的系统吗?

标签: android security encryption 3des


【解决方案1】:

ECB 模式不使用 IV,这使其成为确定性密码模式,这意味着它在语义上不安全。如果还需要使用,把IV作为参数去掉:

int bs = cipher.getBlockSize();
byte[] padded = new byte[original.length + bs - original.length % bs];
System.arraycopy(original, 0, padded, 0, original.length);
cipher.init(mode, keySpec);
return cipher.doFinal(padded);

这将为您提供一个零填充消息,然后可以对其进行加密。这是可行的,因为字节数组总是用零初始化。填充与 BouncyCastle 的 ZeroPadding 相同。如果你想像 PHP 的 mcrypt 那样做零填充,那么使用

byte[] padded = new byte[original.length + (bs - original.length % bs) % bs];

【讨论】:

  • 好的,现在它可以工作了,但它并没有像它应该的那样加密,我认为问题出在填充中,我不能放 ZeroPading 因为它说它不存在,但是它必须是 ZeroPading。
  • 用实际代码编辑了我的问题。还没有最终解决方案。你知道出了什么问题吗?感谢您的帮助。
  • 我不知道你做错了什么。就个人而言,我真的不喜欢将加密代码与一些未指定它们如何加密的在线工具相匹配。有一百万件事情可以以不同的方式完成。你自己一个人。
  • 网上说“此工具使用 PHP 中的 mcrypt_encrypt() 函数”感谢您的帮助
  • 不过,differently 仍有很多工作要做。特别是,TDEA/3DES 有不同的子密钥布局(记住它是 EDE,这三个阶段中的每一个都应该有它自己的 64 位密钥)。我怀疑这可能与this 有关,但它只是guessing,我不想猜测。
【解决方案2】:

以下是我使用 3DES 加密某些内容的代码示例。我的 secretKey 也使用 Base64,但我想你会明白的。

public class KeywordsCipher {

private static final String PADDING = "DESede/ECB/NoPadding";
private static final String UTF_F8 = "UTF-8";
private static final String DE_SEDE = "DESede";
private String secretKey;

{...}

public String encrypt(String message, String secretKey) {

    byte[] cipherText = null;

    try {
        final byte[] secretBase64Key = Base64.decodeBase64(secretKey);
        final SecretKey key = new SecretKeySpec(secretBase64Key, DE_SEDE);
        final Cipher cipher = Cipher.getInstance(PADDING);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        final byte[] plainTextBytes = message.getBytes();
        cipherText = cipher.doFinal(plainTextBytes);
    } catch (NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException e) {
        throw new CipherException("Problem with encryption occured");
    }

    return Hex.encodeHexString(cipherText);
}

public CipherKeywordModel decrypt(String keyToDecrypt, String secretKey) {

    try {
        byte[] message = DatatypeConverter.parseHexBinary(keyToDecrypt);
        final byte[] secretBase64Key = Base64.decodeBase64(secretKey);
        final SecretKey key = new SecretKeySpec(secretBase64Key, DE_SEDE);
        final Cipher decipher = Cipher.getInstance(PADDING);
        decipher.init(Cipher.DECRYPT_MODE, key);
        final byte[] plainText = decipher.doFinal(message);
        String decryptedText = new String(plainText, UTF_F8);
    } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException e) {
        throw new CipherException("Problem with encryption occured");
    }
    return decryptedText;
}

【讨论】:

  • 你能显示更多代码吗?什么是 CipherKeywordModel?如何使用我需要使用的字符串键?什么是 DSLContext?
  • 嗨,我编辑了这段代码,删除了 CipherKeywordModel 和 DSLContext。我曾经创建一些对象并使用数据库,但你不需要这个。此代码现在仅使用 secretKey。您可能不想使用密钥的 Base64 编码。如果是这样,请仅使用 final byte[] secretBase64Key = secretKey.getBytes(Charset.forName("UTF-8"));
猜你喜欢
  • 2016-12-22
  • 2015-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多