RSA should I use X.509 or PKCS #1

夙愿已清 提交于 2020-12-06 15:47:45

问题


Use case: I have a use case wherein client generates private and public key , sends the base 64 encoded public key to the server.

On server side I will encrypt a message using this public key and send the encrypted message to client , which the client decrypts using its private key.The algorithm agreed upon is 'RSA'.

The problem is on server side I am seeing that certain keys are working using X509EncodedKeySpec as key spec

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

While some keys throw exception (Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence) using X509EncodedKeySpec but work using 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);

So, what I came to understand is that client and server need to agree whether to use: PKCS #1 or X.509 for encoding the key . My question is which one is better for my use case? Any guidelines when to use which format?


回答1:


There's very little difference. The key format Java calls X.509, more exactly known as the ASN.1 structure SubjectPublicKeyInfo (or SPKI) defined in X.509 or equivalently and more conveniently in RFC5280 sec 4.1, is a quite simple way to handle a large and flexible set of algorithms: it consists of a substructure AlgorithmIdentifier which identifies the algorithm and its parameters if applicable, then an opaque BIT STRING which contains the actual key information (encoded) in a format depending on (the algorithm identified by) the AlgorithmIdentifier.

For RSA, the algorithm-dependent part is the ASN.1 structure RSAPublicKey defined in PKCS1 or more conveniently RFC8017 appendix A.1.1 and its earlier versions, and duplicated in RFC3279 sec 2.3.1. Thus for RSA the X.509 (SPKI) format contains the PKCS1 format, and since RSA doesn't have parameters (or at least key-related parameters), the only real difference is that the X.509 format explicitly specifies that the key is RSA -- which in your application you already know.

You have already discovered that vanilla (Oracle-was-Sun-now-OpenJDK) Java crypto, aka JCA Java Cryptographic Architecture, directly supports only the X.509 (SPKI) format, which is a minor advantage. However if you use BouncyCastle it is much easier to convert back and forth than the code in your Q; you simply use the org.bouncycastle.asn1.x509.SubjectPublicKeyInfo class to add or discard the AlgorithmIdentifier:

    // 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));

See my answer to the similar golang x509.MarshalPKIXPublicKey vs x509.MarshalPKCS1PublicKey() and especially the links to:
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

If you don't have BouncyCastle, it's a little harder; you need to write a partial ASN.1 parser or generator. Full ASN.1 processing is rather complicated, but for this case you need only a small subset that isn't too bad. (Yeah, that's faint praise.) I may add this later if I have more time.

A much bigger potential issue is that your key is not authenticated. The hard part of public key distribution, much harder than tiny format details, is making sure that only the legitimate key is distributed. If an attacker can substitute their publickey for the correct one, then the victim encrypts the supposedly secret data in a way the attacker can easily read, and all your fancy cryptography code is completely worthless.

This is why most actual systems don't distribute bare publickeys, but instead certificates that allow verifying the key is the correct key. There are a few certificate schemes, but the most widespread by far is X.509 and its Internet profile PKIX -- in fact the RFCs I referenced above, 5280 and 3279, are part of PKIX. SSL-now-TLS uses X.509. Code-signing uses X.509. S/MIME email uses X.509. (PGP/GPG uses a different kind of certificates, not X.509, but still certificates.) And (vanilla) Java directly supports X.509 certificates just as well or even better than it does "X.509" (SPKI) publickeys.



来源:https://stackoverflow.com/questions/53924326/rsa-should-i-use-x-509-or-pkcs-1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!