有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java RSA我应该使用X.509还是PKCS#1

用例: 我有一个用例,其中客户机生成私钥和公钥,将base64编码的公钥发送到服务器

在服务器端,我将使用此公钥加密消息,并将加密的消息发送到客户端,客户端使用其私钥对其进行解密。商定的算法是“RSA”

问题是在服务器端,我看到某些密钥正在使用X509EncodedKeySpec作为密钥规范工作

byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);

虽然有些键使用X509EncodedKeySpec抛出异常(Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence),但使用RSAPublicKeySpec工作:

byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey.getInstance(publicBytes);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);

因此,我的理解是,客户机和服务器需要商定是否使用: PKCS #1X.509用于对密钥进行编码我的问题是哪一个更适合我的用例何时使用哪种格式有什么指导原则吗


共 (1) 个答案

  1. # 1 楼答案

    差别很小密钥格式Java调用X.509,更确切地说是ASN。1结构SubjectPublicKeyInfo(或SPKI)在X.509中定义,或在RFC5280 sec 4.1中定义,是处理大型灵活算法集的一种非常简单的方法:它由子结构AlgorithmIdentifier组成,子结构识别算法及其参数(如果适用),然后是一个不透明的位字符串,它包含实际的密钥信息(已编码),其格式取决于算法标识符(所识别的算法)

    对于RSA,依赖于算法的部分ASN。1结构RSAPublicKey在PKCS1或更方便的RFC8017 appendix A.1.1及其早期版本中定义,并在RFC3279 sec 2.3.1中复制。因此,对于RSA,X.509(SPKI)格式包含PKCS1格式,并且由于RSA没有参数(或者至少没有与密钥相关的参数),唯一真正的区别是X.509格式明确指定密钥是RSA,而在您的应用程序中,您已经知道该密钥

    您已经发现,vanilla(Oracle是Sun现在的OpenJDK)Java crypto,也称为JCA Java加密体系结构,直接只支持X.509(SPKI)格式,这是一个次要的优势。但是,如果您使用BouncyCastle,则与Q中的代码相比,来回转换要容易得多;您只需使用org.bouncycastle.asn1.x509.SubjectPublicKeyInfo类添加或放弃算法标识符:

        // test data source
        KeyStore ks = KeyStore.getInstance("JKS"); ks.load (new FileInputStream (args[0]), args[1].toCharArray());
        byte[] spkienc = ks.getCertificate(args[2]).getPublicKey().getEncoded();
        System.out.println (DatatypeConverter.printHexBinary(spkienc));
    
        // extract PKCS1 part of original SPKI
        byte[] pkcs1enc = SubjectPublicKeyInfo.getInstance(spkienc).parsePublicKey().getEncoded();
        System.out.println (DatatypeConverter.printHexBinary(pkcs1enc));
    
        // rebuild SPKI from the PKCS1
        AlgorithmIdentifier algid = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
        byte[] spki2enc = new SubjectPublicKeyInfo (algid, pkcs1enc).getEncoded();
        System.out.println (DatatypeConverter.printHexBinary(spki2enc));
    

    请参阅我对类似golang x509.MarshalPKIXPublicKey vs x509.MarshalPKCS1PublicKey()的回答,特别是以下链接:
    Converting A public key in SubjectPublicKeyInfo format to RSAPublicKey format java
    Generating RSA keys in PKCS#1 format in Java
    Problem transmiting a RSA public key, javaME , bouncy castle

    如果你没有BouncyCastle,它会有点难;您需要编写一个部分ASN。1解析器或生成器。完全ASN。1处理相当复杂,但对于这种情况,您只需要一小部分,这并不太糟糕。(是的,那是微弱的赞扬。)如果我有更多的时间,我可以稍后再加上这个

    一个更大的潜在问题是您的密钥没有经过身份验证公钥分发的难点,比微小的格式细节要难得多,是确保分发合法密钥。如果攻击者可以用他们的公钥替换正确的公钥,那么受害者就会以攻击者可以轻松读取的方式对假定的机密数据进行加密,而你所有花哨的密码都一文不值

    这就是为什么大多数实际系统不分发裸公钥,而是分发允许验证密钥是否正确的证书。有一些证书方案,但最广泛的是X.509及其Internet配置文件PKIX。事实上,上面提到的RFC 5280和3279是PKIX的一部分。SSL现在TLS使用X.509。代码签名使用X.509。S/MIME电子邮件使用X.509。(PGP/GPG使用不同类型的证书,不是X.509,而是证书。)Java直接支持X.509证书,与“X.509”(SPKI)公钥一样好,甚至更好