Cette classe contient la logique métier principale pour créer les fichiers d'activation et pour activer un appareil auprès d'un serveur de contenu.
This commit is contained in:
481
src/main/java/AdeptActivate.java
Normal file
481
src/main/java/AdeptActivate.java
Normal file
@@ -0,0 +1,481 @@
|
||||
import common.*;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static common.XmlUtils.ADOBE_ADEPT_NS;
|
||||
|
||||
|
||||
// =====================================================================================
|
||||
// Adept Activate
|
||||
// =====================================================================================
|
||||
|
||||
public class AdeptActivate
|
||||
{
|
||||
private final Logger LOGGER = Logger.getLogger( AdeptActivate.class.getName() );
|
||||
private boolean debug = false;
|
||||
private final Activation activation;
|
||||
private final Device device;
|
||||
private final String outputDir;
|
||||
|
||||
AdeptActivate( Activation activation, Device device, String adeptDir )
|
||||
{
|
||||
this.activation = activation;
|
||||
this.device = device;
|
||||
this.outputDir = adeptDir;
|
||||
}
|
||||
|
||||
public void setLogLevel( Level level )
|
||||
{
|
||||
LOGGER.setLevel( level );
|
||||
if (level.intValue() <= Level.FINER.intValue())
|
||||
debug = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the SignIn request XML Document. Used exclusively by the {@link #signIn( Document ) }
|
||||
* method. This SignIn request contains the devicekey, username, and password encrypted with
|
||||
* Adobe's public key from the authentication Certificate (<signInData>). It also contains two
|
||||
* RSA key pairs where the public keys merely Base64 encoded, but the private keys are encrypted
|
||||
* with the device key before being Base64 encoded. Because the device key is in the <signInData>
|
||||
* node, Adobe now has access to both the public and private keys.
|
||||
*
|
||||
* @param adobeID The Adobe ID.
|
||||
* @param adobePassword The Adobe password.
|
||||
* @param authenticationCertificate The authentication certificate (Base64 encoded). Used to
|
||||
* encrypt the <signInData> content
|
||||
* @return a sign in request with device data encrypted with Adobe's public key
|
||||
* @throws GourouException on error
|
||||
*/
|
||||
public Document buildSignInRequest( String adobeID, String adobePassword,
|
||||
String authenticationCertificate )
|
||||
throws GeneralSecurityException, GourouException
|
||||
{
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
docFactory.setNamespaceAware( true );
|
||||
DocumentBuilder docBuilder;
|
||||
try
|
||||
{
|
||||
docBuilder = docFactory.newDocumentBuilder();
|
||||
Document signInRequest = docBuilder.newDocument();
|
||||
|
||||
Element signIn = signInRequest.createElementNS( ADOBE_ADEPT_NS, "adept:signIn" );
|
||||
signInRequest.appendChild( signIn );
|
||||
|
||||
String loginMethod = activation.getLoginMethod();
|
||||
if ("anonymous".equals( adobeID ))
|
||||
{
|
||||
signIn.setAttribute( "method", "anonymous" );
|
||||
}
|
||||
else if (null != loginMethod && !loginMethod.isEmpty())
|
||||
{
|
||||
signIn.setAttribute( "method", loginMethod );
|
||||
}
|
||||
else
|
||||
{
|
||||
signIn.setAttribute( "method", "AdobeID" );
|
||||
}
|
||||
byte[] encryptedSignInData;
|
||||
byte[] deviceKeyBytes = device.getDeviceKey(); // AES session secret
|
||||
|
||||
// Build buffer <deviceKey> <len username> <username> <len password> <password>
|
||||
byte[] adobeIDBytes = adobeID.getBytes( StandardCharsets.UTF_8 ); // UTF_8 encoded adobeID
|
||||
byte[] adobePasswordBytes = adobePassword.getBytes( StandardCharsets.UTF_8 ); // ditto
|
||||
|
||||
ByteBuffer ar = ByteBuffer.allocate(
|
||||
Device.DEVICE_KEY_SIZE + 1 + adobeIDBytes.length + 1 + adobePasswordBytes.length );
|
||||
ar.put( deviceKeyBytes );
|
||||
ar.put( (byte) adobeIDBytes.length ); // single byte
|
||||
ar.put( adobeIDBytes );
|
||||
ar.put( (byte) adobePasswordBytes.length ); // single byte
|
||||
ar.put( adobePasswordBytes );
|
||||
ar.flip();
|
||||
|
||||
// Encrypt with Adobe's authentication certificate (public key)
|
||||
byte[] inData = new byte[ar.remaining()];
|
||||
ar.get( inData ); // inData contains my private encryption key and the adobeID and password.
|
||||
|
||||
// encrypt with Adobe's public key
|
||||
encryptedSignInData = CryptoUtils.x509Crypt( authenticationCertificate, Cipher.ENCRYPT_MODE, inData );
|
||||
String signInData = Base64.getEncoder().encodeToString( encryptedSignInData );
|
||||
XmlUtils.appendTextElem( signIn, "adept:signInData", signInData );
|
||||
|
||||
// Generate Auth key and License Key
|
||||
int RSA_KEY_BITS = 1024;
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "RSA" );
|
||||
keyPairGenerator.initialize( RSA_KEY_BITS );
|
||||
|
||||
// This key pair is for communications. The adobe server will return the private key
|
||||
// in a pkcs12 object using the activation's UUID as the subject name and encrypted with
|
||||
// a base-64 encoding of the device key. Presumably, it retains the public key for
|
||||
// secure messaging.
|
||||
KeyPair rsaAuthKeyPair = keyPairGenerator.generateKeyPair();
|
||||
|
||||
// Extract public key for Auth
|
||||
PublicKey publicKey = rsaAuthKeyPair.getPublic();
|
||||
String serializedData = Base64.getEncoder().encodeToString( publicKey.getEncoded() );
|
||||
XmlUtils.appendTextElem( signIn, "adept:publicAuthKey", serializedData );
|
||||
if (debug)
|
||||
{
|
||||
Files.writeString( Path.of( outputDir, "publicAuthKey.b64" ), serializedData );
|
||||
}
|
||||
// Extract private key for Auth and encrypt with device key
|
||||
PrivateKey privateKey = rsaAuthKeyPair.getPrivate();
|
||||
byte[] privateKeyData = privateKey.getEncoded();
|
||||
if (debug)
|
||||
{
|
||||
serializedData = Base64.getEncoder().encodeToString( privateKeyData );
|
||||
Files.writeString( Path.of( outputDir, "privateAuthKey.b64" ), serializedData );
|
||||
}
|
||||
byte[] encrypted = CryptoUtils.aesEncrypt( device.getDeviceKey(), privateKeyData );
|
||||
|
||||
// the encrypted data is prepended with a random 16 byte initialization vector,
|
||||
serializedData = Base64.getEncoder().encodeToString( encrypted );
|
||||
|
||||
XmlUtils.appendTextElem( signIn, "adept:encryptedPrivateAuthKey", serializedData );
|
||||
|
||||
// This keypair is for licensing content
|
||||
KeyPair rsaLicense = keyPairGenerator.generateKeyPair();
|
||||
|
||||
// Extract public key for License
|
||||
publicKey = rsaLicense.getPublic();
|
||||
serializedData = Base64.getEncoder().encodeToString( publicKey.getEncoded() );
|
||||
XmlUtils.appendTextElem( signIn, "adept:publicLicenseKey", serializedData );
|
||||
if (debug)
|
||||
{
|
||||
Files.writeString( Path.of( outputDir, "publicLicenseKey.b64" ), serializedData );
|
||||
}
|
||||
// Extract private key for License and encrypt with device key
|
||||
privateKey = rsaLicense.getPrivate();
|
||||
privateKeyData = privateKey.getEncoded();
|
||||
if (debug)
|
||||
{
|
||||
serializedData = Base64.getEncoder().encodeToString( privateKeyData );
|
||||
Files.writeString( Path.of( outputDir, "privateLicenseKey.b64" ), serializedData );
|
||||
}
|
||||
encrypted = CryptoUtils.aesEncrypt( device.getDeviceKey(), privateKeyData );
|
||||
serializedData = Base64.getEncoder().encodeToString( encrypted );
|
||||
XmlUtils.appendTextElem( signIn, "adept:encryptedPrivateLicenseKey", serializedData );
|
||||
return signInRequest;
|
||||
// TODO: At this point we have lost the Auth public key. We may want to remedy that.
|
||||
// The publicLicenseKey is embedded in the <adept:licenseCertificate> node returned
|
||||
}
|
||||
catch (ParserConfigurationException e)
|
||||
{
|
||||
throw new GourouException( "Error building sign-in request XML", e );
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign in to the activation server at the URL stored in the activation "authURL" property
|
||||
* (required to activate device). The "signInRequest" contains two pairs of RSA encryption keys.
|
||||
* The signIn response returns a UUID for the user (<user>) and a private authentication
|
||||
* key encapsulated in a non-standard pkcs12 object using the UUID as the subject name and
|
||||
* a base-64 encoding of the device key as the password. For anonymous sign-ins the private
|
||||
* key is the same as that in the "signInRequest"; for existing Adobe IDs this may not be true.<br/><br/>
|
||||
* It also returns the same private license key as was sent, re-encrypted with the device key
|
||||
* and a new initialization vector(<encryptedPrivateLicenseKey>; decrypted and stored as the
|
||||
* activation "privateLicenseKey" property). The <licenseCertificate> node contains the
|
||||
* public license key from the request wrapped in an X509 certificate signed by the activation server.
|
||||
*
|
||||
* @param signInRequest The signIn request document. May not be null.
|
||||
* @throws GourouException If the sign-in failed for any reason
|
||||
*/
|
||||
public void signIn( Document signInRequest )
|
||||
throws GourouException, IOException, GeneralSecurityException
|
||||
{
|
||||
String xmlStr = XmlUtils.docToString( signInRequest, true );
|
||||
if (debug)
|
||||
{
|
||||
Files.writeString( Paths.get( outputDir, "signInReq.xml" ), xmlStr );
|
||||
}
|
||||
String signInURL = activation.getAuthURL( ) + "/SignInDirect";
|
||||
Document credentialsDoc;
|
||||
try
|
||||
{ // NOTE: the Document returned is not namespace aware.
|
||||
credentialsDoc = activation.httpUtils.sendHTTPRequestForXML( signInURL, xmlStr,
|
||||
"application/vnd.adobe.adept+xml", null, false );
|
||||
}
|
||||
catch (ParserConfigurationException | IOException | SAXException e)
|
||||
{
|
||||
throw new GourouException( GourouException.SIGN_INVALID_CREDENTIALS,
|
||||
"Invalid credentials reply", e );
|
||||
}
|
||||
if (debug)
|
||||
{
|
||||
xmlStr = XmlUtils.docToString( credentialsDoc, false );
|
||||
Files.writeString( Paths.get( outputDir, "signInResp.xml" ), xmlStr );
|
||||
}
|
||||
Element credentialsNode = credentialsDoc.getDocumentElement();
|
||||
|
||||
if (!"credentials".equals( credentialsNode.getTagName() ))
|
||||
{
|
||||
throw new GourouException( GourouException.SIGN_INVALID_CREDENTIALS,
|
||||
"Invalid credentials reply: root is not 'credentials'" );
|
||||
}
|
||||
|
||||
// The "buildSignInRequest" method created two pair of RSA encryption keys and sends them
|
||||
// to the sign in server. TODO: The public authentication key is currently lost; the signIn
|
||||
// response returns the private authentication key encapsulated in a non-standard pkcs12
|
||||
// object using the activation's UUID as the subject name and a base-64 encoding of the device key
|
||||
// as the password. It also returns the same private license key as was sent, simply
|
||||
// re-encrypted with the device key and a new initialization vector. The licenseCertificate
|
||||
// node is the public license key wrapped in an X509 certificate signed by Adobe
|
||||
|
||||
// Handle encryptedPrivateLicenseKey
|
||||
Node encryptedPrivateLicenseKeyNode = XmlUtils.getNode( credentialsNode.getOwnerDocument(),
|
||||
"//adept:encryptedPrivateLicenseKey", true );
|
||||
String privateKeyDataBase64 = encryptedPrivateLicenseKeyNode.getFirstChild().getNodeValue();
|
||||
byte[] privateKeyDataStr = Base64.getDecoder().decode( privateKeyDataBase64 );
|
||||
|
||||
// Decrypt the private key using device key, which we sent to Adobe in the sign in request
|
||||
byte[] privateKey = decryptWithDeviceKey( privateKeyDataStr );
|
||||
privateKeyDataBase64 = Base64.getEncoder().encodeToString( privateKey ); // Appears to match the private license key sent in the signin request.
|
||||
|
||||
// Remove existing encryptedPrivateLicenseKeyNode node from the credentialsNode...
|
||||
credentialsNode.removeChild( encryptedPrivateLicenseKeyNode );
|
||||
|
||||
// and add the base64 encoded version of the decrypted privateLicenseKey into the credentialsNode instead
|
||||
XmlUtils.appendTextElem( credentialsNode, "privateLicenseKey", privateKeyDataBase64 );
|
||||
|
||||
// copy the authenticationCertificate from the activation node to the credentials node
|
||||
// TODO: why?? probably just because that's the way they are stored in the Windows registry
|
||||
String authenticationCertificateB64 = activation.getAuthenticationCertificate();
|
||||
XmlUtils.appendTextElem( credentialsNode, "adept:authenticationCertificate",
|
||||
authenticationCertificateB64 );
|
||||
|
||||
activation.storeCredentials( credentialsNode );
|
||||
try
|
||||
{
|
||||
activation.updateActivationFile();
|
||||
}
|
||||
catch (ParserConfigurationException e)
|
||||
{
|
||||
String message = "Cannot update activation file (" + activation.getActivationDirPath() + ")";
|
||||
LOGGER.log( Level.SEVERE, message );
|
||||
System.err.println( message );
|
||||
throw new GourouException( GourouException.GGOUROU_FILE_ERROR, message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the activate` request XML. Used only by the {@link #activateDevice( Document )} method.
|
||||
* Mostly just details about the device this process is running on.
|
||||
*
|
||||
* @return A new XML Document with the activation request.
|
||||
*/
|
||||
public Document buildActivateReq()
|
||||
{
|
||||
Document tempDoc;
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
docFactory.setNamespaceAware( true );
|
||||
DocumentBuilder docBuilder;
|
||||
try
|
||||
{
|
||||
docBuilder = docFactory.newDocumentBuilder();
|
||||
tempDoc = docBuilder.newDocument();
|
||||
Element root = tempDoc.createElementNS( ADOBE_ADEPT_NS, "adept:activate" );
|
||||
tempDoc.appendChild( root );
|
||||
root.setAttribute( "requestType", "initial" );
|
||||
|
||||
XmlUtils.appendTextElem( root, "adept:fingerprint",
|
||||
device.getFingerprint() );
|
||||
XmlUtils.appendTextElem( root, "adept:deviceType", device.getDeviceType());
|
||||
XmlUtils.appendTextElem( root, "adept:clientOS", device.getClientOS() );
|
||||
XmlUtils.appendTextElem( root, "adept:clientLocale",
|
||||
device.getClientLocale() );
|
||||
XmlUtils.appendTextElem( root, "adept:clientVersion",
|
||||
device.getDeviceClass() );
|
||||
|
||||
Element targetDevice = tempDoc.createElementNS( ADOBE_ADEPT_NS, "adept:targetDevice" );
|
||||
root.appendChild( targetDevice );
|
||||
XmlUtils.appendTextElem( targetDevice, "adept:softwareVersion",
|
||||
device.getHobbes() );
|
||||
XmlUtils.appendTextElem( targetDevice, "adept:clientOS",
|
||||
device.getClientOS() );
|
||||
XmlUtils.appendTextElem( targetDevice, "adept:clientLocale",
|
||||
device.getClientLocale() );
|
||||
XmlUtils.appendTextElem( targetDevice, "adept:clientVersion",
|
||||
device.getDeviceClass() );
|
||||
XmlUtils.appendTextElem( targetDevice, "adept:deviceType",
|
||||
device.getDeviceType() );
|
||||
XmlUtils.appendTextElem( targetDevice, "adept:fingerprint",
|
||||
device.getFingerprint() );
|
||||
|
||||
XmlUtils.addNonce( root );
|
||||
XmlUtils.appendTextElem( root, "adept:user", activation.getUUID() );
|
||||
}
|
||||
catch (ParserConfigurationException e)
|
||||
{
|
||||
throw new GourouException( "Error building activate request XML", e );
|
||||
}
|
||||
return tempDoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the device via network calls (activation must have successfully signed in prior to this).
|
||||
* Only one new value is returned and stored in this process: A UUID for this device (presumably
|
||||
* it is now registered with the activation server under this UUID).
|
||||
*
|
||||
* @throws GourouException if activation fails.
|
||||
*/
|
||||
public void activateDevice( Document activateReq )
|
||||
throws GourouException, IOException, GeneralSecurityException,
|
||||
ParserConfigurationException
|
||||
{
|
||||
|
||||
LOGGER.log( Level.FINER, "Activate device" );
|
||||
|
||||
// Document activateReq = buildActivateReq();
|
||||
String xmlStr; // = XmlUtils.docToString( activateReq, true );
|
||||
|
||||
Element root = activateReq.getDocumentElement();
|
||||
ByteArrayOutputStream xer = new ByteArrayOutputStream();
|
||||
//noinspection Convert2Diamond
|
||||
XmlUtils.xml2ASN( root, new HashMap<String,String>(), xer );
|
||||
|
||||
byte[] encryptedHash = CryptoUtils.signData( xer.toByteArray(), activation.getPKCS12(), activation.getUUID(),
|
||||
device.getDeviceKey() );
|
||||
String signature = Base64.getEncoder().encodeToString( encryptedHash );
|
||||
|
||||
XmlUtils.appendTextElem( root, "adept:signature", signature );
|
||||
|
||||
String activationURL = activation.getActivationURL();
|
||||
activationURL += "/Activate";
|
||||
xmlStr = XmlUtils.docToString( activateReq, false );
|
||||
if (debug)
|
||||
{
|
||||
Files.writeString( Paths.get( outputDir, "activationReq.xml" ), xmlStr );
|
||||
}
|
||||
Document activationTokenDoc; // This document has no newlines
|
||||
try
|
||||
{ // NOTE: this does not return a namespace aware document
|
||||
activationTokenDoc = activation.httpUtils.sendHTTPRequestForXML( activationURL, xmlStr,
|
||||
"application/vnd.adobe.adept+xml", null, false );
|
||||
}
|
||||
catch (IOException | SAXException | ParserConfigurationException e)
|
||||
{
|
||||
throw new GourouException( "Invalid activation token reply", e );
|
||||
}
|
||||
xmlStr = XmlUtils.docToString( activationTokenDoc, true );
|
||||
if (activationTokenDoc.getDocumentElement().getTagName().equals( "error" ))
|
||||
{
|
||||
// TODO: If the request has expired, perhaps we can build a new request and try again?
|
||||
// Are there other errors we should handle?
|
||||
throw new GourouException( "Invalid activation token reply: " + xmlStr );
|
||||
}
|
||||
if (debug)
|
||||
{
|
||||
Files.writeString( Paths.get( outputDir, "activationResp.xml" ), xmlStr );
|
||||
}
|
||||
activation.updateActivationTokenData( activationTokenDoc.getDocumentElement() );
|
||||
|
||||
// the activation token is signed. Just for grins, lets see if it validates :-)
|
||||
Element activationToken = activationTokenDoc.getDocumentElement();
|
||||
Node sigNode = XmlUtils.getNode( activationTokenDoc,"//adept:signature", false );
|
||||
activationToken.removeChild( sigNode );
|
||||
|
||||
xer = new ByteArrayOutputStream();
|
||||
//noinspection Convert2Diamond
|
||||
XmlUtils.xml2ASN( activationToken, new HashMap<String,String>(), xer );
|
||||
|
||||
MessageDigest shaMd = MessageDigest.getInstance( "SHA1" );
|
||||
|
||||
// the signature as I compute it
|
||||
signature = Base64.getEncoder().encodeToString( shaMd.digest( xer.toByteArray() ) );
|
||||
|
||||
// now get the attached signature, decrypt it with the public key, and see if it matches.
|
||||
String originalSig = sigNode.getTextContent();
|
||||
byte[] enc = CryptoUtils.x509Crypt( activation.getCertificate(), Cipher.DECRYPT_MODE, Base64.getDecoder().decode( originalSig ) );
|
||||
|
||||
originalSig = Base64.getEncoder().encodeToString( enc );
|
||||
if (!signature.equals( originalSig ))
|
||||
System.out.println( "Signatures do not match" );
|
||||
activation.updateActivationFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts data using the device key (AES-128 CBC).
|
||||
*
|
||||
* @param data The encrypted data with IV prefixed.
|
||||
* @return The decrypted data.
|
||||
*/
|
||||
private byte[] decryptWithDeviceKey( byte[] data )
|
||||
throws GeneralSecurityException
|
||||
{
|
||||
byte[] deviceKey = device.getDeviceKey();
|
||||
byte[] decryptedData; // = new byte[data.length - 16]; // Maximum possible size: total length minus IV
|
||||
|
||||
// initializationVector is the first 16 bytes of `data`. Isolate it.
|
||||
byte[] initializationVector = Arrays.copyOfRange( data, 0, 16 );
|
||||
// Actual encrypted data starts from offset 16
|
||||
byte[] encryptedPayload = Arrays.copyOfRange( data, 16, data.length );
|
||||
|
||||
decryptedData = CryptoUtils.aesDecrypt( deviceKey, initializationVector, encryptedPayload );
|
||||
|
||||
String serializedData = Base64.getEncoder().encodeToString( decryptedData );
|
||||
LOGGER.log( Level.FINE, serializedData );
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the private license key to a file.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public byte[] exportPrivateLicenseKey( )
|
||||
{
|
||||
String uuid = activation.getUUID();
|
||||
String userName = activation.getUsername();
|
||||
if (null == userName)
|
||||
userName = "anon";
|
||||
Path outputPath = Paths.get( outputDir, userName + uuid.substring( 32 ) + ".pem" );
|
||||
|
||||
String plk = activation.getPrivateLicenseKey(); // Base64 encoded PKCS8 private key
|
||||
try
|
||||
{
|
||||
BufferedWriter br = new BufferedWriter( new FileWriter( outputPath.toFile() ) );
|
||||
br.write( "-----BEGIN RSA PRIVATE KEY-----" + System.lineSeparator() );
|
||||
for (int remaining = plk.length(), start = 0; remaining > 0; remaining -= 64, start += 64)
|
||||
{
|
||||
br.write( plk.substring( start, start + (Math.min( remaining, 64 )) ) + System.lineSeparator() );
|
||||
}
|
||||
br.write( "-----END RSA PRIVATE KEY-----" + System.lineSeparator() );
|
||||
br.close();
|
||||
|
||||
// Stored as Base 64 encoding, save as binary.
|
||||
byte[] privateLicenseKey = Base64.getDecoder().decode( plk );
|
||||
outputPath = Paths.get( outputDir, userName + uuid.substring( 32 ) + ".der" );
|
||||
Files.write( outputPath, privateLicenseKey );
|
||||
LOGGER.finest( "Exported private license keys to " + outputDir );
|
||||
return privateLicenseKey;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new GourouException( GourouException.GOUROU_FILE_ERROR,
|
||||
"Unable to write " + outputPath + "to " + outputDir, e );
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user