Java中的RSA密钥大小小于512位

时间:2015-09-07 09:57:50

标签: java encryption cryptography rsa digital-signature

我有一个遗留应用程序,其硬编码RSA密钥大小为384位,我需要能够在我的Java应用程序中验证这些密钥的签名。问题:有没有办法在密钥大小小于512的Java中创建和使用RSA密钥?

(我完全清楚有一个限制512位的原因,但我不能改变遗留应用程序。)

2 个答案:

答案 0 :(得分:3)

是的,但您必须使用其他提供商。当您尝试使用384位RSA密钥时,Sun RSAKeyFactoryKeyFactory的基础服务提供程序实现)和RSAKeyPairGenerator都会返回异常。

正确安装Bouncy Castle提供程序后,这将有效:

Security.addProvider(new BouncyCastleProvider());

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
kpg.initialize(384);
KeyPair kp = kpg.generateKeyPair();
PublicKey genPub = kp.getPublic();

byte[] enc = genPub.getEncoded();

KeyFactory kf = KeyFactory.getInstance("RSA", "BC");
X509EncodedKeySpec ks = new X509EncodedKeySpec(enc);
PublicKey decPub = kf.generatePublic(ks);

Signature sig = Signature.getInstance("SHA1withRSA", "BC");
sig.initVerify(decPub);
byte[] faultySig = new byte[384 / Byte.SIZE];
boolean verifies = sig.verify(faultySig);

System.out.println(verifies + " for " + decPub.getAlgorithm());

请注意,由于KeyFactory生成的密钥类型,init实例的Signature方法将默默使用Bouncy Castle提供程序,即使"BC"不是指定。

答案 1 :(得分:0)

如果你想手写代码来创建更小的密钥,我可以为我编写的C#程序提供源代码。你应该很容易移植到Java。请记住,即使使用已建立的算法,也有充分的理由来实现自己的加密例程的一般警告。因为除非程序员非常小心,否则即使算法本身很好,软件也可能容易受到旁道攻击。话虽如此,在384位,您的安全性已经足够低,以至于侧面通道攻击甚至不是必需的,并且不应该成为主要关注点(直接因子攻击会更便宜且更容易)。

所有这一切,这是源代码。它与不存在的窗口交互,但至少它应该让你很好地了解RSA keygen如何工作。我只是尝试了384位密钥,即使在C#中它也可以在一秒钟内生成它们。另请注意代码中的任何低效率。我主要写它是为了确保我理解它是如何工作的。这是代码。

另外,我会分享项目in Dropbox,以防有人想在那里查看。

