defmodule CA.CRT do @moduledoc "X.509 Certificates." def baseLength(oid) when is_tuple(oid) do CA.Curve.getLength(CA.KnownCurves.getCurveByOid(oid)) end def baseLength(_) do 256 end # Rivest-Shamir-Adleman schema of Diffie–Hellman key exchange def decodeRSA(_, _, publicKey) do {:ok, key} = :"PKCS-1".decode(:RSAPublicKey, publicKey) [ key: key, scheme: :RSA ] end # Integers over Prime Galois Fields GF(p) def decodeECC(agreement, parameters, publicKey) do params = CA.CE.decodeObjectIdentifier(parameters) bin = :binary.part(publicKey, 1, :erlang.size(publicKey) - 1) baseLength = baseLength(params) xs = :binary.part(bin, 0, baseLength) ys = :binary.part(bin, baseLength, :erlang.size(bin) - baseLength) [ x: CA.ECDSA.numberFromString(xs), y: CA.ECDSA.numberFromString(ys), scheme: CA.AT.oid(agreement), curve: CA.AT.oid(params) ] end # Polynomials over Binary Galois Fields GF(2^m) def decodeGFE(agreement, params, publicKey) do {:ok, p} = :DSTU.decode(:DSTU4145Params, params) [ key: publicKey, scheme: CA.AT.oid(agreement), field: p ] end def decodePublicKey(agreement, {:asn1_OPENTYPE, params}, publicKey) do decodePublicKey(agreement, params, publicKey) end def decodePublicKey(agreement, params, publicKey) do case agreement do {1, 2, 840, 113_549, 1, 1, 1} -> decodeRSA(agreement, params, publicKey) {1, 2, 840, 10045, 2, 1} -> decodeECC(agreement, params, publicKey) {1, 2, 804, 2, 1, 1, 1, 1, 3, 1, 1} -> decodeGFE(agreement, params, publicKey) _ -> :logger.info(~c"new unknown publicKey agreement scheme detected: ~p~n", [agreement]) :base64.encode(publicKey) end end def parseCertPEM(file) do {:ok, bin} = :file.read_file(file) list = :public_key.pem_decode(bin) :lists.map(fn x -> parseCert(:public_key.pem_entry_decode(x)) end, list) end def parseCertB64(file) do {:ok, bin} = :file.read_file(file) parseCertBin97(:base64.decode(bin)) end def parseCertFile(file) do {:ok, bin} = :file.read_file(file) parseCertBin97(bin) end def parseCertBin97(bin) do {:ok, cert} = :AuthenticationFramework.decode(:Certificate, bin) parseCert(cert) end def parseCertBin88(bin) do {:ok, cert} = :PKIX1Explicit88.decode(:Certificate, bin) parseCert(cert) end def parseCert(cert, _) do parseCert(cert) end def parseCert({:certificate, cert}) do parseCert(cert) end def parseCert(cert) do {:Certificate, tbs, _, _} = case cert do {:Certificate, tbs, x, y} -> {:Certificate, tbs, x, y} {:Certificate, tbs, x, y, _} -> {:Certificate, tbs, x, y} end {_, ver, serial, {_, alg, _}, issuer, {_, {_, nb}, {_, na}}, issuee, {:SubjectPublicKeyInfo, {_, agreement, params}, publicKey}, _b, _c, exts} = tbs extensions = :lists.map( fn {:Extension, code, _x, b} -> CA.CE.oid(code, :lists.flatten(CA.CE.flat(code, :asn1rt_nif.decode_ber_tlv(b), []))) end, exts ) [ resourceType: :Certificate, version: ver, signatureAlgorithm: CA.AT.code(alg), subject: CA.RDN.rdn(CA.RDN.decodeAttrs(issuee)), issuer: CA.RDN.rdn(CA.RDN.decodeAttrs(issuer)), serial: :base64.encode(CA.CE.integer(serial)), validity: [from: nb, to: na], publicKey: decodePublicKey(agreement, params, publicKey), extensions: extensions ] end end