Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support interoperability with "asn1-schema/x509" #372

Open
gnarea opened this issue Jan 13, 2023 · 6 comments
Open

Support interoperability with "asn1-schema/x509" #372

gnarea opened this issue Jan 13, 2023 · 6 comments

Comments

@gnarea
Copy link
Contributor

gnarea commented Jan 13, 2023

I have an ASN.1 SEQUENCE that contains an X.509 certificate, and I'm using a custom asn1-schema schema to validate and (de)serialise the outer SEQUENCE. The certificate is eventually used to produce/verify SignedData values, so it'd be great if I could initialise a PKI.js Certificate with an asn1-schema/x509 Certificate.

Example:

import { Certificate as Asn1Certificate } from '@peculiar/asn1-x509';
import { AsnParser } from '@peculiar/asn1-schema';
import { Certificate as PkijsCertificate } from 'pkijs';

const CERT = `MIIEczCCAyegAwIBAgIIf98Hu6tQXdMwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAgEFAKEcMBoG
CSqGSIb3DQEBCDANBglghkgBZQMEAgEFAKIDAgEgMIGOMYGLMIGIBgNVBAMegYAAOABiADIANAA0ADEA
ZgBjADEANwA2AGQANQA3ADUANQAxADIAOABmADEAZAA5ADgAYQA5ADcAZgAxAGEANAAyAGYAMQBhADQA
NwBjADgANgBhAGQAZAAwADEAZgBhADUAOAAxADcAZQA3ADgAZgA4ADkAMQA2AGYAOQBiADYAOTAeFw0y
MzAxMTMxMDQyMzNaFw0yMzAxMTQxMDQyMzNaMIGOMYGLMIGIBgNVBAMegYAAOABiADIANAA0ADEAZgBj
ADEANwA2AGQANQA3ADUANQAxADIAOABmADEAZAA5ADgAYQA5ADcAZgAxAGEANAAyAGYAMQBhADQANwBj
ADgANgBhAGQAZAAwADEAZgBhADUAOAAxADcAZQA3ADgAZgA4ADkAMQA2AGYAOQBiADYAOTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8cE5qBNshuhv429ngLM1YfSOj+n5MentUr0/4cRLWPh0Qj
98VI3oZs7cvOfSkCeQgsIOhOmNZnC0wqAwo7dv1L2lJqi3975SplDe6VrWJSr6cJYLQxwkNFT6nYwPQ/
OvbqaZUGg4K5Qv5PEPDKO+wh78YfaeDDjMEbLoz0s0sNK/8OvESxH+fbbjwP2/r+Mhiy/PwjW9+Y5//P
Qb5LtKFLdpRqmU06cs3hDZ0Z9T1Mkohr2grp4OEPshgpixpnSSxQS3Sty3P+I08TclVDGsbr54uwXMV6
yHauyZfgw7LFJU6tpumYvwPs4v/w+0B543SQK15MC2H2xAcre75W8ncCAwEAAaNrMGkwDwYDVR0TAQH/
BAUwAwIBADArBgNVHSMEJDAigCCLJEH8F21XVRKPHZipfxpC8aR8hq3QH6WBfnj4kW+baTApBgNVHQ4E
IgQgiyRB/BdtV1USjx2YqX8aQvGkfIat0B+lgX54+JFvm2kwQQYJKoZIhvcNAQEKMDSgDzANBglghkgB
ZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEFAKIDAgEgA4IBAQAgIgvgz3NQw0HW0tT/
imkaQEXAPdOVMJxjojrXmDSUMlBXlHuqcrOHcQxkAsun/QGK6G7MBdLIa64D5+adEtcbudei3EZ47uYM
BjaaY4haMJzvH6MH0k+FEjDsx85kl726v6mlpHEl1l2jLo8zXaHFmldXjFD4fKv7yIf+YR2UHBtiGu16
Ebnmvx5lePwSKau841LLrxPDy1jJbBKBO+/6uDgL49Gx2X6AxlCkYTakRitsTPBjGP4lVLWaX4VC1C/G
rWTAht1wd2thbxnYsdXFArpKLyiBf02RPibEqKFmcnjXOpt1CeJldX0J7EX5sF/uz2l/NQTh11LUkcRB
l9Lu`;