using System;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RSAinCS
{
    public partial class Form1 : Form
    {
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        struct RSAKey
        {
            internal BigInteger p; // one prime factor of the modulus
            internal BigInteger q; // another prime factor of the modulus
            internal BigInteger n; // modulus, a part of both the public key and the private key
            internal BigInteger totient; // product of p-1 and q-1.  Must be kept secret.  We calculate it because we need to confirm our public exponent (65537) doesn't divide it evenly
            internal BigInteger e; // public exponent.  Together with n forms the public key
            internal BigInteger d; // private exponent.  Together with n forms the private key.  Must be kept secret
        }

        struct EGCDReturnStruct
        {
            internal BigInteger g;
            internal BigInteger x;
            internal BigInteger y;
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int bitLength = 0;
            Int32.TryParse(comboBox1.Text, out bitLength);
            if (bitLength == 0) return;
            Task.Run(() => { doWork(bitLength); });
        }

        void doWork(int bitLength)
        {
            RSAKey rsaKey = generateKey(bitLength);
            BeginInvoke(new Action(() => textBox2.Text = rsaKey.p.ToString()));
            BeginInvoke(new Action(() => textBox3.Text = rsaKey.q.ToString()));
            BeginInvoke(new Action(() => textBox4.Text = rsaKey.n.ToString()));
            BeginInvoke(new Action(() => textBox5.Text = rsaKey.totient.ToString()));
            BeginInvoke(new Action(() => textBox6.Text = rsaKey.e.ToString()));
            BeginInvoke(new Action(() => textBox7.Text = rsaKey.d.ToString()));
            BeginInvoke(new Action(() => textBox8.Text = textBox1.Text));
            BigInteger message = convertTextToBigInteger(textBox8.Text);
            BeginInvoke(new Action(() => textBox9.Text = message.ToString()));
            BigInteger cipherText = BigInteger.ModPow (message,rsaKey.e,rsaKey.n);
            BeginInvoke(new Action(() => textBox10.Text = cipherText.ToString()));
            BigInteger decryptedCipherText = BigInteger.ModPow(cipherText, rsaKey.d, rsaKey.n);
            BeginInvoke(new Action(() => textBox11.Text = decryptedCipherText.ToString()));
            BeginInvoke(new Action(() => textBox12.Text = convertBigIntegerToText(decryptedCipherText)));
        }

        string convertBigIntegerToText(BigInteger b)
        {
            StringBuilder sb = new StringBuilder();
            byte[] letterByte = new byte[1];
            string letter;
            while (b > 0)
            {
                letterByte[0] = (byte)(b % 256);
                letter = ASCIIEncoding.UTF8.GetString(letterByte);
                sb.Append(letter);
                b /= 256;
            }
            return sb.ToString();
        }

        BigInteger convertTextToBigInteger(string s)
        {
            BigInteger textInteger = 0;
            byte[] textBytes = ASCIIEncoding.UTF8.GetBytes(s);
            for (int i = 0; i < textBytes.Length; i++)
            {
                textInteger += textBytes[textBytes.Length-1-i];
                if (i < textBytes.Length - 1) textInteger *= 256;
            }
            return textInteger;
        }

        RSAKey generateKey(int bitLength)
        {
            RSAKey rsaKey = new RSAKey();
            // Generate 2 large primes.  The first will be p, and the second will be q.
            for (int i = 0; i < 2; i++)
            {
                // The bit length of each prime, p and q, is half the bit length of the whole modulus, and we divide by 8 for byte length
                byte[] primeBytes = new byte[bitLength / 16];
                rng.GetBytes(primeBytes);
                BigInteger primeNumber = 0;
                for (int j = 0; j < primeBytes.Length; j++)
                {
                    primeNumber += primeBytes[j];
                    if (j < primeBytes.Length - 1) primeNumber *= 256;
                }
                if (primeNumber % 2 == 0) primeNumber++; // Make our prime odd

                // This next bit of code ensures zeros in the high bits don't give us small (less secure) prime factors of the modulus
                BigInteger highBitValue = BigInteger.Pow(2, (bitLength / 2) - 1);
                BigInteger secondBitValue = BigInteger.Pow(2, (bitLength / 2) - 2);
                if (primeNumber < secondBitValue) primeNumber += secondBitValue;
                if (primeNumber < highBitValue) primeNumber += highBitValue;

                if (isProbablyPrime(primeNumber, 100) == true)
                {
                    if (i == 0) rsaKey.p = primeNumber;
                    else rsaKey.q = primeNumber;
                }
                else
                {
                    i--;
                    continue;
                }
            }
            rsaKey.n = rsaKey.p * rsaKey.q;
            rsaKey.totient = (rsaKey.p - 1) * (rsaKey.q - 1);
            // A little recursion.  Checks if totient is OK for use with our chose public exponent.  If not, runs method again
            // I also have it repeat if the modInv method returns a value less than 0.  This may be fixable in the modInv or egcd method, not sure
            if (rsaKey.totient % 65537 == 0 || modInv(65537, rsaKey.totient) < 0) return generateKey(bitLength);
            //if (rsaKey.totient % 65537 == 0) return generateKey(bitLength);
            else
            {
                rsaKey.e = 65537;
                rsaKey.d = modInv(rsaKey.e, rsaKey.totient);
                return rsaKey;
            }
        }

        BigInteger modInv(BigInteger a, BigInteger m)
        {
            EGCDReturnStruct eGCDReturnStruct = new EGCDReturnStruct();
            eGCDReturnStruct = egcd(a, m);
            if (eGCDReturnStruct.g != 1) throw new Exception("Modular Inverse Does Not Exist");
            else return eGCDReturnStruct.x % m;
        }

        EGCDReturnStruct egcd(BigInteger a, BigInteger b)
        {
            EGCDReturnStruct eGCDReturnStruct = new EGCDReturnStruct();
            if (a == 0)
            {
                eGCDReturnStruct.g = b;
                eGCDReturnStruct.x = 0;
                eGCDReturnStruct.y = 1;
                return eGCDReturnStruct;
            }
            else
            {
                eGCDReturnStruct = egcd(b % a, a);
                BigInteger temp = eGCDReturnStruct.x;
                eGCDReturnStruct.x = eGCDReturnStruct.y;
                eGCDReturnStruct.y = temp;
                eGCDReturnStruct.x -= (b / a) * eGCDReturnStruct.y;
                return eGCDReturnStruct;
            }
        }

        bool isProbablyPrime(BigInteger testNumber, int confidence)
        {
            int[] firstPrimes = {2,3,5,7,11,13,17,19};

            for (int i = 0; i < firstPrimes.Length; i++)
            {
                if ((testNumber % firstPrimes[i]) == 0) return false;
            }
            for (int i = 2; i < 101; i++)
            {
                if (BigInteger.ModPow(i, testNumber - 1, testNumber) != 1) return false;
            }
            BigInteger nMinusOne = testNumber - 1;
            BigInteger nMinusOneTemp = nMinusOne;
            int s = 0;
            while (nMinusOneTemp % 2 == 0)
            {
                s++;
                nMinusOneTemp /= 2;
            }
            BigInteger d = nMinusOneTemp;
            bool probablyPrime = true;
            int counter = 0;
            int a = 2;
            while ((counter < confidence) & (probablyPrime == true) & (a < testNumber))
            {
                counter++;
                probablyPrime = false;
                if (BigInteger.ModPow(a, d, testNumber) == 1)
                {
                    probablyPrime = true;
                }
                else
                {
                    for (int r = 0; r < s; r++)
                    {
                        BigInteger j = BigInteger.ModPow(a, d, testNumber);
                        for (BigInteger t = 0; t < r; t++)
                        {
                            j = (j * BigInteger.ModPow(j, 2, testNumber)) % testNumber;
                        }
                        if (j == nMinusOne)
                        {
                            probablyPrime = true;
                            break;
                        }
                    }
                }
                if (probablyPrime == true)
                {
                    a++;
                }
                else
                {
                    return false;
                }
            }
            return true;
        }
    }
}