Is it possible to create a new root CA (just for myself) and use it to sign a set of existing intermediate CAs which I'd like to trust? If so, which tool can be used to accomplish this? The private keys of the intermediates are not in my possession.
2 Answers
I have reconsidered my initial response and I think this is technically possible, although unsure as to what it would be useful for.
Certificate signing requests (CSRs) don't have any secret information so conceptually you could create CSRs using the information in your intermediate certs including their public keys. Using that you could create clones signed by your root CA. You would end up with new intermediate certificates signed (only) by your root CA. You still can't use them to sign certificates because you don't have the private key paired with that public key.
OLD ANSWER:
By design, what you ask is not possible. Existing signed certificates cannot be resigned, and if they could you would absolutely need the private keys (to be able to sign certificates with them). Were this possible, the public key infrastructure would not work.
What you could do is create your own CA and sign intermediate certificates with the same details as those that you have, however they would never be able to match the existing ones and absolutely would not verify signatures of certificates signed by the existing intermediate ones.
-
Thank you, just to clarify: A setup as described in security.stackexchange.com/questions/14043/… is therefore not possible if I don't have the private keys of the intermediate CA?– JonasApr 29, 2020 at 15:49
-
1@Jonas - Signing a certificate requires the private key of the signer. What happens in the linked answer is that the owner of the certificate has made two separate signing requests to two different intermediate CAs, each of which return a separate signed certificate. The intermediate CAs are unlikely to know that the other CA is also signing the certificate. Apr 29, 2020 at 17:33
-
The new cert is different, but if it contains the same Subject, key, and (if used) SKI, it certainly can verify the existing children. Real CAs do this all the time to 'bridge' to new generation(s) and/or for ownership changes. Apr 30, 2020 at 6:17
-
1The more I read and think about this, the more I reckon that it may be possible. CSRs don't have secret information, so conceptually you could retrieve information from certificates and produce an equivalent CSR which you could use to sign with your root CA. Then you'd be able to have copies of those intermediate CAs signed by you. But then what? You still don't have the private key pair to the certificates....– PedroApr 30, 2020 at 8:06
-
2@Pedro I think it might be possible, too, which is why I really hope the OP rephrases the question as I suggested. The answer is either "no, to maintain practice you need the CSR" or, it is "yes, but you need to do something non-standard besides an CSR" and then the pseudo-CSR is borne from other info or the result is insecure. There's a really good question to be asked here.– schroeder ♦Apr 30, 2020 at 8:37
This is a thrown-together ad-hoc 'tool' and can definitely be improved, but:
$ for f in chain.[01] dstx3; do echo $f:; openssl x509 <$f -noout -subject -dates -issuer -fingerprint; done
chain.0:
subject= /CN=*.stackexchange.com
notBefore=Mar 3 14:52:01 2020 GMT
notAfter=Jun 1 14:52:01 2020 GMT
issuer= /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
SHA1 Fingerprint=2F:15:D5:FD:D9:0D:5A:FF:A7:C2:1D:C6:EA:E1:73:2C:D1:6D:E0:A2
chain.1:
subject= /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
notBefore=Mar 17 16:40:46 2016 GMT
notAfter=Mar 17 16:40:46 2021 GMT
issuer= /O=Digital Signature Trust Co./CN=DST Root CA X3
SHA1 Fingerprint=E6:A3:B4:5B:06:2D:50:9B:33:82:28:2D:19:6E:FE:97:D5:95:6C:CB
dstx3:
subject= /O=Digital Signature Trust Co./CN=DST Root CA X3
notBefore=Sep 30 21:12:19 2000 GMT
notAfter=Sep 30 14:01:15 2021 GMT
issuer= /O=Digital Signature Trust Co./CN=DST Root CA X3
SHA1 Fingerprint=DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13
$ openssl verify -show_chain -untrusted chain.1 -CAfile dstx3 chain.0
chain.0: OK
Chain:
depth=0: CN = *.stackexchange.com (untrusted)
depth=1: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 (untrusted)
depth=2: O = Digital Signature Trust Co., CN = DST Root CA X3
$
$ openssl req -newkey rsa:2048 -keyout myca.key -nodes -x509 -subj "/C=AQ/O=Dave's Bait and Certs" -out myca.crt
Generating a RSA private key
..............+++++
................................................................................................+++++
writing new private key to 'myca.key'
-----
$ cat se230737ResignImedCert.java
//nopackage
import java.io.*;
import java.math.BigInteger;
import java.security.PrivateKey;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.io.pem.*;
public class se230737ResignImedCert {
@SuppressWarnings("resource")
public static void main (String[] args) throws Exception {
// cacertfile cakeyfile imedcertfile outputfile [serial?timestamp]
X509CertificateHolder cacert = (X509CertificateHolder) new PEMParser(new FileReader(args[0])).readObject();
// unencrypted privatekey; better use encrypted with JcaPEMDecryptorBuilder etc.
PrivateKeyInfo cakeyi = (PrivateKeyInfo) new PEMParser(new FileReader(args[1])).readObject();
PrivateKey cakey = new JcaPEMKeyConverter ().getPrivateKey(cakeyi);
X509CertificateHolder imcert = (X509CertificateHolder) new PEMParser(new FileReader(args[2])).readObject();
BigInteger serial = args.length>4? new BigInteger(args[4]): BigInteger.valueOf(System.currentTimeMillis()/1000);
X509v3CertificateBuilder build = new X509v3CertificateBuilder (cacert.getSubject(), serial,
imcert.getNotBefore(), imcert.getNotAfter(), imcert.getSubject(), imcert.getSubjectPublicKeyInfo());
Extensions exts = imcert.getExtensions();
for( ASN1ObjectIdentifier oid : exts.getExtensionOIDs() ){
if( oid.equals(Extension.authorityKeyIdentifier)
|| oid.equals(Extension.authorityInfoAccess)
|| oid.equals(Extension.cRLDistributionPoints)
|| oid.equals(Extension.certificatePolicies) )
; // discard; might better replace, or even understand policies
else build.addExtension(exts.getExtension(oid));
}
X509CertificateHolder newcert = build.build(new JcaContentSignerBuilder("SHA256withRSA").build(cakey));
try( PemWriter w = new PemWriter(new FileWriter(args[3])) ){
w.writeObject(new PemObject ("CERTIFICATE", newcert.getEncoded()));
}
}
}
$ javac -cp bcprov160.jar:bcpkix160.jar se230737ResignImedCert.java
$ java -cp .:bcprov160.jar:bcpkix160.jar se230737ResignImedCert myca.crt myca.key chain.1 imnew
$ openssl x509 <imnew -noout -subject -dates -issuer -fingerprint
subject=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
notBefore=Mar 17 16:40:46 2016 GMT
notAfter=Mar 17 16:40:46 2021 GMT
issuer=C = AQ, O = Dave's Bait and Certs
SHA1 Fingerprint=EC:FF:43:F8:8A:90:78:57:3B:5A:B5:69:9E:4B:A8:8F:21:F9:5F:74
$ openssl verify -show_chain -untrusted imnew -CAfile myca.crt chain.0
chain.0: OK
Chain:
depth=0: CN = *.stackexchange.com (untrusted)
depth=1: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 (untrusted)
depth=2: C = AQ, O = Dave's Bait and Certs
However, whether your client(s) will use this instead of the 'real' intermediate which should be sent by e.g. the TLS server is a different question that depends on a lot of details missing from your question.
L - I1 - I0 - Root
, can one create a chain withL - I1 - newI0 - myRoot
, i.e.L
andI1
being the same,newI0
being likeI0
(same subject and pubkey) but with issuermyRoot
.