Not able to verify the validity of X509Certificate using Apples App attest root certificate - ssl

All I am able to do is validate the generated X509Certificate using its method checkValidity(), but as per the steps mentioned in https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server, we have to validate the X509Certificate using Apple App attest root certificate which is
-----BEGIN CERTIFICATE-----
MIICITCCAaegAwIBAgIQC/O+DvHN0uD7jG5yH2IXmDAKBggqhkjOPQQDAzBSMSYw
JAYDVQQDDB1BcHBsZSBBcHAgQXR0ZXN0YXRpb24gUm9vdCBDQTETMBEGA1UECgwK
QXBwbGUgSW5jLjETMBEGA1UECAwKQ2FsaWZvcm5pYTAeFw0yMDAzMTgxODMyNTNa
Fw00NTAzMTUwMDAwMDBaMFIxJjAkBgNVBAMMHUFwcGxlIEFwcCBBdHRlc3RhdGlv
biBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9y
bmlhMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAERTHhmLW07ATaFQIEVwTtT4dyctdh
NbJhFs/Ii2FdCgAHGbpphY3+d8qjuDngIN3WVhQUBHAoMeQ/cLiP1sOUtgjqK9au
Yen1mMEvRq9Sk3Jm5X8U62H+xTD3FE9TgS41o0IwQDAPBgNVHRMBAf8EBTADAQH/
MB0GA1UdDgQWBBSskRBTM72+aEH/pwyp5frq5eWKoTAOBgNVHQ8BAf8EBAMCAQYw
CgYIKoZIzj0EAwMDaAAwZQIwQgFGnByvsiVbpTKwSga0kP0e8EeDS4+sQmTvb7vn
53O5+FRXgeLhpJ06ysC5PrOyAjEAp5U4xDgEgllF7En3VcE3iexZZtKeYnpqtijV
oyFraWVIyd/dganmrduC1bmTBGwD
-----END CERTIFICATE-----
You can have a look at my code:
String decodedCredCert = "
-----BEGIN CERTIFICATE----MIIC4jCCAmmgAwIBAgIGAX66NktmMAoGCCqGSM49BAMCME8xIzAhBgNVBAMMGkFwcGxlIEFwcCBBdHRlc3RhdGlvbiBDQSAxMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTIyMDIwMTExMzM0N1oXDTIyMDIwNDExMzM0N1owgZExSTBHBgNVBAMMQGMwYTY1ZmRjYjE1MDJjNTNmNzUyMzM4NzZlM2ViZTE3NGVlMzYyODkwZDVmZGE3N2RmZTJhMmE2NWQwOTQ1MGYxGjAYBgNVBAsMEUFBQSBDZXJ0aWZpY2F0aW9uMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExooI0SVHKxhLJu+1zDz1VmCQU7SDdoYTBP5FPsl3sn2sYc1De9tzkKCbb/txsvZXDU6cn+HH5Lt3w4+s+wkkmqOB7TCB6jAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIE8DB6BgkqhkiG92NkCAUEbTBrpAMCAQq/iTADAgEBv4kxAwIBAL+JMgMCAQG/iTMDAgEBv4k0IgQgV0EyREI2MzdKVy5jb20uZ2wubWljaGFlbEtvcnMucWGlBgQEc2tzIL+JNgMCAQW/iTcDAgEAv4k5AwIBAL+JOgMCAQAwGQYJKoZIhvdjZAgHBAwwCr+KeAYEBDE1LjIwMwYJKoZIhvdjZAgCBCYwJKEiBCCDM1heaSkW2MvNzjxqor1xYX1U/tdYlXz9a1CiCT/VBjAKBggqhkjOPQQDAgNnADBkAjA4Ek+QKVFVunbBBBbjQ4qfni7vhXVABvbV/ru8CjsSc/fkuKsR4mKmmWjNoFq2vnACMAv5YFRC6rt1fv9vD1duDxv608DOGZtC95H7LOI65RHn0IYiq1iMLfBF
MPUPwJACRw==
-----END CERTIFICATE----- ";
X509Certificate cert1 = getParentCertificate(decodedCredCert);
System.out.println(cert1);
cert1.checkValidity();
where my

This app attest step is to verify the certificate chain. You will get 2 certificates in attestation request i.e. under x5c[0], x5c[1]. These are leaf and intermediate certificates.
To verify the certificate chain, x5c[0] certificate should be signed by x5c[1] and x5c[1] certificate should be signed by Apple App attest root certificate.
Sample code for this
CertificateFactory cf = CertificateFactory.getInstance(AppConstants.X_509);
byte[] credCertByte = Base64.getDecoder().decode(x5c[0]);
X509Certificate credCert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(credCertByte));
byte[] caCertByte = Base64.getDecoder().decode(x5c[1]);
X509Certificate caCert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(caCertByte));
X509Certificate appleAppAttestationRootCaCert = (X509Certificate) cf
.generateCertificate(APPLE_APP_ATTEST_CERT);
credCert.verify(caCert.getPublicKey());
caCert.verify(appleAppAttestationRootCaCert.getPublicKey());
This is the code for NodeJS/Typescript:
import fs from 'fs';
import crypto from 'crypto'
const appleAppAttestationRootCaCert = fs.readFileSync(__dirname + '/../assets/AppleRootCA-G3.pem').toString();
const credCert = new crypto.X509Certificate(Buffer.from(x5c[0], 'base64'))
const caCert = new crypto.X509Certificate(Buffer.from(x5c[1], 'base64'))
const appleCert = new crypto.X509Certificate(appleAppAttestationRootCaCert)
const valid = credCert.verify(caCert.publicKey)
console.log("Valid:", valid)
caCert.verify(appleCert.publicKey);
console.log("Valid with Apple Key:", valid)