test('it', () => {
  const serialisation = Buffer.from(CERT, 'base64');
  const asn1 = AsnParser.parse(serialisation, Asn1Certificate);
  const pkijs = new PkijsCertificate({ schema: asn1 });
  expect(Buffer.from(pkijs.toSchema().toBER())).toStrictEqual(serialisation);
});

As a workaround, I'm re-serialising the certificate with asn1-schema/x509 and re-deserialising it with PKI.js.

@microshine
Copy link
Contributor

@gnarea @peculiar/x509 is based on this schema. PKIjs includes PKI logic and ASN.1 schemas.
The simplest way to parse the Certificate using PKIjs is pkijs.Certificate.fromBER(raw).

@gnarea
Copy link
Contributor Author

gnarea commented Jan 17, 2023

Thanks for looking into this @microshine!

Do you mean that pkijs.Certificate.fromBER(raw) can take an asn1-schema/x509 Certificate too, not just a buffer? I tried that but maybe I'm doing something wrong because I got AsnError: Error during parsing of ASN.1 data. Data is not correct for 'Certificate':

import { Certificate as Asn1Certificate } from '@peculiar/asn1-x509';
import { AsnParser } from '@peculiar/asn1-schema';
import { Certificate as PkijsCertificate } from 'pkijs';

const CERT = `MIIEczCCAyegAwIBAgIIf98Hu6tQXdMwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAgEFAKEcMBoG
CSqGSIb3DQEBCDANBglghkgBZQMEAgEFAKIDAgEgMIGOMYGLMIGIBgNVBAMegYAAOABiADIANAA0ADEA
ZgBjADEANwA2AGQANQA3ADUANQAxADIAOABmADEAZAA5ADgAYQA5ADcAZgAxAGEANAAyAGYAMQBhADQA
NwBjADgANgBhAGQAZAAwADEAZgBhADUAOAAxADcAZQA3ADgAZgA4ADkAMQA2AGYAOQBiADYAOTAeFw0y
MzAxMTMxMDQyMzNaFw0yMzAxMTQxMDQyMzNaMIGOMYGLMIGIBgNVBAMegYAAOABiADIANAA0ADEAZgBj
ADEANwA2AGQANQA3ADUANQAxADIAOABmADEAZAA5ADgAYQA5ADcAZgAxAGEANAAyAGYAMQBhADQANwBj
ADgANgBhAGQAZAAwADEAZgBhADUAOAAxADcAZQA3ADgAZgA4ADkAMQA2AGYAOQBiADYAOTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8cE5qBNshuhv429ngLM1YfSOj+n5MentUr0/4cRLWPh0Qj
98VI3oZs7cvOfSkCeQgsIOhOmNZnC0wqAwo7dv1L2lJqi3975SplDe6VrWJSr6cJYLQxwkNFT6nYwPQ/
OvbqaZUGg4K5Qv5PEPDKO+wh78YfaeDDjMEbLoz0s0sNK/8OvESxH+fbbjwP2/r+Mhiy/PwjW9+Y5//P
Qb5LtKFLdpRqmU06cs3hDZ0Z9T1Mkohr2grp4OEPshgpixpnSSxQS3Sty3P+I08TclVDGsbr54uwXMV6
yHauyZfgw7LFJU6tpumYvwPs4v/w+0B543SQK15MC2H2xAcre75W8ncCAwEAAaNrMGkwDwYDVR0TAQH/
BAUwAwIBADArBgNVHSMEJDAigCCLJEH8F21XVRKPHZipfxpC8aR8hq3QH6WBfnj4kW+baTApBgNVHQ4E
IgQgiyRB/BdtV1USjx2YqX8aQvGkfIat0B+lgX54+JFvm2kwQQYJKoZIhvcNAQEKMDSgDzANBglghkgB
ZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEFAKIDAgEgA4IBAQAgIgvgz3NQw0HW0tT/
imkaQEXAPdOVMJxjojrXmDSUMlBXlHuqcrOHcQxkAsun/QGK6G7MBdLIa64D5+adEtcbudei3EZ47uYM
BjaaY4haMJzvH6MH0k+FEjDsx85kl726v6mlpHEl1l2jLo8zXaHFmldXjFD4fKv7yIf+YR2UHBtiGu16
Ebnmvx5lePwSKau841LLrxPDy1jJbBKBO+/6uDgL49Gx2X6AxlCkYTakRitsTPBjGP4lVLWaX4VC1C/G
rWTAht1wd2thbxnYsdXFArpKLyiBf02RPibEqKFmcnjXOpt1CeJldX0J7EX5sF/uz2l/NQTh11LUkcRB
l9Lu`;

