解密错误:由java.lang.noclassdeffounderror com/android/org/constrypt/OpenSSLRSAPublicKey-android引起

解密错误:由java.lang.noclassdeffounderror com/android/org/constrypt/OpenSSLRSAPublicKey-android引起,java,android,encryption,cryptography,android-6.0-marshmallow,Java,Android,Encryption,Cryptography,Android 6.0 Marshmallow,嗨,我正在尝试在Android上解密我的公钥中的内容: public String decrypt(String basetext) { try { FileInputStream iR = new FileInputStream("/sdcard/publickkey"); ObjectInputStream inputStream = new ObjectInputStream(iR); final P

嗨,我正在尝试在Android上解密我的公钥中的内容:

public String decrypt(String basetext) {

        try {
            FileInputStream iR = new FileInputStream("/sdcard/publickkey");
            ObjectInputStream inputStream = new ObjectInputStream(iR);
            final PublicKey key = (PublicKey) inputStream.readObject();

            byte[] text = Base64.decode(basetext, Base64.DEFAULT);

            // get an RSA cipher object and print the provider
            final Cipher cipher = Cipher.getInstance("RSA");

            // decrypt the text using the public key
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] dectyptedText = cipher.doFinal(text);

            iR.close();
            return new String(dectyptedText,"UTF-8");

        } catch (Exception ex) {
            ex.printStackTrace();
            return  null;
        }
    }
它在我的棉花糖上运行良好,尝试在emulator 4.2.2上运行,并抛出以下错误:

caused by java.lang.noclassdeffounderror com/android/org/constcrypt/OpenSSLRSAPublicKey android
如果我看到我的导入,则不会出现类似上述错误的导入

import javax.crypto.Cipher;
import java.security.PublicKey;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
这在真实设备安卓棉花糖上运行良好,4.2.2在模拟器上崩溃

全班:-

import android.util.Base64;

import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.Cipher;

public class EncryptionUtils {

    public static final String ALGORITHM = "RSA";
    public static final String PRIVATE_KEY_FILE = "/sdcard/private.key";
    public static final String PUBLIC_KEY_FILE = "/sdcard/public.key";

    public static void generateKey() {
        try {
            final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
            keyGen.initialize(2048);
            final KeyPair key = keyGen.generateKeyPair();

            File privateKeyFile = new File(PRIVATE_KEY_FILE);
            File publicKeyFile = new File(PUBLIC_KEY_FILE);

            // Create files to store public and private key
            if (privateKeyFile.getParentFile() != null) {
                privateKeyFile.getParentFile().mkdirs();
            }
            privateKeyFile.createNewFile();

            if (publicKeyFile.getParentFile() != null) {
                publicKeyFile.getParentFile().mkdirs();
            }
            publicKeyFile.createNewFile();

            // Saving the Public key in a file
            ObjectOutputStream publicKeyOS = new ObjectOutputStream(
                    new FileOutputStream(publicKeyFile));
            publicKeyOS.writeObject(key.getPublic());
            publicKeyOS.close();

            // Saving the Private key in a file
            ObjectOutputStream privateKeyOS = new ObjectOutputStream(
                    new FileOutputStream(privateKeyFile));
            privateKeyOS.writeObject(key.getPrivate());
            privateKeyOS.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static String encrypt(String text, PrivateKey key) {
        try {
            // get an RSA cipher object and print the provider
            final Cipher cipher = Cipher.getInstance(ALGORITHM);
            // encrypt the plain text using the private key
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] cipherText = cipher.doFinal(text.getBytes("UTF-8"));
            String base64 = Base64.encodeToString(cipherText, Base64.DEFAULT);
            return  base64;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String decrypt(String basetext, PublicKey key) {

        try {
            byte[] text = Base64.decode(basetext, Base64.DEFAULT);

            // get an RSA cipher object and print the provider
            final Cipher cipher = Cipher.getInstance(ALGORITHM);

            // decrypt the text using the public key
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] dectyptedText = cipher.doFinal(text);
            return new String(dectyptedText,"UTF-8");

        } catch (Exception ex) {
            ex.printStackTrace();
            return  null;
        }
    }
}

在定义您要使用的密码时,必须是特定的。而不是使用:

final Cipher cipher = Cipher.getInstance("RSA");
你应该试试:

final Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

我相信纯Java和Android之间存在一些不一致之处,我不记得是什么版本,但在大多数情况下,上面的调整解决了问题。

在定义您打算使用的密码时,特定于是值得的。而不是使用:

final Cipher cipher = Cipher.getInstance("RSA");
你应该试试:

final Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

我相信普通Java和Android之间存在一些不一致,我不记得是什么版本,但在大多数情况下,上面的调整解决了问题。

您不应该使用
ObjectOutputStream
/
ObjectInputStream
来存储密钥。
PublicKey
的底层实现类因版本而异,如果某个对象的类不在更高版本中,则使用内部类对该对象进行序列化将失败

我建议直接存储公钥数据

byte publicKeyData[] = publicKey.getEncoded();
并使用

X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyData);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey = kf.generatePublic(spec);
还要注意的是,在
4.3
之前的android中,加密提供程序没有完全实现,使用RSA公钥解密和私钥加密等非常规操作可以得到保证

更新-海绵城堡 或者,您可以使用spongycastle加密提供程序生成和加载密钥

渐变依赖关系

compile 'com.madgag.spongycastle:core:1.51.0.0'
compile 'com.madgag.spongycastle:prov:1.51.0.0'
compile 'com.madgag.spongycastle:pkix:1.51.0.0'
添加提供程序

static {
    Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
用法

SC
添加到provider参数。比如说

KeyPairGenerator keyGen= KeyPairGenerator.getInstance("RSA", "SC");

KeyFactory keyFactory = KeyFactory.getInstance("RSA", "SC");

请参阅更多信息,您不应使用
ObjectOutputStream
/
ObjectInputStream
存储密钥。
PublicKey
的底层实现类因版本而异,如果某个对象的类不在更高版本中,则使用内部类对该对象进行序列化将失败

我建议直接存储公钥数据

byte publicKeyData[] = publicKey.getEncoded();
并使用

X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyData);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey = kf.generatePublic(spec);
还要注意的是,在
4.3
之前的android中,加密提供程序没有完全实现,使用RSA公钥解密和私钥加密等非常规操作可以得到保证

更新-海绵城堡 或者,您可以使用spongycastle加密提供程序生成和加载密钥

渐变依赖关系

compile 'com.madgag.spongycastle:core:1.51.0.0'
compile 'com.madgag.spongycastle:prov:1.51.0.0'
compile 'com.madgag.spongycastle:pkix:1.51.0.0'
添加提供程序

static {
    Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
用法

SC
添加到provider参数。比如说

KeyPairGenerator keyGen= KeyPairGenerator.getInstance("RSA", "SC");

KeyFactory keyFactory = KeyFactory.getInstance("RSA", "SC");

查看更多信息

唯一的一点是,android 4.3及以上版本中添加了真正的加密技术,因此在android 4.2及以下版本中,它会导致问题,因为openssl类在api 18中是默认的,所以在4.3+上使用此rsa加密/解密是可以的,否则在下面使用一些其他技术支持

这就是我这边的解决方案,如果有人遇到这个问题,你可以在这里看到解决方案


或者,如果有人能够在不使用第三方库的情况下使其工作,那么他非常欢迎在这里编写它

唯一的问题是,android 4.3及以上版本中添加了真正的加密技术,因此在android 4.2及以下版本中,它将导致问题,因为openssl类在api 18中被设置为默认类,所以在4.3+上使用这个rsa加密/解密是可以的,下面我们将使用一些其他技术来支持它

这就是我这边的解决方案,如果有人遇到这个问题,你可以在这里看到解决方案


或者,如果有人在不使用第三方库的情况下成功地使其工作,则最欢迎他在此处编写

still force closestill force closewell它也不会有任何问题,主要问题是RSA在安卓4.3以下无法完美运行,这就是为什么它会变得非常接近。因此需要为安卓4.3以下的版本制定一些措施。我在4.3及以上版本中选择了“是”,它工作正常,但低于它会导致崩溃,您可以阅读api 18中添加的大部分加密内容,您能给我安卓实现的链接吗?是的,我知道安卓18和安全密钥库的本机实现的优点。我已经发布了一些使用Spongycastle provider的示例。Spongycastle可以解决这个问题,但会增加MBs大小,这是降级,所以我想在4.3和更高版本上使用rsa,或者使用其他东西。它也不会有任何问题,主要问题是RSA在安卓4.3以下无法完美运行,这就是为什么它会变得非常接近。因此需要为安卓4.3以下的版本制定一些措施。我在4.3及以上版本中选择了“是”,它工作正常,但低于它会导致崩溃,您可以阅读api 18中添加的大部分加密内容,您能给我安卓实现的链接吗?是的,我知道安卓18和安全密钥库的本机实现的优点。我已经发布了一些使用Spongycastle provider的例子。Spongycastle可以解决这个问题,但是它增加了MBs大小,这就是降级,所以我想在4.3和更高版本上使用rsa,或者使用其他东西