zoukankan      html  css  js  c++  java
  • JAVA解析各种编码密钥对(DER、PEM、openssh公钥)

    一、DER编码密钥对

    先说下DER编码,是因为JCE本身是支持DER编码密钥对的解析的,可以参见PKCS8EncodedKeySpec和X509EncodedKeySpec.

    DER编码是ASN.1编码规则中的一个子集,具体格式如何编排没有去了解,但最终呈现肯定的是一堆有规律的二进制组合而成。

    PKCS#8定义了私钥信息语法和加密私钥语法,而X509定义证书规范,通常都会用DER和PEM进行编码存储,而在JAVA中则使用的

    DER。

    接下来看看如果通过DER编码的密钥对分别获取JAVA的公私钥对象。

    1.下面一段是生成私钥对象的,传入参数是DER编码的私钥内容。

    1. @Override  
    2. public PrivateKey generatePrivateKey(byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException {  
    3.     KeySpec keySpec = new PKCS8EncodedKeySpec(key);  
    4.     KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
    5.     return keyFactory.generatePrivate(keySpec);  
    6. }  


    2.下面是生成公钥对象的,传入参数是DER编码公钥内容,可以看到和生成私钥的部分非常相似。

    1. public PublicKey geneneratePublicKey(byte[] key) throws InvalidKeySpecException, NoSuchAlgorithmException{  
    2.     KeySpec keySpec = new X509EncodedKeySpec(key);  
    3.     KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
    4.     return keyFactory.generatePublic(keySpec);  
    5. }  

    二、PEM编码

    PEM编码也是密钥对较常用的编码方式,openssl则是以PEM编码为主,相对DER对人可读性更强,以BASE64编码呈现,外围包上类似-----BEGIN RSA PRIVATE KEY-----。

    JCE没有对PEM直接支持的方式,但是可以通过第三方包例如bouncycastle解析,当然如果想要自己理解pem编码结构,也可以自己写代码解析。

    这里介绍下如何使用bouncycastle进行解析。

    1.     FileInputStream fis = new FileInputStream("id_rsa");  
    2.     byte[] key = PrivateKeyUtils.readStreamToBytes(fis);  
    3. Security.addProvider(new BouncyCastleProvider());  
    4. ByteArrayInputStream bais = new ByteArrayInputStream(key);  
    5. PEMReader reader = new PEMReader(new InputStreamReader(bais), new PasswordFinder() {  
    6.       
    7.     @Override  
    8.     public char[] getPassword() {  
    9.         return "".toCharArray();  
    10.     }  
    11. });  
    12. KeyPair keyPair = (KeyPair) reader.readObject();  
    13. reader.close();  
    14. PublicKey pubk = keyPair.getPublic();  
    15. System.out.println(pubk);  
    16. PrivateKey prik = keyPair.getPrivate();  
    17. System.out.println(prik);  
    18.   
    19. KeySpec keySpec = new X509EncodedKeySpec(pubk.getEncoded());  
    20. KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
    21. System.out.println(keyFactory.generatePublic(keySpec));  
    22.   
    23. KeySpec keySpec2 = new PKCS8EncodedKeySpec(prik.getEncoded());  
    24. System.out.println(keyFactory.generatePrivate(keySpec2));  



    
    
    
    

    看下这个输出结果

    1. RSA Public Key  
    2.             modulus: c8f3e2d2e7fffe049127a115cab648fa9f55a7712d40868dccbddef9ebf030480a31f060e6c1ace2c53660e467cd173870367223dccea00ef2bdf6795757eb34fe1e8cfb63a0d333eefc9739029512df68108dd4b8054a12bdb206abd2ee7a727faa79604680c1337473ecd3d9a1a10b6cbc3af7862a74619ea7fe3a5bb2b89dded41dc5e4e4d5fcad169b85a599487929de1788e1e9a8d4efae4fda811d1e4d975b50d0d61b5887402ca975ec5e1d3ff193522b84746fe35ab00d073fed466786d9303f19c642c02cb1ad3a1ec6f0b7234e492e79500ee0bb1c1f07c23ae70af9b75aa35a6c75573d302cbf8f034341932dc371689b9f952388328c5277c117  
    3.     public exponent: 10001  
    4.   
    5. RSA Private CRT Key  
    6.             modulus: c8f3e2d2e7fffe049127a115cab648fa9f55a7712d40868dccbddef9ebf030480a31f060e6c1ace2c53660e467cd173870367223dccea00ef2bdf6795757eb34fe1e8cfb63a0d333eefc9739029512df68108dd4b8054a12bdb206abd2ee7a727faa79604680c1337473ecd3d9a1a10b6cbc3af7862a74619ea7fe3a5bb2b89dded41dc5e4e4d5fcad169b85a599487929de1788e1e9a8d4efae4fda811d1e4d975b50d0d61b5887402ca975ec5e1d3ff193522b84746fe35ab00d073fed466786d9303f19c642c02cb1ad3a1ec6f0b7234e492e79500ee0bb1c1f07c23ae70af9b75aa35a6c75573d302cbf8f034341932dc371689b9f952388328c5277c117  
    7.     public exponent: 10001  
    8.    xxx  
    9.   
    10. Sun RSA public key, 2048 bits  
    11.   modulus: 25367925677263221630752072905429434117596189021449325931333193529363239091429133073657699480437320802724298965580526553453499379398405915207286949216370963889754756788008021698178495726807109888833039800230667805051637457878962812581009778614579379073430749907762728841603230968432191178635884450213875555645164935313884823663096624318071901833679005494934145072511042211644746801428698070096755012497436134537077746175344235590315572214836519284172251946833712681076781034466422251569387242330311670205489884189790153154281087401570994337126054046621401176808489895271448688335849540690562754961439975230588159770903  
    12.   public exponent: 65537  
    13. Sun RSA private CRT key, 2048 bits  
    14.   modulus:          25367925677263221630752072905429434117596189021449325931333193529363239091429133073657699480437320802724298965580526553453499379398405915207286949216370963889754756788008021698178495726807109888833039800230667805051637457878962812581009778614579379073430749907762728841603230968432191178635884450213875555645164935313884823663096624318071901833679005494934145072511042211644746801428698070096755012497436134537077746175344235590315572214836519284172251946833712681076781034466422251569387242330311670205489884189790153154281087401570994337126054046621401176808489895271448688335849540690562754961439975230588159770903  
    15.   public exponent:  65537  
    16.   xxx  

    中间内容太多,略去了一部分,看下重点的public exponent部分,发现不同,但其实是一个是10进制输出,一个是16进制输出,所以在这里提个醒,这里生成过程没有错的。

    三、openssh公钥

    很多SSH公钥习惯使用openssh格式的,下面介绍下openssh格式的公钥如何解析,目前好像是没有官方库或者第三方库提供支持的。

    openssh公钥呈现形式如

    1. ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCW6qYq6m8gVOWLyTB1JGl1aLrJDOCIfErXWNUsNeUXg4UdAtSbkiA+Ta9Nx6oMR4w+OkPbxyivnzkZt1YpmDxrm1z99z81/VyVw+lue+3neRjTgfGMascG+46b7DpEKLXlfS2hwOA+4ooRIeR+LbQZVovy5SP6ZTngskiqcySYqQ== RSA-1024  



    以ssh-rsa打头,描述“RSA-1024”结尾的形式,中间是Base64编码。

    这里过滤掉除了Base64外的其他部分,解码Base64得到公钥二进制内容。

    这里二进制编码格式如下:

    前11字节固定

    0 0 0 7  's' 's' 'h' '-' ‘r' 's' 'a' 

    紧接着4个字节为一个int值,表示public exponent所占字节长度

    可通过移位符及相加或者BigInteger方式实现转换。

    1.     public static int decodeUInt32(byte[] key, int start_index){  
    2.         byte[] test = Arrays.copyOfRange(key, start_index, start_index + 4);  
    3.         return new BigInteger(test).intValue();  
    4. //      int int_24 = (key[start_index++] << 24) & 0xff;  
    5. //      int int_16 = (key[start_index++] << 16) & 0xff;  
    6. //      int int_8 = (key[start_index++] << 8) & 0xff;  
    7. //      int int_0 = key[start_index++] & 0xff;  
    8. //      return int_24 + int_16 + int_8 + int_0;  
    9.     }  


    得到长度后,再从后一字节开始读取该长度字节作为public exponent的值,构造相应BigInteger。

    再紧接着4个字节也是一个int值,表示modulus所占字节长度,同理转换得到长度。

    再根据长度读取字节数组得到modulus值即可。

    相应代码如下

    1. public static RSAPublicKey decodePublicKey(byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException{  
    2.     byte[] sshrsa = new byte[] { 0007's''s''h''-''r''s',  
    3.     'a' };  
    4.     int start_index = sshrsa.length;  
    5.     /* Decode the public exponent */  
    6.     int len = decodeUInt32(key, start_index);  
    7.     start_index += 4;  
    8.     byte[] pe_b = new byte[len];  
    9.     for(int i= 0 ; i < len; i++){  
    10.         pe_b[i] = key[start_index++];  
    11.     }  
    12.     BigInteger pe = new BigInteger(pe_b);  
    13.     /* Decode the modulus */  
    14.     len = decodeUInt32(key, start_index);  
    15.     start_index += 4;  
    16.     byte[] md_b = new byte[len];  
    17.     for(int i = 0 ; i < len; i++){  
    18.         md_b[i] = key[start_index++];  
    19.     }  
    20.     BigInteger md = new BigInteger(md_b);  
    21.     KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
    22.     KeySpec ks = new RSAPublicKeySpec(md, pe);  
    23.     return (RSAPublicKey) keyFactory.generatePublic(ks);  
    24. }  



    四、其他编码

    后续有机会再研究其他编码方式如何解析,不过可能bouncycastle已经提供了许多编码的解析,可以直接使用,具体没看,有兴趣的可以研究下。
     
    下面有个网站介绍各种编码的,还有利用openssl进行各种转换的
  • 相关阅读:
    位运算简介以及常用技巧
    JMeter 十六:加密处理
    JMeter 十五:函数以及变量
    JMeter 十四:最佳实践
    JMeter 十三:生成 report dashboard
    JMeter 十二:命令行执行
    JMeter 十一:参数化
    JMeter 十:录制脚本--使用bodboy
    LoadRunner录制:检查点
    LoadRunner录制: 选择协议
  • 原文地址:https://www.cnblogs.com/adylee/p/3611560.html
Copyright © 2011-2022 走看看