PKCS8 with PBES2 protection supported by EncryptedPrivateKeyInfo?

Anthony Scarpino anthony.scarpino at oracle.com
Tue Aug 9 00:12:04 UTC 2022


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