PKCS8 with PBES2 protection supported by EncryptedPrivateKeyInfo?

Anthony Scarpino anthony.scarpino at oracle.com
Tue Aug 9 01:26:46 UTC 2022


Oops.. I think I suggested something that only works in my development 
repo with PBES2.

I ran you code and it worked on jdk-dev.  Other than setting -v2 on the 
'openssl pkcs8' cmdline,

en javax.crypto.EncryptedPrivateKeyInfo at 46d56d67 
alg=PBEWithHmacSHA1AndAES_256 p=PBEWithHmacSHA1AndAES_256
k=SunRsaSign RSA private CRT key, 2048 bits
   params: null
   modulus: ...

Tony

On 8/8/22 5:12 PM, Anthony Scarpino wrote:
> Hi,
> 
> I did this not long ago. You are close, but you do not need to do the 
> Cipher.init() separately, it is done as part of 
> EncryptedPrivateKeyInfo.getKeySpec().  It also reads the encoding for 
> the count and iv.
> 
> The below will decrypt the DER-encoded 'data' into a 
> PKCS8EncryptedKeySpec for you to use.
> 
> EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(data);
> PBEKeySpec pks = new PBEKeySpec(password);
> SecretKeyFactory skf = SecretKeyFactory.getInstance(epki.getAlgName());
> SecretKey sk = skf.generateSecret(pks);
> PKCS8EncodedKeySpec keySpec = epki.getKeySpec(sk);
> 
> Tony
> 
> On 8/7/22 12:18 PM, Bernd wrote:
>> Hello,
>>
>> there is a longstanding issue in the PostgreSQL JDBC driver which 
>> reads secret keys in PKCS#8 format, but does not support the newer 
>> PKCS#5 2.0 (PBES2) modes (-v1 works). The (naive) code is here:
>>
>> pgjdbc/LazyKeyManager.java at 80d4ed34c99d51dd8b06df00baad0265fd620fec 
>> · pgjdbc/pgjdbc · GitHub 
>> <https://github.com/pgjdbc/pgjdbc/blob/80d4ed34c99d51dd8b06df00baad0265fd620fec/pgjdbc/src/main/java/org/postgresql/ssl/LazyKeyManager.java#L220>
>>
>> I was playing around with EncryptedPrivateKeyInfo in order to see 
>> whats needed to get it working with PBES2 encryption, but it did not 
>> work with quite a few tries.
>>
>> I wonder is the Code behind PBES2Parameters in JCE supposed to work 
>> and interoperable with openssl PKCS#8? If I understand the api 
>> correctly the following code should work, but it results in a padding 
>> error:
>>
>>      public static void main(String[] args) throws Throwable
>>      {
>>          byte[] b = readFileFully("test-key.p8"); // DER Format
>>          EncryptedPrivateKeyInfo ePKInfo = new 
>> EncryptedPrivateKeyInfo(b);
>>          System.out.println("en " + ePKInfo + " alg=" + 
>> ePKInfo.getAlgName()
>>                                         + " p=" + 
>> ePKInfo.getAlgParameters().toString());
>>          AlgorithmParameters algParams = ePKInfo.getAlgParameters();
>>          //PBEParameterSpec pbep = 
>> algParams.getParameterSpec(PBEParameterSpec.class);
>>          //System.out.println("pbep " + pbep);
>>          //AlgorithmParameterSpec cp = pbep.getParameterSpec();
>>          //System.out.println("cp = " + cp ); // IvParameters
>>
>>          PBEKeySpec pbeKeySpec = new PBEKeySpec("test".toCharArray());
>>          //PBEKeySpec pbeKeySpec = new PBEKeySpec("test".toCharArray(),
>>          //   pbep.getSalt(), pbep.getIterationCount(), 256);
>>          SecretKeyFactory skFac = 
>> SecretKeyFactory.getInstance(algParams.toString());
>>          Key pbeKey = skFac.generateSecret(pbeKeySpec);
>>
>>          Cipher cipher = Cipher.getInstance(algParams.toString());
>>          cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
>>
>>          // Decrypt the encrypted private key
>>          // when passing pbeKey here instead of cipher the algParams 
>> are lost
>>          KeySpec pkcs8KeySpec = ePKInfo.getKeySpec(cipher); // L61 --> 
>> throws
>>
>>          // not reached
>>          KeyFactory kf = KeyFactory.getInstance("RSA");
>>          PrivateKey key = kf.generatePrivate(pkcs8KeySpec);
>>          System.out.println("k=" + key);
>> }
>>
>> The test object created with openssl:
>>  > openssl req -x509 -newkey rsa:2048 -keyout test-key.pem -out 
>> test-cert.pem -sha256 -days 365 -nodes
>>  > openssl pkcs8 -topk8 -in test-key.pem -out test-key.p8 -outform der 
>> # password "test"
>>  > openssl asn1parse -in test-key.p8 -inform DER
>>      0:d=0  hl=4 l=1325 cons: SEQUENCE
>>      4:d=1  hl=2 l=  87 cons: SEQUENCE
>>      6:d=2  hl=2 l=   9 prim: OBJECT            :PBES2
>>     17:d=2  hl=2 l=  74 cons: SEQUENCE
>>     19:d=3  hl=2 l=  41 cons: SEQUENCE
>>     21:d=4  hl=2 l=   9 prim: OBJECT            :PBKDF2
>>     32:d=4  hl=2 l=  28 cons: SEQUENCE
>>     34:d=5  hl=2 l=   8 prim: OCTET STRING      [HEX 
>> DUMP]:BDCF0B11DB8BAFAB
>>     44:d=5  hl=2 l=   2 prim: INTEGER           :0800
>>     48:d=5  hl=2 l=  12 cons: SEQUENCE
>>     50:d=6  hl=2 l=   8 prim: OBJECT            :hmacWithSHA256
>>     60:d=6  hl=2 l=   0 prim: NULL
>>     62:d=3  hl=2 l=  29 cons: SEQUENCE
>>     64:d=4  hl=2 l=   9 prim: OBJECT            :aes-256-cbc
>>     75:d=4  hl=2 l=  16 prim: OCTET STRING      [HEX 
>> DUMP]:E355E1C72C9F486DDBE2E1F58EE7B61E
>>     93:d=1  hl=4 l=1232 prim: OCTET STRING      [HEX 
>> DUMP]:E4D9C29E4789840093EE371AB6EACB3111920E636CCB924651125D97CBD5312156214AD173CF4159AEEDBB497900815FE3B24184846CA252A421784CAD121B59C93EE3427BEA7CF0E404FAD0F226A2D13B86BD4898455B0750FBA6C4FB5F0B66980BD586CFFCBB5FA2170A67917FD55F7AF0E03D03078E5191965F74C099E357F28B6DA969E3CF3D713ABBC35C20A513E8068822A1C9B67BAFB650FB4B8EB7755A9F1760CD0B82AEBFBCCA01F575C377CD5AA2732D42F10A43EEF46048650E492F9FE1FD56596DDAC70461BF3E60CEA97F7EA99741254D1C1452CF1081DD799EACC74C8C0E806104230AE91E560C8F458B7BB358F031726355E99D31938CF39EC40B76D963FC3D45A59C7BE14CC769E7E4DA8C9FC08F4BD1A4C4CC07141BA0D5A31F0319E32D48A12A2BA4ABA4979A68447AFA57B8A9F82D465B1E765169B1C339C88F9EC9934B0B1B58C5793991FB9990F44C9C7A816ABE97015ECFB408CBF906BABAF9C7F5D0F32673AA1D9D72A0C3738FC9C1909FB24A029A3928C583B2DE4CCF3247F7C89D97E8DEEF3E08796789D43B56A66D1C07B3D368948964FAA1746EEC59605A14934B02BFE14B6BC8A281973E76215E7FF9A43FD33780F34D76A21791586EDBA5999BCE08D8DF5E20B736DDA9B91091991B0EE883CC3E8358D8403486B53D205C0DD33EE3224D1BE40DE9FCD444D70BDE6500B08FF843153EC8EF01F5CDC9D01CB9372BBCA6F42D5D13159AC9E67CAB075A20D86F9538AE604E87DEBF1300D3E6A8BEB72F1F2124F903E95A558DE3BEB61A1AAE792E9A77A6F860BD736D2C0BA97FDA25D4E3AEAF8149415E68E473F48D99860DC2A49F108AE5468DFC36A5764E3B06412D2C3498D87E9F5D487421FD5C15C0EA8306A69BFA2956D8F8C1F4CD4786EDE575515202F8442CBA2E3AA21CB267DDEC5BA7BB1CBC54F471E84CD522FCE9A69F084DD4EB100F00875A0A32BCCD6D5F5473FF8CA6E6B448BAB76A659E0350AAFC3A2EFBCCAF1AFB52F3C8A7F3B452E5B35CDC2CD05710D86529EE3544DE9AB5A7FAA9AD8ADC93B0F7F5432FF8EB280A3BEE3C094796C9938DC5A4956D47CE4CE7A7D2300E328D47F030332EC84296CD57F02B61B63A099210C78F64B3DB618E79A17585030CB82B8B39BB34333D148F5856D04B4083BD0F006B8A747D7E020DDD8DCA99AEF6B719367DB619C7736D7897F071875CC9C17F7AF3E742F06CDBEBECDF486CC6B143BB96FC4FE34A54CB1CBFDF877598094B459DBAA6B101B0DE8000A5E2D06ABAE1C651EDF3EA3A7D37EA263AC999B65F621E1F65FFD06F461B7285EB90AE2C32AA60C8424966BF37F2C4F502BFE253E252C1CD652FC8667E488BF8B282D0379B38AC9B53014C87C55E8DE8EB7A217F521F46FC13F983C3E77B61417A552D7ACC147FC40C295DD87DDC4972AF340384B449A8A888BD1F4AB7B5B2D6AB3E963F7E90870639709075FA4A618A640753B6E2B6B4C0734BFEEE7A14C94C64131C666749E813A07FB8622278BF7F823675883F8B57C9BC081420F96C7F71E006D3A41299711E6E283F7C97E1ECC1202D98DF0BB13742D8DB5FDFE4569C35E207983645AE86AA2286680A9EDB69742DBD164D499096C90A063EE6304C81D2DB8311A73F80D7E1BB14B2C0C249D14F7D953369513731A5DEDB2AAD671E74F704CAFBCF4CDEDB72E42A238DD9CA2AB77ABAE80350F508062276A66C9BF81C325368E496C9490B
>>
>>  From debugging it looks like it picked up the salt for PBKFD#2 and 
>> the Iv for AES, but it is a bit nested, so I tried passing Cipher 
>> and/or Key to getKeySpec().
>>
>> The output
>>
>> en javax.crypto.EncryptedPrivateKeyInfo at 27716f4 
>> alg=1.2.840.113549.1.5.13 p=PBEWithHmacSHA1AndAES_256
>> Exception in thread "main" java.security.spec.InvalidKeySpecException: 
>> Cannot retrieve the PKCS8EncodedKeySpec
>> at 
>> javax.crypto.EncryptedPrivateKeyInfo.getKeySpec(EncryptedPrivateKeyInfo.java:255)
>> at bernd.TestKey.main(TestKey.java:61)
>> Caused by: javax.crypto.BadPaddingException: Given final block not 
>> properly padded. Such issues can arise if a bad key is used during 
>> decryption.
>> at com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975)
>> at 
>> com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056)
>> at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
>> at com.sun.crypto.provider.PBES2Core.engineDoFinal(PBES2Core.java:323)
>> at javax.crypto.Cipher.doFinal(Cipher.java:2168)
>> at 
>> javax.crypto.EncryptedPrivateKeyInfo.getKeySpec(EncryptedPrivateKeyInfo.java:250)
>> ... 1 more
>>
>> I see there are some test cases for a similar usage, but they depend 
>> on an initialized key with defaults, so I am not sure if that would 
>> actually use AES IV?
>>
>> https://github.com/openjdk/jdk/blob/357f990e3244feaba6d8709b7ea50660220a418b/test/jdk/sun/security/x509/AlgorithmId/PBES2.java#L89 <https://github.com/openjdk/jdk/blob/357f990e3244feaba6d8709b7ea50660220a418b/test/jdk/sun/security/x509/AlgorithmId/PBES2.java#L89>
>>
>> BTW: its hard to say if the problem is "only" caused by UTF-8 vs. 
>> UTF-16 password encoding. In that case it would be a bit unfortunate, 
>> is it maybe an option to add that support? (however I am not sure what 
>> password encoding openssl uses in this case).
>>


More information about the security-dev mailing list