Skip to content

BouncyCastle doesn't validate the version field when parsing traditional DSA PRIVATE KEY PEM files #2319

@Jennifer-first

Description

@Jennifer-first

BouncyCastle doesn't validate the version field when parsing traditional DSA PRIVATE KEY PEM files.
I apologize that I couldn't locate a specific standalone specification for this DSA key version. However, looking at RFC 5208 (PKCS#8), Section 5, it explicitly states: "version is the syntax version number, for compatibility with future revisions of this document. It shall be 0 for this version of the document."
Following this cryptographic design logic, the version field should strictly be 0. However, BouncyCastle currently accepts keys with version number 17 without throwing format or compliance errors.

To Reproduce:
Steps to reproduce the behavior:

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import java.io.File;
import java.io.FileReader;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
public class PrivateKeyParser {
    static {
        if (Security.getProvider("BC") == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }
    public static PrivateKey parse(File keyFile) throws Exception {
        try (FileReader reader = new FileReader(keyFile);
             PEMParser pemParser = new PEMParser(reader)) {
            Object object = pemParser.readObject();
            if (object != null) {
                JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
                if (object instanceof PEMKeyPair) {
                    return converter.getKeyPair((PEMKeyPair) object).getPrivate();
                } else if (object instanceof PrivateKeyInfo) {
                    return converter.getPrivateKey((PrivateKeyInfo) object);
                }
            }
        }
        byte[] keyBytes = Files.readAllBytes(keyFile.toPath());
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        
        String[] algorithms = {"SM2", "EC", "RSA"};
        for (String alg : algorithms) {
            try {
                KeyFactory kf = KeyFactory.getInstance(alg, "BC");
                return kf.generatePrivate(spec);
            } catch (Exception ignored) {
            }
        }

        throw new IllegalArgumentException(String.valueOf(keyFile));
    }
    public static void main(String[] args) {
        if (args.length < 1) {
            return;
        }
        try {
            File file = new File(args[0]);
            PrivateKey privateKey = parse(file);
            System.out.println(privateKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

javac -cp "bcprov-jdk18on-1.78.jar:bcpkix-jdk18on-1.78.jar" PrivateKeyParser.java
java -cp ".:bcprov-jdk18on-1.78.jar:bcpkix-jdk18on-1.78.jar" PrivateKeyParser key.pem
Expected behavior:
It should be rejected.

key.zip

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions