简体   繁体   English

Java 7:使用密码“ RSA / ECB / OAEPWithSHA1AndMGF1Padding”使用公共RSA密钥加密的输出与openssl命令不匹配

[英]Java 7 : output from encryption with public RSA key using cipher “RSA/ECB/OAEPWithSHA1AndMGF1Padding” does not match with openssl command

We have to encrypt our data with HMAC-SHA256 that needs randomly generated salt. 我们必须使用需要随机生成的盐的HMAC-SHA256加密数据。 We are generating the salt this way: 我们以这种方式产生盐:

 public String generateSalt() throws Exception
    {
        KeyGenerator keyGen;
        String salt = null;
        try
        {
            keyGen = KeyGenerator.getInstance( "HmacSHA256" );
            keyGen.init( 128 );
            SecretKey key = keyGen.generateKey();

            byte[] encodedKey = key.getEncoded();
            salt = Base64.encodeBase64String( key.getEncoded() );
            LOG.info( "Salt : " + salt );

        }
        catch ( NoSuchAlgorithmException  )
        {
            e.printStackTrace();
            throw e;
        }

        return salt;
    }

According to our test this salt generation part is right. 根据我们的测试,该盐生成部分是正确的。 I have issue with the next part: 我对下一部分有疑问:

Now I have to write this salt in binary format in a file ( say named as pie_raw) and that's been done as: 现在,我必须以二进制格式将此盐写入文件(例如,命名为pie_raw),并且可以这样完成:

    private void writeToFile( byte[] saltBytes, String fileName ) throws FileNotFoundException, IOException
    {

        DataOutputStream out = new DataOutputStream( new FileOutputStream( enviro.getOutputFilePath()
                + fileName ) );

        out.write( saltBytes );
        out.close();
        LOG.info( " Raw file created : " + enviro.getOutputFilePath() + fileName );
    }

And then, I have to encrypt this salt with a supplied public RSA key in ".pem" and for Java implementation, the cipher will be "RSA/ECB/OAEPWithSHA1AndMGF1Padding". 然后,我必须使用“ .pem”中提供的公共RSA密钥对此盐进行加密,对于Java实现,密码将为“ RSA / ECB / OAEPWithSHA1AndMGF1Padding”。 And finally the binary ciphertext should be written to a file named "pie_key". 最后,应将二进制密文写入名为“ pie_key”的文件中。 This part has been implemented this way: 此部分已通过以下方式实现:

    private byte[] encryptSalt( String salt ) throws Exception
    {
        byte[] cipheredKey = null;
        try
        {

             String keyString= readKeyFile( enviro.getPublicKeyFile() );
             byte[] pem = pemToDer(keyString); 
             PublicKey publicKey = derToPublicKey(pem);


            //PublicKey publicKey = getPublicKey( enviro.getPublicKeyFile() );
            // Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
            rsaCipher.init( Cipher.ENCRYPT_MODE, publicKey );
            cipheredKey = rsaCipher.doFinal( salt.getBytes( "UTF-8" ) );//"UTF-8"
            LOG.info( "Cyphered key : " + cipheredKey.toString() );

        }
        catch ( IOException | GeneralSecurityException e )
        {
            e.printStackTrace();
            throw e;
        }

        return cipheredKey;
    }

    static String readKeyFile( String path )
            throws IOException
    {
        String line = null;

        try (BufferedReader br =
                new BufferedReader( new FileReader( path ) ))
        {

            StringBuilder sb = new StringBuilder();
            line = br.readLine();

            while ( line != null )
            {
                sb.append( line );
                sb.append( "\n" );
                line = br.readLine();
            }

            return sb.toString();
        }

    }

    public static byte[] pemToDer( String pemKey ) throws GeneralSecurityException
    {
        String[] parts = pemKey.split( "-----" );
        return DatatypeConverter.parseBase64Binary( parts[ parts.length / 2 ] );
    }

    public static PublicKey derToPublicKey( byte[] asn1key ) throws GeneralSecurityException
    {
        X509EncodedKeySpec spec = new X509EncodedKeySpec( asn1key );
        KeyFactory keyFactory = KeyFactory.getInstance( "RSA" );
        return keyFactory.generatePublic( spec );
    }