Related

xades4j validation error: SigningCertificate property contains one or more certificates that are not part of the certification path

I get a SigningCertificateCertsNotInCertPathException when validating a XADES. The file is signed using a certificate what has a intermediate cert and a root cert:
The section xades:SigningCertificate in the XADES file contains the tree certs. If I build the XADES with only the personal cert in xades:SigningCertificate (using false in FileSystemKeyStoreKeyingDataProvider.returnFullChain), the validation run Ok, but not with the tree certs in xades:SigningCertificate...
Debugging the library, I see that certChainData.getCertificateChain() in method 'verify' of SigningCertificateVerifier.java only contains 2 certs: personal and intermediate certs, but no root cert. I think this could be the reason why SigningCertificateCertsNotInCertPathException arise, but really I don't know
How must I validate the XADES with the tree certs in the xades:SigningCertificate section? My code is this:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new FileReader(SIGNED)));
DOMHelper.useIdAsXmlId(doc.getDocumentElement());
NodeList nl = doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature");
FileSystemDirectoryCertStore certStore = new FileSystemDirectoryCertStore(CERT_FOLDER);
KeyStore ks;
try (FileInputStream fis = new FileInputStream(CERT_FOLDER + KEY_STORE)) {
ks = KeyStore.getInstance("jks");
ks.load(fis, PASS.toCharArray());
}
CertificateValidationProvider provider = new PKIXCertificateValidationProvider(
ks, false, certStore.getStore());
XadesVerificationProfile profile = new XadesVerificationProfile(provider);
Element sigElem = (Element) nl.item(0);
XAdESVerificationResult r = profile.newVerifier().verify(sigElem, null);
In CERT_FOLDER, I have the two .cer files (root and intermediate certs)
In the KEY_STORE file, I have the three certificates:
Tipo de Almacén de Claves: PKCS12
Proveedor de Almacén de Claves: SUN
Su almacén de claves contiene 3 entradas
personal, 09-dic-2021, PrivateKeyEntry,
Huella Digital de Certificado (SHA1): D8:25:0E:AA:...
intermedio, 09-dic-2021, trustedCertEntry,
Huella Digital de Certificado (SHA1): 80:8B:72:E4:...
raiz, 09-dic-2021, trustedCertEntry,
Huella Digital de Certificado (SHA1): EC:50:35:07:...
If the intermediate certificate is also on the trusted key store (ks) supplied to PKIXCertificateValidationProvider , then you're saying that it can be used as a trust root. During verification, it will be possible to build a certification path consisting of personal < intermedio, as the later is trusted, so no need to keep going.
If your trust root is raiz, then ks should only contain this certificate.

How can I use a key.pem file in Netsuite to sign a HTTP request with Suitescript?

I am trying to sign a https request and for that I need to encrypt a digest. From the api I generated both a certificate.pem and a privateKey.pem. I uploaded them both in Netsuite in the Certficate and Key part of the company set up.
My question is mainly how do I now get the privateKey from the file to use with the crypto module?
Here is what I have so far.
"payload" is the data I want to encrypt for my digest and is just a string.
var sKey = keyControl.loadKey('custkey2');
var hmacObj = crypto.createHmac({
algorithm: crypto.HashAlg.SHA256,
key: sKey
});
var updatedHmac = hmacObj.update({
input: payload,
inputEncoding:encode.Encoding.UTF_8
});
var reencoded = encode.convert({
string: updatedHmac,
inputEncoding: encode.Encoding.UTF_8,
outputEncoding: encode.Encoding.BASE_64
});
But when ever I run that in my Suitelet I get an error coming from the "create Hmac".
any help would be more than appreciated thank you.
SS2.0 module N/https/clientCertificate holds the answer. Instead of using https.post() use clientCertificate.post() which can send SSL requests with a digital certificate.
Example that works for me:
/* 1st create certificate in NetSuite UI (Setup > Pereferences > Certificates) */
const certId = 'custcertificate_xy';
/* 2nd use certificates id inside request call */
const response = clientCertificate.post({
url: url,
body: body,
certId: certId,
headers: headers
});
Please note that for some reason NetSuite wanted me to have certificate (*.pem) file in following format:
-----BEGIN PRIVATE KEY-----
{{private key}}
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
{{certificate}}
-----END CERTIFICATE-----

How to create a JWKs key from a CRT encoded in PEM format

I have a CRT file:
Example:
-----BEGIN CERTIFICATE-----
MIIDijCCAvOgAwIBAgIJAKRvtQxONVZoMA0GCSqGSIb3DQEBBAUAMIGLMQswCQYD
aXJlbGVzcyBOZXR3b3JrczEMMAoGA1UECxMDVEFDMSMwIQYDVQQDExpteXNlcnZl
ci5hcnViYW5ldHdvcmtzLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
zRwqc9prVXycGhHcsAjGPzC2MKU4DhXSr86Z89Jk8/cXEJBJ0C/NgdAqqDgxneUh
nVyxGxODa7BNGAWSagdCsKLrbkchr479E3xLfgdc3UzAJITLGCXGiQ66NwQDyM5I
YWxpZm9ybmlhMRIwEAYDVQQHEwlTdW5ueXZhbGUxIDAeBgNVBAoTF0FydWJhIFdp
cmVsZXNzIE5ldHdvcmtzMQwwCgYDVQQLEwNUQUMxIzAhBgNVBAMTGm15c2VydmVy
LmFydWJhbmV0d29ya3MuY29tggkApG+1DE41VmgwDAYDVR0TBAUwAwEB/zANBgkq
hkiG9w0BAQQFAAOBgQBp71WeF6dKvqUSO1JFsVhBeUesbEgx9+tx6eP328uL0oSC
fQ6EaiXZVbrQt+PMqG0F80+4wxVXug9EW5Ob9M/opaCGI+cgtpLCwSf6CjsmAcUc
b6EjG/l4HW2BztYJfx15pk51M49TYS7okDKWYRT10y65xcyQdfUKvfDC1k5P9Q==
-----END CERTIFICATE-----
I know that this CRT is encoded in PEM. But i want to create a JWK key from this file how should i go about doing this ?
You can use the below code to convert your PEM to JWK.
const jwk = require('jwk');
const myKeyJWK = jwk.readCertificate('crykey-my-key');

Adding extension in CSR for generating an intermediate certificate

I am generating a Certificate Signing Request for an intermediate certificate. I want to make the certificate a certificate authority (CA), so I want to add the basic constraints extension in CSR. I am currently using the following code
exts = sk_X509_EXTENSION_new_null();
add_ext(exts, x509_req, NID_basic_constraints, "critical,CA:TRUE");
X509_REQ_add_extensions(x509_req, exts);
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
The add extension function looks like this
int add_ext(STACK_OF(X509_EXTENSION) *sk, X509_REQ* req, int nid, char *value)
{
X509_EXTENSION *ex;
X509V3_CTX ctx;
X509V3_set_ctx_nodb(&ctx);
X509V3_set_ctx(&ctx, NULL, NULL, req, NULL, 0);
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
if (!ex)
{
log("X509V3_EXT_conf_nid generated error", cspsdk::Logging::LOG_LEVEL_INFO);
return 0;
}
sk_X509_EXTENSION_push(sk, ex);
return 1;
}
The problem is that after getting signed, the certificate has the CA value of basic constraints extension set to false. I am at a loss here. Can anybody point out the issue.
Your issuer can choose to override the constraints like CA: False even though you requested for CA: True. You need to contact them unless you are self-signing your certs.
openssl x509 -in your-signed-cert.pem -text -noout
Please check if the output contains "CA:True".

Go TLS connection

I connect to some server via openssl:
openssl s_client -crlf -connect somehost.com:700 -cert key.pem
And it works. Connection is successful.
But when I tried to do same from Go code (example from documentation), it doesn't work for me:
import (
"crypto/tls"
"crypto/x509"
)
func main() {
// Connecting with a custom root-certificate set.
const rootPEM = `
-----BEGIN CERTIFICATE-----
my key text
-----END CERTIFICATE-----`
// First, create the set of root certificates. For this example we only
// have one. It's also possible to omit this in order to use the
// default root set of the current operating system.
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
if !ok {
panic("failed to parse root certificate")
}
conn, err := tls.Dial("tcp", "somehost.com:700", &tls.Config{
RootCAs: roots,
})
if err != nil {
panic("failed to connect: " + err.Error())
}
conn.Close()
}
My text error is:
panic: failed to connect: x509: certificate is valid for otherhost.com, not somehost.com [recovered]
Question: what did I do wrong? And maybe I didn't add some tls.Config parameters?
openssl s_client is just a test tool which connects but it does not care much if the certificate is valid for the connection. Go instead cares if the certificate could be validated, so you get the information that the certificate is invalid because the name does not match.
what did I do wrong?
Based on the error message you did access the host by the wrong hostname. Or you've configured your server badly so that it sends the wrong certificate.
I didn't need to check ssl certificate of server. It was demo server of some domain registry. So I need server to check my certificate.
const certPEM = `-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
`
const certKey = `-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----`
cert, err := tls.X509KeyPair([]byte(certPEM), []byte(certKey))
if err != nil {
t.Error("server: loadkeys: %s", err)
}
cfg := tls.Config{
InsecureSkipVerify: true,
ServerName: "somehost.com",
Certificates: []tls.Certificate{cert},
}
conn, err := tls.Dial("tcp", "somehost.com:700", &cfg)
if err != nil {
t.Error("failed to connect: " + err.Error())
}
defer conn.Close()
So this code works in my case.