001/* 002 * acme4j - Java ACME client 003 * 004 * Copyright (C) 2021 Richard "Shred" Körber 005 * http://acme4j.shredzone.org 006 * 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 013 */ 014package org.shredzone.acme4j.smime.challenge; 015 016import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode; 017import static org.shredzone.acme4j.toolbox.AcmeUtils.sha256hash; 018 019import jakarta.mail.internet.AddressException; 020import jakarta.mail.internet.InternetAddress; 021import org.shredzone.acme4j.Login; 022import org.shredzone.acme4j.challenge.TokenChallenge; 023import org.shredzone.acme4j.exception.AcmeProtocolException; 024import org.shredzone.acme4j.toolbox.JSON; 025 026/** 027 * Implements the {@value TYPE} challenge. 028 * 029 * @see <a href="https://datatracker.ietf.org/doc/html/rfc8823">RFC 8823</a> 030 * @since 2.12 031 */ 032public class EmailReply00Challenge extends TokenChallenge { 033 private static final long serialVersionUID = 2502329538019544794L; 034 035 /** 036 * Challenge type name: {@value} 037 */ 038 public static final String TYPE = "email-reply-00"; 039 040 private static final String KEY_FROM = "from"; 041 042 /** 043 * Creates a new generic {@link EmailReply00Challenge} object. 044 * 045 * @param login 046 * {@link Login} the resource is bound with 047 * @param data 048 * {@link JSON} challenge data 049 */ 050 public EmailReply00Challenge(Login login, JSON data) { 051 super(login, data); 052 } 053 054 /** 055 * Returns the email address in the "from" field of the challenge. 056 * 057 * @return The "from" email address, as String. 058 */ 059 public String getFrom() { 060 return getJSON().get(KEY_FROM).asString(); 061 } 062 063 /** 064 * Returns the email address of the expected sender of the "challenge" mail. 065 * <p> 066 * This is the same value that is returned by {@link #getFrom()}, but as {@link 067 * InternetAddress} instance. 068 * 069 * @return Expected sender of the challenge email. 070 */ 071 public InternetAddress getExpectedSender() { 072 try { 073 return new InternetAddress(getFrom()); 074 } catch (AddressException ex) { 075 throw new AcmeProtocolException("bad email address " + getFrom(), ex); 076 } 077 } 078 079 /** 080 * Returns the token, which is a concatenation of the part 1 that is sent by email, 081 * and part 2 that is passed into this callenge via {@link #getTokenPart2()}; 082 * 083 * @param part1 084 * Part 1 of the token, which can be found in the subject of the corresponding 085 * challenge email. 086 * @return Concatenated token 087 */ 088 public String getToken(String part1) { 089 return part1.concat(getTokenPart2()); 090 } 091 092 /** 093 * Returns the part 2 of the token to be used for this challenge. Part 2 is sent via 094 * this challenge. 095 */ 096 public String getTokenPart2() { 097 return super.getToken(); 098 } 099 100 /** 101 * This method is not implemented. Use {@link #getAuthorization(String)} instead. 102 */ 103 @Override 104 public String getAuthorization() { 105 throw new UnsupportedOperationException("use getAuthorization(String)"); 106 } 107 108 /** 109 * Returns the authorization string. 110 * 111 * @param part1 112 * Part 1 of the token, which can be found in the subject of the corresponding 113 * challenge email. 114 */ 115 public String getAuthorization(String part1) { 116 String keyAuth = keyAuthorizationFor(getToken(part1)); 117 return base64UrlEncode(sha256hash(keyAuth)); 118 } 119 120 @Override 121 protected boolean acceptable(String type) { 122 return TYPE.equals(type); 123 } 124 125}