001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2018, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.crypto; 019 020 021import java.security.GeneralSecurityException; 022 023import com.google.crypto.tink.subtle.Ed25519Sign; 024import com.nimbusds.jose.JOSEException; 025import com.nimbusds.jose.JWSAlgorithm; 026import com.nimbusds.jose.JWSHeader; 027import com.nimbusds.jose.JWSSigner; 028import com.nimbusds.jose.crypto.impl.EdDSAProvider; 029import com.nimbusds.jose.jwk.Curve; 030import com.nimbusds.jose.jwk.OctetKeyPair; 031import com.nimbusds.jose.util.Base64URL; 032import net.jcip.annotations.ThreadSafe; 033 034 035/** 036 * Ed25519 signer of {@link com.nimbusds.jose.JWSObject JWS objects}. 037 * Expects an {@link OctetKeyPair} with {@code "crv"} Ed25519. 038 * Uses the Edwards-curve Digital Signature Algorithm (EdDSA). 039 * 040 * <p>See <a href="https://tools.ietf.org/html/rfc8037">RFC 8037</a> 041 * for more information. 042 * 043 * <p>This class is thread-safe. 044 * 045 * <p>Supports the following algorithm: 046 * 047 * <ul> 048 * <li>{@link com.nimbusds.jose.JWSAlgorithm#EdDSA} 049 * </ul> 050 * 051 * <p>with the following curve: 052 * 053 * <ul> 054 * <li>{@link com.nimbusds.jose.jwk.Curve#Ed25519} 055 * </ul> 056 * 057 * @author Tim McLean 058 * @version 2018-07-11 059 */ 060@ThreadSafe 061public class Ed25519Signer extends EdDSAProvider implements JWSSigner { 062 063 064 private final OctetKeyPair privateKey; 065 066 067 private final Ed25519Sign tinkSigner; 068 069 070 /** 071 * Creates a new Ed25519 signer. 072 * 073 * @param privateKey The private key. Must be non-{@code null}, and must 074 * be of type Ed25519 ({@code "crv": "Ed25519"}). 075 * 076 * @throws JOSEException If the key subtype is not supported or if the key is not a private key 077 */ 078 public Ed25519Signer(final OctetKeyPair privateKey) 079 throws JOSEException { 080 081 super(); 082 083 if (! Curve.Ed25519.equals(privateKey.getCurve())) { 084 throw new JOSEException("Ed25519Signer only supports OctetKeyPairs with crv=Ed25519"); 085 } 086 087 if (! privateKey.isPrivate()) { 088 throw new JOSEException("The OctetKeyPair doesn't contain a private part"); 089 } 090 091 this.privateKey = privateKey; 092 093 try { 094 tinkSigner = new Ed25519Sign(privateKey.getDecodedD()); 095 096 } catch (GeneralSecurityException e) { 097 // If Tink failed to initialize; generally should not happen 098 throw new JOSEException(e.getMessage(), e); 099 } 100 } 101 102 103 /** 104 * Gets the Ed25519 private key as an {@code OctetKeyPair}. 105 * 106 * @return The private key. 107 */ 108 public OctetKeyPair getPrivateKey() { 109 110 return privateKey; 111 } 112 113 114 @Override 115 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 116 throws JOSEException { 117 118 // Check alg field in header 119 final JWSAlgorithm alg = header.getAlgorithm(); 120 if (! JWSAlgorithm.EdDSA.equals(alg)) { 121 throw new JOSEException("Ed25519Signer requires alg=EdDSA in JWSHeader"); 122 } 123 124 final byte[] jwsSignature; 125 126 try { 127 jwsSignature = tinkSigner.sign(signingInput); 128 129 } catch (GeneralSecurityException e) { 130 131 throw new JOSEException(e.getMessage(), e); 132 } 133 134 return Base64URL.encode(jwsSignature); 135 } 136}