Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在没有库的Java中读取PKCS#1或SPKI公钥_Java_Encryption_Public Key Encryption_Java Security - Fatal编程技术网

在没有库的Java中读取PKCS#1或SPKI公钥

在没有库的Java中读取PKCS#1或SPKI公钥,java,encryption,public-key-encryption,java-security,Java,Encryption,Public Key Encryption,Java Security,我需要使用公钥来验证Java中的一些数据,但我似乎无法将密钥格式化为Java在没有第三方插件的情况下可以使用的方式 我使用Node.js的crypto库生成密钥,该库提供了PKCS#1或SPKI选项,以及.pem或.der文件格式 我听说Java不支持开箱即用的PKCS#1,关于StackOverflow的几乎所有其他答案都建议使用BouncyCastle或类似工具,但就我而言,我正在编写一个SDK,根本无法使用库来读取此公钥 因此,我目前正在读取.der格式的密钥,因为它省去了剥离PEM头并从

我需要使用公钥来验证Java中的一些数据,但我似乎无法将密钥格式化为Java在没有第三方插件的情况下可以使用的方式

我使用Node.js的
crypto
库生成密钥,该库提供了
PKCS#1
SPKI
选项,以及.pem或.der文件格式

我听说Java不支持开箱即用的
PKCS#1
,关于StackOverflow的几乎所有其他答案都建议使用BouncyCastle或类似工具,但就我而言,我正在编写一个SDK,根本无法使用库来读取此公钥

因此,我目前正在读取.der格式的密钥,因为它省去了剥离PEM头并从base-64解码密钥的麻烦。当我运行此操作时,我会得到以下错误:

java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0000be:ASN.1 encoding routines:OPENSSL_internal:WRONG_TAG
以下是我所拥有的(抱歉,它是用Kotlin编写的,而不是像标题所示的Java)


目前我最好的办法是在Node.js端安装一个库,这是一个问题较少的库,以支持将密钥导出为PKCS#8,但我想我应该先检查一下是否缺少任何内容。

以下代码将PKCS#1编码的公钥转换为SubjectPublicKeyInfo编码的公钥,这是RSA
KeyFactory使用
X509EncodedKeySpec
接受的公钥编码,因为X.509规范中定义了SubjectPublicKeyInfo