Writing this encrypted salt to a file named as "pie_key" in binary format by calling the "writeToFile" method above. 通过调用上面的“ writeToFile”方法,将该加密的盐以二进制格式写入名为“ pie_key”的文件。

Now the content of the file "pie_key" should match the out put of the cmd : 现在,文件“ pie_key”的内容应与cmd的输出匹配:

openssl rsautl -encrypt -pubin -inkey wrap_pie_key_rsa.pem -oaep -in pie_key.raw -out pie_key

But whatever I tried ( you may find some signs of the ways, I tried ) did not work means that the final binary-encrypted-salt did not match with the output of openssl cmd. 但是,无论我尝试了什么(您可能会找到一些尝试的迹象,我都尝试过)都不起作用,这意味着最终的二进制加密盐与openssl cmd的输出不匹配。

Any idea what I am doing wrong? 知道我在做什么错吗?

I am using Java 7. And the .pem (partial) looks like 我正在使用Java7。.pem(部分)看起来像

-----BEGIN PUBLIC KEY-----
MIIBIjANBgk345iG9w0BAQEFAA54328AMIIBCgKCAQEAt4GLJGPmvYdxwwAe59n3
. 
.
.
.
7QIDNQAB
-----END PUBLIC KEY-----

Thanks in advance. 提前致谢。

First of all, as Artjom already mentioned, the padding for OAEP or PKCS#1 v1.5 compatible padding is randomized. 首先,如Artjom所述,OAEP或PKCS#1 v1.5兼容填充的填充是随机的。 So even if you encrypt the same salt multiple times you would not get the same value. 因此,即使您多次加密相同的盐,也不会获得相同的值。 You can only decrypt the result to see if encryption succeeded. 您只能解密结果以查看加密是否成功。


Furthermore, you say you need a binary salt, but you first encode the salt to base64. 此外,您说您需要二进制盐,但首先将盐编码为base64。 It's unlikely that your encryption should contain an encoded salt. 您的加密不太可能包含编码的盐。 Maybe you need to encode the output of the encryption, not the salt. 也许您需要对加密输出而不是盐进行编码。

The spurious encoding happens in the following line: 伪编码发生在以下行中:

salt = Base64.encodeBase64String( key.getEncoded() );

Finally, although a new HMAC key generally consists of fully random bytes, I would say that it is not the right way to generate a salt. 最后,尽管新的HMAC密钥通常由完全随机的字节组成,但我想这不是生成盐的正确方法。 Instead just use: 而是使用:

SecureRandom rngForSalt = new SecureRandom();
byte[] salt = new byte[SALT_SIZE];
rngForSalt.nextBytes(salt);

Note too that the Bouncy Castle lightweight API (ie calling org.bouncycastle functionality directly) contains a PEM codec. 还要注意,Bouncy Castle轻量级API(即直接调用org.bouncycastle功能)包含PEM编解码器。 No need to program or hack that yourself. 无需自己编程或修改。


Try this Java 8 code. 试试这个Java 8代码。 Bouncy castle provider classes required (no need to register the provider, this is just for the PEM handling). 需要Bouncy Castle提供程序类(无需注册提供程序,这仅用于PEM处理)。

package nl.maartenbodewes.stackoverflow;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;

public class GenerateAndWrapHMACKey {

    public static SecretKey generateHMACKey() throws Exception {
        final KeyGenerator keyGen;
        try {
            keyGen = KeyGenerator.getInstance("HmacSHA256");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("HMAC KeyGeneration should be available");
        }
        keyGen.init(128);
        SecretKey key = keyGen.generateKey();
        return key;
    }

