001/* 002 * acme4j - Java ACME client 003 * 004 * Copyright (C) 2018 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; 015 016import static java.util.Objects.requireNonNull; 017 018import java.net.URL; 019import java.security.KeyPair; 020import java.util.Objects; 021 022import org.shredzone.acme4j.challenge.Challenge; 023import org.shredzone.acme4j.connector.Connection; 024import org.shredzone.acme4j.exception.AcmeException; 025import org.shredzone.acme4j.exception.AcmeLazyLoadingException; 026import org.shredzone.acme4j.exception.AcmeProtocolException; 027import org.shredzone.acme4j.toolbox.JSON; 028 029/** 030 * A {@link Login} is a {@link Session} that is connected to an {@link Account} at the 031 * ACME server. It contains the account's {@link KeyPair} and the {@link URL} of the 032 * account. 033 * <p> 034 * Note that {@link Login} objects are not serializable, as they contain a keypair and 035 * volatile data. 036 */ 037public class Login { 038 039 private final Session session; 040 private final URL accountLocation; 041 private final Account account; 042 private KeyPair keyPair; 043 044 /** 045 * Creates a new {@link Login}. 046 * 047 * @param accountLocation 048 * Account location {@link URL} 049 * @param keyPair 050 * {@link KeyPair} of the account 051 * @param session 052 * {@link Session} to be used 053 */ 054 public Login(URL accountLocation, KeyPair keyPair, Session session) { 055 this.accountLocation = Objects.requireNonNull(accountLocation, "accountLocation"); 056 this.keyPair = Objects.requireNonNull(keyPair, "keyPair"); 057 this.session = Objects.requireNonNull(session, "session"); 058 this.account = new Account(this); 059 } 060 061 /** 062 * Gets the {@link Session} that is used. 063 */ 064 public Session getSession() { 065 return session; 066 } 067 068 /** 069 * Gets the {@link KeyPair} of the ACME account. 070 */ 071 public KeyPair getKeyPair() { 072 return keyPair; 073 } 074 075 /** 076 * Gets the location {@link URL} of the account. 077 */ 078 public URL getAccountLocation() { 079 return accountLocation; 080 } 081 082 /** 083 * Gets the {@link Account} that is bound to this login. 084 * 085 * @return {@link Account} bound to the login 086 */ 087 public Account getAccount() { 088 return account; 089 } 090 091 /** 092 * Creates a new instance of {@link Authorization} and binds it to this login. 093 * 094 * @param location 095 * Location of the Authorization 096 * @return {@link Authorization} bound to the login 097 */ 098 public Authorization bindAuthorization(URL location) { 099 return new Authorization(this, requireNonNull(location, "location")); 100 } 101 102 /** 103 * Creates a new instance of {@link Certificate} and binds it to this login. 104 * 105 * @param location 106 * Location of the Certificate 107 * @return {@link Certificate} bound to the login 108 */ 109 public Certificate bindCertificate(URL location) { 110 return new Certificate(this, requireNonNull(location, "location")); 111 } 112 113 /** 114 * Creates a new instance of {@link Order} and binds it to this login. 115 * 116 * @param location 117 * Location URL of the order 118 * @return {@link Order} bound to the login 119 */ 120 public Order bindOrder(URL location) { 121 return new Order(this, requireNonNull(location, "location")); 122 } 123 124 /** 125 * Creates a new instance of {@link Challenge} and binds it to this login. 126 * 127 * @param location 128 * Location URL of the challenge 129 * @return {@link Challenge} bound to the login 130 * @since 2.8 131 */ 132 public Challenge bindChallenge(URL location) { 133 try { 134 Connection connect = session.connect(); 135 connect.sendSignedPostAsGetRequest(location, this); 136 return createChallenge(connect.readJsonResponse()); 137 } catch (AcmeException ex) { 138 throw new AcmeLazyLoadingException(Challenge.class, location, ex); 139 } 140 } 141 142 /** 143 * Creates a new instance of a challenge and binds it to this login. 144 * 145 * @param location 146 * Location URL of the challenge 147 * @param type 148 * Expected challenge type 149 * @return Challenge bound to the login 150 * @throws AcmeProtocolException 151 * if the challenge found at the location does not match the expected 152 * challenge type. 153 * @since 2.12 154 */ 155 public <C extends Challenge> C bindChallenge(URL location, Class<C> type) { 156 Challenge challenge = bindChallenge(location); 157 if (!type.isInstance(challenge)) { 158 throw new AcmeProtocolException("Challenge type " + challenge.getType() 159 + " does not match requested class " + type); 160 } 161 return type.cast(challenge); 162 } 163 164 /** 165 * Creates a {@link Challenge} instance for the given challenge data. 166 * 167 * @param data 168 * Challenge JSON data 169 * @return {@link Challenge} instance 170 */ 171 public Challenge createChallenge(JSON data) { 172 Challenge challenge = session.provider().createChallenge(this, data); 173 if (challenge == null) { 174 throw new AcmeProtocolException("Could not create challenge for: " + data); 175 } 176 return challenge; 177 } 178 179 /** 180 * Sets a different {@link KeyPair}. 181 */ 182 protected void setKeyPair(KeyPair keyPair) { 183 this.keyPair = Objects.requireNonNull(keyPair, "keyPair"); 184 } 185 186}