基本上,它是一种低级别的DER编码方案

  • 将PKCS#1编码密钥包装为位字符串(标记为
    0x03
    ,编码为未使用的位数,字节值为
    0x00
  • 在前面添加RSA算法标识符序列(RSA OID+空参数),预编码为字节数组常量
  • 最后将这两个元素放入一个序列中(tag
    0x30
  • 没有使用任何库。实际上,对于
    createSubjectPublicKeyInfoEncoding
    ,甚至不需要导入语句


    导入java.security.KeyFactory;
    导入java.security.NoSuchAlgorithmException;
    导入java.security.interfaces.RSAPublicKey;
    导入java.security.spec.InvalidKeySpecException;
    导入java.security.spec.X509EncodedKeySpec;
    导入java.util.Base64;
    公共类PKCS1对象PublicKeyInfo{
    私有静态最终整数序列_标记=0x30;
    私有静态最终整数位\字符串\标记=0x03;
    私有静态最终字节[]无未使用字节=新字节[]{0x00};
    私有静态最终字节[]RSA算法标识符序列=
    {(字节)0x30,(字节)0x0d,
    (字节)0x06,(字节)0x09,(字节)0x2a,(字节)0x86,(字节)0x48,(字节)0x86,(字节)0xf7,(字节)0x0d,(字节)0x01,(字节)0x01,(字节)0x01,
    (字节)0x05,(字节)0x00};
    公共静态RSAPublicKey decodePKCS1PublicKey(字节[]pkcs1PublicKeyEncoding)
    抛出NoSuchAlgorithmException、InvalidKeySpecException
    {
    字节[]subjectPublicKeyInfo2=createSubjectPublicKeyInfoEncoding(pkcs1PublicKeyEncoding);
    KeyFactory rsaKeyFactory=KeyFactory.getInstance(“RSA”);
    RSAPublicKey generatePublic=(RSAPublicKey)rsaKeyFactory.generatePublic(新的X509EncodedKeySpec(subjectPublicKeyInfo2));
    返回生成公共;
    }
    公共静态字节[]createSubjectPublicKeyInfoEncoding(字节[]pkcs1PublicKeyEncoding)
    {
    byte[]subjectPublicKeyBitString=CreateDeRecoded(位字符串标记,concat(无未使用位,pkcs1PublicKeyEncoding));
    字节[]subjectPublicKeyInfoValue=concat(RSA算法标识符序列,subjectPublicKeyBitString);
    字节[]subjectPublicKeyInfoSequence=CreatedEncoding(序列标记,subjectPublicKeyInfoValue);
    返回subjectPublicKeyInfoSequence;
    }
    专用静态字节[]concat(字节[]…bas)
    {
    int len=0;
    对于(int i=0;i=0xFF)
    {
    抛出新的IllegalArgumentException(“当前仅支持单字节标记”);
    }
    字节[]LengthCoding=CreateDerLengthCoding(值.length);
    int size=1+lengtencoding.length+value.length;
    字节[]DeRecodingBuf=新字节[大小];
    int off=0;
    DeRecodingBuf[off++]=(字节)标记;
    System.arraycopy(LengthCoding,0,DeRecodingBuf,off,LengthCoding.length);
    off+=lengtencoding.length;
    System.arraycopy(值,0,DeRecodingBuf,关闭,值.长度);
    返回DerecodingBuf;
    }   
    私有静态字节[]CreateDerLengthCoding(整数大小)
    {
    
    如果(虽然SDK是针对Android的,因此有一个精简版的BouncyCastle可用,但我相信它只是作为密码的提供者,因此没有用处。此外,提供纯Java解决方案对未来的读者更有帮助。由于nodejs支持SPKI,这是Java称之为X509Encoded的,所以使用起来最简单那就是。要么使用PEM,在java中去掉头/尾并解码base64,要么使用DER,在java中按原样使用。正如nodejs doc所说,PKCS8用于私钥而不是公钥。顺便说一句,617位对于RSA来说是一个奇怪的大小,太小而不安全。
    // Here's a key for convenience
    val key = Base64.getDecoder().decode("MFUCTgF/uLsPBS13Gy7C3dPpiDF6SYCLUyyl6CFqPtZT1h5bwKR9EDFLQjG/kMiwkRMcmEeaLKe5qdj9W/FfFitwRAm/8F53pQw2UETKQI2b2wIDAQAB");
    
    val keySpec = X509EncodedKeySpec(key)
    val keyFactory = KeyFactory.getInstance("RSA")
    val publicKey = keyFactory.generatePublic(keySpec) // error thrown here
    
    val cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding")
    cipher.init(Cipher.DECRYPT_MODE, publicKey)
    
    import java.security.KeyFactory;
    import java.security.NoSuchAlgorithmException;
    import java.security.interfaces.RSAPublicKey;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.Base64;
    
    public class PKCS1ToSubjectPublicKeyInfo {
    
        private static final int SEQUENCE_TAG = 0x30;
        private static final int BIT_STRING_TAG = 0x03;
        private static final byte[] NO_UNUSED_BITS = new byte[] { 0x00 };
        private static final byte[] RSA_ALGORITHM_IDENTIFIER_SEQUENCE =
                {(byte) 0x30, (byte) 0x0d,
                        (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01,
                        (byte) 0x05, (byte) 0x00};
    
    
        public static RSAPublicKey decodePKCS1PublicKey(byte[] pkcs1PublicKeyEncoding)
                throws NoSuchAlgorithmException, InvalidKeySpecException
        {
            byte[] subjectPublicKeyInfo2 = createSubjectPublicKeyInfoEncoding(pkcs1PublicKeyEncoding);
            KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
            RSAPublicKey generatePublic = (RSAPublicKey) rsaKeyFactory.generatePublic(new X509EncodedKeySpec(subjectPublicKeyInfo2));
            return generatePublic;
        }
    
        public static byte[] createSubjectPublicKeyInfoEncoding(byte[] pkcs1PublicKeyEncoding)
        {
            byte[] subjectPublicKeyBitString = createDEREncoding(BIT_STRING_TAG, concat(NO_UNUSED_BITS, pkcs1PublicKeyEncoding));
            byte[] subjectPublicKeyInfoValue = concat(RSA_ALGORITHM_IDENTIFIER_SEQUENCE, subjectPublicKeyBitString);
            byte[] subjectPublicKeyInfoSequence = createDEREncoding(SEQUENCE_TAG, subjectPublicKeyInfoValue);
    
            return subjectPublicKeyInfoSequence;
        }
    
        private static byte[] concat(byte[] ... bas)
        {
            int len = 0;
            for (int i = 0; i < bas.length; i++)
            {
                len += bas[i].length;
            }
    
            byte[] buf = new byte[len];
            int off = 0;
            for (int i = 0; i < bas.length; i++)
            {
                System.arraycopy(bas[i], 0, buf, off, bas[i].length);
                off += bas[i].length;
            }
    
            return buf;
        }
    
        private static byte[] createDEREncoding(int tag, byte[] value)
        {
            if (tag < 0 || tag >= 0xFF)
            {
                throw new IllegalArgumentException("Currently only single byte tags supported");
            }
    
            byte[] lengthEncoding = createDERLengthEncoding(value.length);
    
            int size = 1 + lengthEncoding.length + value.length;
            byte[] derEncodingBuf = new byte[size];
    
            int off = 0;
            derEncodingBuf[off++] = (byte) tag;
            System.arraycopy(lengthEncoding, 0, derEncodingBuf, off, lengthEncoding.length);
            off += lengthEncoding.length;
            System.arraycopy(value, 0, derEncodingBuf, off, value.length);
    
            return derEncodingBuf;
        }   
    
        private static byte[] createDERLengthEncoding(int size)
        {
            if (size <= 0x7F)
            {
                // single byte length encoding
                return new byte[] { (byte) size };
            }
            else if (size <= 0xFF)
            {
                // double byte length encoding
                return new byte[] { (byte) 0x81, (byte) size };
            }
            else if (size <= 0xFFFF)
            {
                // triple byte length encoding
                return new byte[] { (byte) 0x82, (byte) (size >> Byte.SIZE), (byte) size };
            }
    
            throw new IllegalArgumentException("size too large, only up to 64KiB length encoding supported: " + size);
        }
    
        public static void main(String[] args) throws Exception
        {
            // some weird 617 bit key, which is way too small and not a multiple of 8
            byte[] pkcs1PublicKeyEncoding = Base64.getDecoder().decode("MFUCTgF/uLsPBS13Gy7C3dPpiDF6SYCLUyyl6CFqPtZT1h5bwKR9EDFLQjG/kMiwkRMcmEeaLKe5qdj9W/FfFitwRAm/8F53pQw2UETKQI2b2wIDAQAB");
            RSAPublicKey generatePublic = decodePKCS1PublicKey(pkcs1PublicKeyEncoding);
            System.out.println(generatePublic);
        }
    }