    public static void writeToFile(SecretKey key, String filename)
            throws IOException {
        // file handling probably should be in a separate class
        Files.write((new File(filename)).toPath(), key.getEncoded());
    }

    public static RSAPublicKey readRSAPublicKey(String filename) throws IOException, InvalidKeySpecException {
        try (PemReader reader = new PemReader(new FileReader(filename))) {
            PemObject pemObject = reader.readPemObject();
            KeyFactory kf;
            try {
                kf = KeyFactory.getInstance("RSA");
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("RSA key factory not available", e);
            }
            KeySpec keySpec = new X509EncodedKeySpec(pemObject.getContent());
            try {
                return (RSAPublicKey) kf.generatePublic(keySpec);
            } catch (ClassCastException e) {
                throw new InvalidKeySpecException("That's no RSA key", e);
            }
        }
    }

    public static byte[] wrapKey(Key key, RSAPublicKey wrappingKey) throws InvalidKeyException, IllegalBlockSizeException {
        Cipher rsaWrapper;
        try {
            rsaWrapper = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
            rsaWrapper.init(Cipher.WRAP_MODE, wrappingKey);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
            throw new RuntimeException("RSA OAEP should be available for RSA public key", e);
        }
        return rsaWrapper.wrap(key);
    }

    public static void main(String[] args) throws Exception {
        // we need an RSA PEM key first I guess :)
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(1024, new SecureRandom());
        KeyPair kp = kpg.generateKeyPair();
        String publicKeyFilename = "rsa_pub.pem";
        try (PemWriter pemWriter = new PemWriter(new FileWriter(publicKeyFilename))) {
            pemWriter.writeObject(new PemObject("PUBLIC KEY", kp.getPublic().getEncoded()));
        }

        RSAPublicKey wrappingRSAPublicKey = readRSAPublicKey(publicKeyFilename);
        SecretKey hmacKey = generateHMACKey();
        byte[] wrappedKey = wrapKey(hmacKey, wrappingRSAPublicKey);
        System.out.println(Base64.getEncoder().encodeToString(wrappedKey));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 java RSA/ECB/OAEPWithSHA 256AndMGF1Padding 在 golang 中等效 - java RSA/ECB/OAEPWithSHA 256AndMGF1Padding equivalent in golang C# Bouncy Castle 中的 RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING - 对于 RSA 密码输入太大 - RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING in C# Bouncy Castle - input too large for RSA cipher “RSA/ECB/OAEPWITHSHA256ANDMGF1PADDING”和“RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING”之间的区别是什么 - what is deference between "RSA/ECB/OAEPWITHSHA256ANDMGF1PADDING" and "RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING" 获取 ruby 的“RSA/ECB/OAEPWithSHA-256AndMGF1Padding”组合 - Get this “RSA/ECB/OAEPWithSHA-256AndMGF1Padding” combination for ruby Java的RSA / ECB / OAEPWITHSHA-256ANDMGF1PADDING在PHP中等效 - Java's RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING equivalent in PHP 分解 RSA/ECB/OAEPWithSHA-256AndMGF1Padding - Breaking down RSA/ECB/OAEPWithSHA-256AndMGF1Padding C#等效于Java RSA / ECB / OAEPWithSHA-256AndMGF1Padding - C# equivalent to Java RSA/ECB/OAEPWithSHA-256AndMGF1Padding Java 的 RSA/ECB/OAEPWithSHA-256AndMGF1Padding 在 Node.js 中的等价物 - Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding equivalent in Node.js RSA / ECB / OAEPWithSHA-256AndMGF1Padding但MGF1使用SHA-256? - RSA/ECB/OAEPWithSHA-256AndMGF1Padding but with MGF1 using SHA-256? 错误的填充例外-pkcs11中的RSA / ECB / OAEPWITHSHA-256ANDMGF1PADDING - Bad Padding Exception - RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING in pkcs11
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM