use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class AbstractStatementValidator method getJcaName.
protected String getJcaName(@NonNull COSEAlgorithmIdentifier alg) {
String jcaName;
try {
SignatureAlgorithm signatureAlgorithm = alg.toSignatureAlgorithm();
jcaName = signatureAlgorithm.getJcaName();
} catch (IllegalArgumentException e) {
throw new BadAttestationStatementException("alg is not signature algorithm", e);
}
return jcaName;
}
use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class KeyDescriptionValidator method getIntegerFromAsn1.
@Nullable
private BigInteger getIntegerFromAsn1(Asn1ParseResult asn1Value) throws IOException {
if (asn1Value == null) {
return null;
}
if (!asn1Value.isPrimitive()) {
throw new BadAttestationStatementException(String.format("ASN1Integer is expected. Found %s instead.", asn1Value.getClass().getName()));
}
Asn1Integer value = new Asn1Integer();
value.decode(asn1Value);
return value.getValue();
}
use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class AndroidSafetyNetAttestationStatementValidator method validateNonce.
private void validateNonce(@Nullable String nonce, @NonNull byte[] authenticatorData, @NonNull byte[] clientDataHash) {
if (nonce == null) {
throw new BadAttestationStatementException("Nonce in the Android safetynet response is null.");
}
ByteBuffer buffer = ByteBuffer.allocate(authenticatorData.length + clientDataHash.length);
byte[] data = buffer.put(authenticatorData).put(clientDataHash).array();
byte[] hash = MessageDigestUtil.createSHA256().digest(data);
// there is no need to prevent timing attack and it is OK to use `Arrays.equals` instead of `MessageDigest.isEqual` here.
if (!Arrays.equals(hash, Base64Util.decode(nonce))) {
throw new BadAttestationStatementException("Nonce in the Android safetynet response doesn't match.");
}
}
use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class AndroidSafetyNetAttestationStatementValidator method validate.
@SuppressWarnings("ConstantConditions")
@Override
@NonNull
public AttestationType validate(@NonNull CoreRegistrationObject registrationObject) {
AssertUtil.notNull(registrationObject, "registrationObject must not be null");
if (!supports(registrationObject)) {
throw new IllegalArgumentException("Specified format is not supported by " + this.getClass().getName());
}
AndroidSafetyNetAttestationStatement attestationStatement = (AndroidSafetyNetAttestationStatement) registrationObject.getAttestationObject().getAttestationStatement();
validateAttestationStatementNotNull(attestationStatement);
if (attestationStatement.getX5c().isEmpty()) {
throw new BadAttestationStatementException("No attestation certificate is found in android safetynet attestation statement.");
}
// / Given the verification procedure inputs attStmt, authenticatorData and clientDataHash,
// the verification procedure is as follows:
// / Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it
// to extract the contained fields.
// / Verify that response is a valid SafetyNet response of version ver.
versionValidator.validate(attestationStatement.getVer());
// / Verify that the nonce in the response is identical to the Base64url encoding of the SHA-256 hash of the concatenation of authenticatorData and clientDataHash.
Response response = attestationStatement.getResponse().getPayload();
String nonce = response.getNonce();
byte[] authenticatorData = registrationObject.getAuthenticatorDataBytes();
validateNonce(nonce, authenticatorData, registrationObject.getClientDataHash());
// / Let attestationCert be the attestation certificate.
// / Verify that attestationCert is issued to the hostname "attest.android.com" (see SafetyNet online documentation).
AttestationCertificate attestationCertificate = attestationStatement.getX5c().getEndEntityAttestationCertificate();
if (!Objects.equals(attestationCertificate.getSubjectCommonName(), "attest.android.com")) {
throw new BadAttestationStatementException("The attestation certificate is not issued to 'attest.android.com'.");
}
// / Verify that the ctsProfileMatch attribute in the payload of response is true.
if (!Objects.equals(response.getCtsProfileMatch(), true)) {
throw new BadAttestationStatementException("The profile of the device doesn't match the profile of a device that has passed Android Compatibility Test Suite.");
}
if (response.getTimestampMs() == null) {
throw new BadAttestationStatementException("timestampMs is null.");
}
// Verify the timestampMs doesn't violate backwardThreshold
if (Instant.ofEpochMilli(response.getTimestampMs()).isBefore(registrationObject.getTimestamp().minus(Duration.ofSeconds(backwardThreshold)))) {
throw new BadAttestationStatementException("timestampMs violates backwardThreshold.");
}
// Verify the timestampMs doesn't violate forwardThreshold
if (Instant.ofEpochMilli(response.getTimestampMs()).isAfter(registrationObject.getTimestamp().plus(Duration.ofSeconds(forwardThreshold)))) {
throw new BadAttestationStatementException("timestampMs violates forwardThreshold.");
}
if (!attestationStatement.getResponse().isValidSignature()) {
throw new BadAttestationStatementException("Android safetynet response in the attestation statement doesn't have a valid signature.");
}
// / If successful, return implementation-specific values representing attestation type Basic and attestation trust path attestationCert.
return AttestationType.BASIC;
}
use of com.webauthn4j.validator.exception.BadAttestationStatementException in project webauthn4j by webauthn4j.
the class AppleAnonymousAttestationStatementValidator method validateNonce.
private void validateNonce(@NonNull CoreRegistrationObject registrationObject) {
AppleAnonymousAttestationStatement attestationStatement = (AppleAnonymousAttestationStatement) registrationObject.getAttestationObject().getAttestationStatement();
byte[] nonce = getNonce(registrationObject);
byte[] extensionValue = attestationStatement.getX5c().getEndEntityAttestationCertificate().getCertificate().getExtensionValue("1.2.840.113635.100.8.2");
byte[] extracted;
try {
Asn1OctetString extensionEnvelope = new Asn1OctetString();
extensionEnvelope.decode(extensionValue);
extensionEnvelope.getValue();
byte[] extensionEnvelopeValue = extensionEnvelope.getValue();
Asn1Container container = (Asn1Container) Asn1Parser.parse(ByteBuffer.wrap(extensionEnvelopeValue));
Asn1ParseResult firstElement = container.getChildren().get(0);
Asn1OctetString octetString = new Asn1OctetString();
octetString.decode(firstElement);
extracted = octetString.getValue();
} catch (IOException | RuntimeException e) {
throw new BadAttestationStatementException("Failed to extract nonce from Apple anonymous attestation statement.", e);
}
// there is no need to prevent timing attack and it is OK to use `Arrays.equals` instead of `MessageDigest.isEqual` here.
if (!Arrays.equals(extracted, nonce)) {
throw new BadAttestationStatementException("nonce doesn't match.");
}
}
Aggregations