test('it', () => {
  const serialisation = Buffer.from(CERT, 'base64');
  const asn1 = AsnParser.parse(serialisation, Asn1Certificate);
  const pkijs = PkijsCertificate.fromBER(asn1 as any);
  expect(Buffer.from(pkijs.toSchema().toBER())).toStrictEqual(serialisation);
});

Note that the reason why I want to init a PKI.js Certificate with an asn1-schema/x509 Certificate, instead of a Buffer/ArrayBuffer, is that the X.509 is contained in an ASN.1 SEQUENCE that's already been parsed with asn1-schema:

import { AsnProp } from '@peculiar/asn1-schema';
import { Certificate } from '@peculiar/asn1-x509';

import { DnssecChain } from '../dns/DnssecChain.js';

export class MemberIdBundleSchema {
  @AsnProp({ type: DnssecChain })
  public dnssecChain!: DnssecChain;

  @AsnProp({ type: Certificate })
  public organisationCertificate!: Certificate;

  @AsnProp({ type: Certificate })
  public memberCertificate!: Certificate;
}

So if I already have an instance of MemberIdBundleSchema, which was created from a well-formed DER buffer, I'd prefer to have the option to pass the MemberIdBundleSchema.organisationCertificate value (for example), to PKI.js' Certificate.

This isn't the end of the world because I can always re-serialise the asn1-schema/x509 Certificate to an ArrayBuffer and have PKI.js' Certificate parse it, but it'd be easier and better for performance if we could skip the the re-serialisation.

@microshine
Copy link
Contributor

Note that the reason why I want to init a PKI.js Certificate with an asn1-schema/x509 Certificate, instead of a Buffer/ArrayBuffer, is that the X.509 is contained in an ASN.1 SEQUENCE that's already been parsed with asn1-schema

I see what you are trying to do. You don't want to parse the Certificate raw twice. But it's impossible. PKIjs use its own ASN.1 schemas. Supporting of @peculiar/asn1-schema requires a major update of PKIjs with architecture changing. As I told before, each PKIjs class implements 2 logics - ASN.1 schema and PKI functions. If we use @peculiar/asn1-schema in PKIjs then PKIjs ASN.1 implementation becomes odd.

What do you use in PKIjs we don't have in @peculiar/x509?

@gnarea
Copy link
Contributor Author

gnarea commented Jan 17, 2023

If we use @peculiar/asn1-schema in PKIjs then PKIjs ASN.1 implementation becomes odd.

Agreed 💯 . I was just checking if you'd be open to that, but this isn't blocking me so it's not the end of the world.

What do you use in PKIjs we don't have in @peculiar/x509?

I basically need to verify certification paths. The root/trusted certificate will be present in the ASN.1 SEQUENCE I mentioned above, and I want to pass that certificate to the trustedCerts param in SignedData.verify().

@microshine
Copy link
Contributor

@peculiar/x509 has a simple chain validation. But it's based on certificate signatures. It doesn't check revocations and policies. I'd like to support the chain validation with those features, but I need time to implement it.

So if you need just chain without those validations, you can use X509ChainBuilder

https://peculiarventures.github.io/x509/classes/X509ChainBuilder.html

const chain = new x509.X509ChainBuilder({
  certificates: [
    new x509.X509Certificate(raw1),
    new x509.X509Certificate(raw2),
    // ...
    new x509.X509Certificate(rawN),
  ],
});

const cert = x509.X509Certificate(raw);
const items = await chain.build(cert);

@gnarea
Copy link
Contributor Author

gnarea commented Jan 17, 2023

Thanks @microshine!

Though in my case, the signer's certificate and any intermediate certificates are embedded in the SignedData value, so I guess I'd have to re-serialise those certificates so I can build x509.X509Certificate instances.

In which case I think it'd be easier/faster to only re-serialise the root certificate and pass it to SignedData.verify() (trustedCerts param).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants