├── sample ├── build │ ├── tmp │ │ └── jar │ │ │ └── MANIFEST.MF │ ├── libs │ │ └── sample-0.8.3.jar │ └── classes │ │ └── java │ │ └── main │ │ └── foundation │ │ └── icon │ │ └── did │ │ ├── util │ │ └── FileUtils.class │ │ ├── CredentialSample.class │ │ ├── DidManageSample.class │ │ ├── KeyPoviderSample.class │ │ ├── api │ │ ├── ApiResponse.class │ │ ├── AuthResponse.class │ │ ├── ClaimIssueApi.class │ │ ├── ClaimIssueApi$Api.class │ │ └── CredentialResponse.class │ │ ├── config │ │ ├── DevConfig.class │ │ ├── LocalConfig.class │ │ ├── SampleConfig.class │ │ └── SampleKeys.class │ │ ├── PresentationSample.class │ │ └── exception │ │ └── SampleException.class ├── src │ └── main │ │ └── java │ │ └── foundation │ │ └── icon │ │ └── did │ │ ├── exception │ │ └── SampleException.java │ │ ├── api │ │ ├── AuthResponse.java │ │ ├── CredentialResponse.java │ │ ├── ApiResponse.java │ │ └── ClaimIssueApi.java │ │ ├── config │ │ ├── DevConfig.java │ │ ├── LocalConfig.java │ │ ├── SampleKeys.java │ │ └── SampleConfig.java │ │ ├── util │ │ └── FileUtils.java │ │ ├── KeyPoviderSample.java │ │ └── DidManageSample.java ├── README.md └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── lib ├── build │ └── libs │ │ └── icon-did-0.8.6.jar ├── src │ ├── main │ │ └── java │ │ │ └── foundation │ │ │ └── icon │ │ │ └── did │ │ │ ├── document │ │ │ ├── Encoder.java │ │ │ ├── EncodeType.java │ │ │ ├── AuthenticationProperty.java │ │ │ ├── Document.java │ │ │ └── PublicKeyProperty.java │ │ │ ├── exceptions │ │ │ ├── ResolveException.java │ │ │ ├── KeyPairException.java │ │ │ ├── JwtException.java │ │ │ ├── AlgorithmException.java │ │ │ ├── KeystoreException.java │ │ │ └── TransactionException.java │ │ │ ├── jwt │ │ │ ├── ConvertJwt.java │ │ │ ├── Header.java │ │ │ ├── IssuerDid.java │ │ │ └── Payload.java │ │ │ ├── core │ │ │ ├── RS256Algorithm.java │ │ │ ├── PropertyName.java │ │ │ ├── NoneAlgorithm.java │ │ │ ├── ES256Algorithm.java │ │ │ ├── KeyProvider.java │ │ │ ├── DidKeyHolder.java │ │ │ ├── AlgorithmProvider.java │ │ │ ├── ES256KAlgorithm.java │ │ │ └── Algorithm.java │ │ │ ├── score │ │ │ ├── DidScore.java │ │ │ └── ScoreParameter.java │ │ │ ├── Presentation.java │ │ │ └── Credential.java │ └── test │ │ ├── resources │ │ ├── ec256-key-public.pem │ │ ├── ec256-key-public-invalid.pem │ │ ├── ec384-key-public.pem │ │ ├── ec256-key-pair.pem │ │ ├── ec384-key-public-invalid.pem │ │ ├── ec256-key-private.pem │ │ ├── ec512-key-public.pem │ │ ├── ec512-key-public-invalid.pem │ │ ├── ec384-key-pair.pem │ │ ├── ec384-key-private.pem │ │ ├── ec512-key-pair.pem │ │ ├── ec512-key-private.pem │ │ ├── rsa-public.pem │ │ ├── rsa-public_invalid.pem │ │ └── rsa-private.pem │ │ └── java │ │ └── foundation │ │ └── icon │ │ └── did │ │ ├── TempFileProvider.java │ │ ├── IconServiceFactory.java │ │ ├── CredentialTest.java │ │ ├── core │ │ ├── KeystoreTest.java │ │ ├── AlgorithmTest.java │ │ ├── AlgorithmProvider.java │ │ └── KeyProviderTest.java │ │ ├── JJwtTest.java │ │ ├── RepeatedTest.java │ │ ├── DIDBuildJwtTest.java │ │ ├── TestKeys.java │ │ ├── PemUtilsTest.java │ │ ├── IssuerDidTest.java │ │ ├── PresentationTest.java │ │ ├── DidJwtServiceTest.java │ │ └── ClaimRequestTest.java └── build.gradle ├── .idea ├── vcs.xml ├── modules.xml ├── did-sdk-java.iml ├── misc.xml └── workspace.xml ├── gradle.properties ├── did-sdk-java.iml ├── gradlew.bat ├── CHANGELOG.md └── gradlew /sample/build/tmp/jar/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | 3 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'did-sdk-java' 2 | include 'lib' 3 | include 'sample' 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /lib/build/libs/icon-did-0.8.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/lib/build/libs/icon-did-0.8.6.jar -------------------------------------------------------------------------------- /sample/build/libs/sample-0.8.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/libs/sample-0.8.3.jar -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/util/FileUtils.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/util/FileUtils.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/CredentialSample.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/CredentialSample.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/DidManageSample.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/DidManageSample.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/KeyPoviderSample.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/KeyPoviderSample.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/api/ApiResponse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/api/ApiResponse.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/api/AuthResponse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/api/AuthResponse.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/config/DevConfig.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/config/DevConfig.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/PresentationSample.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/PresentationSample.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/api/ClaimIssueApi.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/api/ClaimIssueApi.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/config/LocalConfig.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/config/LocalConfig.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/config/SampleConfig.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/config/SampleConfig.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/config/SampleKeys.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/config/SampleKeys.class -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/document/Encoder.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.document; 2 | 3 | public interface Encoder { 4 | String encode(byte[] data); 5 | byte[] decode(String data); 6 | } 7 | -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/api/ClaimIssueApi$Api.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/api/ClaimIssueApi$Api.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/api/CredentialResponse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/api/CredentialResponse.class -------------------------------------------------------------------------------- /sample/build/classes/java/main/foundation/icon/did/exception/SampleException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/did-sdk-java/HEAD/sample/build/classes/java/main/foundation/icon/did/exception/SampleException.class -------------------------------------------------------------------------------- /lib/src/test/resources/ec256-key-public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQgb5npLHd0Bk61bNnjK632uwmBfr 3 | F7I8hoPgaOZjyhh+BrPDO6CL6D/aW/yPObXXm7SpZogmRwGROcOA3yUleg== 4 | -----END PUBLIC KEY----- 5 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec256-key-public-invalid.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoBUyo8CQAFPeYPvv78ylh5MwFZjT 3 | CLQeb042TjiMJxG+9DLFmRSMlBQ9T/RsLLc+PmpB1+7yPAR+oR5gZn3kJQ== 4 | -----END PUBLIC KEY----- 5 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec384-key-public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEns0hpQLuMAGiXuOu+pkDNBf+bJSPhJHa 3 | bUSXX7ED/ZjcXu0Xcx4HEsO4/1sWuOZ5ZxZxazzy+L4iSjgUcTAdY41QPZsvT4sc 4 | SRqAdtqg0MBWz/JXZhKemm8HQbFzWnjz 5 | -----END PUBLIC KEY----- 6 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/exception/SampleException.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.exception; 2 | 3 | public class SampleException extends RuntimeException { 4 | 5 | public SampleException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/api/AuthResponse.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.api; 2 | 3 | import lombok.Getter; 4 | import lombok.ToString; 5 | 6 | @Getter 7 | @ToString 8 | public class AuthResponse extends ApiResponse { 9 | String token; 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec256-key-pair.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIDxiRgJuF9X7wbgtc0qTv+CM8ej13zTGoimkuUVJBahBoAoGCCqGSM49 3 | AwEHoUQDQgAEQgb5npLHd0Bk61bNnjK632uwmBfrF7I8hoPgaOZjyhh+BrPDO6CL 4 | 6D/aW/yPObXXm7SpZogmRwGROcOA3yUleg== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec384-key-public-invalid.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEJ0tamPyAJVUhDO1DlceSYCdA9WKVX6nO 3 | K4VYetXvqmMKdyVkaoA4Gl02KoVLujiSSSAE6oK/Hf7x2fagaE9LgJdJxg07Ip+T 4 | C6cgFi2HHDeXG7djB5Zl1TKA9/w/8iW5 5 | -----END PUBLIC KEY----- 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Dec 20 15:21:30 KST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/exceptions/ResolveException.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.exceptions; 2 | 3 | 4 | public class ResolveException extends RuntimeException { 5 | 6 | public ResolveException(String message) { 7 | super(message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/api/CredentialResponse.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.api; 2 | 3 | import lombok.Getter; 4 | import lombok.ToString; 5 | 6 | @Getter 7 | @ToString 8 | public class CredentialResponse extends ApiResponse { 9 | String credentialClaim; 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec256-key-private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPGJGAm4X1fvBuC1z 3 | SpO/4Izx6PXfNMaiKaS5RUkFqEGhRANCAARCBvmeksd3QGTrVs2eMrrfa7CYF+sX 4 | sjyGg+Bo5mPKGH4Gs8M7oIvoP9pb/I85tdebtKlmiCZHAZE5w4DfJSV6 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/api/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.api; 2 | 3 | import lombok.*; 4 | 5 | @Getter 6 | @Setter 7 | @ToString 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class ApiResponse { 11 | Boolean success; 12 | String message; 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec512-key-public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAmG8JrpLz14+qUs7oxFX0pCoe90Ah 3 | MMB/9ZENy8KZ+us26i/6PiBBc7XaiEi6Q8Icz2tiazwSpyLPeBrFVPFkPgIADyLa 4 | T0fp7D2JKHWpdrWQvGLLMwGqYCaaDi79KugPo6V4bnpLBlVtbH4ogg0Hqv89BVyI 5 | ZfwWPCBH+Zssei1VlgM= 6 | -----END PUBLIC KEY----- 7 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/exceptions/KeyPairException.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.exceptions; 2 | 3 | import java.security.GeneralSecurityException; 4 | 5 | public class KeyPairException extends GeneralSecurityException { 6 | public KeyPairException(String msg) { 7 | super(msg); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec512-key-public-invalid.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBgtBWt9rVtdQPdeoChvCXnXHvkg/F 3 | 41AAubIVd+3yTvLFmxuHuaor9CHl7FlT3532mUG+GG0EEw5UuYkFrg/ABBgAfuVf 4 | wO1u0V0wJPkmpxcnZoojFaAOKcsBvUvDulbBh0HhAyd+nlfZquvV43uwFVpn2Cjb 5 | Lx+/AT1PE6Muqy4JkGU= 6 | -----END PUBLIC KEY----- 7 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec384-key-pair.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MIGkAgEBBDCVWQsOJHjKD0I4cXOYJm4G8i5c7IMhFbxFq57OUlrTVmND43dvvNW1 3 | oQ6i6NiXEQWgBwYFK4EEACKhZANiAASezSGlAu4wAaJe4676mQM0F/5slI+Ekdpt 4 | RJdfsQP9mNxe7RdzHgcSw7j/Wxa45nlnFnFrPPL4viJKOBRxMB1jjVA9my9PixxJ 5 | GoB22qDQwFbP8ldmEp6abwdBsXNaePM= 6 | -----END EC PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec384-key-private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCVWQsOJHjKD0I4cXOY 3 | Jm4G8i5c7IMhFbxFq57OUlrTVmND43dvvNW1oQ6i6NiXEQWhZANiAASezSGlAu4w 4 | AaJe4676mQM0F/5slI+EkdptRJdfsQP9mNxe7RdzHgcSw7j/Wxa45nlnFnFrPPL4 5 | viJKOBRxMB1jjVA9my9PixxJGoB22qDQwFbP8ldmEp6abwdBsXNaePM= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /.idea/did-sdk-java.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/exceptions/JwtException.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.exceptions; 2 | 3 | public class JwtException extends RuntimeException { 4 | 5 | public JwtException(String message) { 6 | super(message); 7 | } 8 | 9 | public JwtException(String message, Throwable cause) { 10 | super(message, cause); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec512-key-pair.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MIHbAgEBBEHzl1DpZSQJ8YhCbN/uvo5SOu0BjDDX9Gub6zsBW6B2TxRzb5sBeQaW 3 | VscDUZha4Xr1HEWpVtua9+nEQU/9Aq9Pl6AHBgUrgQQAI6GBiQOBhgAEAJhvCa6S 4 | 89ePqlLO6MRV9KQqHvdAITDAf/WRDcvCmfrrNuov+j4gQXO12ohIukPCHM9rYms8 5 | Eqciz3gaxVTxZD4CAA8i2k9H6ew9iSh1qXa1kLxiyzMBqmAmmg4u/SroD6OleG56 6 | SwZVbWx+KIINB6r/PQVciGX8FjwgR/mbLHotVZYD 7 | -----END EC PRIVATE KEY----- 8 | -------------------------------------------------------------------------------- /lib/src/test/resources/ec512-key-private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIHtAgEAMBAGByqGSM49AgEGBSuBBAAjBIHVMIHSAgEBBEHzl1DpZSQJ8YhCbN/u 3 | vo5SOu0BjDDX9Gub6zsBW6B2TxRzb5sBeQaWVscDUZha4Xr1HEWpVtua9+nEQU/9 4 | Aq9Pl6GBiQOBhgAEAJhvCa6S89ePqlLO6MRV9KQqHvdAITDAf/WRDcvCmfrrNuov 5 | +j4gQXO12ohIukPCHM9rYms8Eqciz3gaxVTxZD4CAA8i2k9H6ew9iSh1qXa1kLxi 6 | yzMBqmAmmg4u/SroD6OleG56SwZVbWx+KIINB6r/PQVciGX8FjwgR/mbLHotVZYD 7 | -----END PRIVATE KEY----- 8 | -------------------------------------------------------------------------------- /sample/README.md: -------------------------------------------------------------------------------- 1 | ## Sample 2 | 3 | `did-sdk-java` 의 샘플 모음 4 | 5 | - KeyProviderSample : KeyProvider 생성 샘플 6 | - DidManageSample : DID Document Create, Add Key, Revoke Key, Read 샘플 7 | - CredentialSample : Owner 가 Issuer 에게 Credential 요청하고 발급받아 확인하는 샘플 8 | - PresentationSample : Owner 가 Presentation 을 생성하고 Verifier 에게 전달해서 Verifier 가 Claim 을 확인하는 샘플 9 | - ClientToIssuerSample : Client 에서 Issuer(Server) 와 연동해서 Credential 요청하고 발급받아 확인하는 샘플 10 | 11 | -------------------------------------------------------------------------------- /lib/src/test/resources/rsa-public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGbXWiK3dQTyCbX5xdE4 3 | yCuYp0AF2d15Qq1JSXT/lx8CEcXb9RbDddl8jGDv+spi5qPa8qEHiK7FwV2KpRE9 4 | 83wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVs 5 | WXI9C+yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT 6 | 69s7of9+I9l5lsJ9cozf1rxrXX4V1u/SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8 7 | AziMCxS+VrRPDM+zfvpIJg3JljAh3PJHDiLu902v9w+Iplu1WyoB2aPfitxEhRN0 8 | YwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /lib/src/test/resources/rsa-public_invalid.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxzYuc22QSst/dS7geYYK 3 | 5l5kLxU0tayNdixkEQ17ix+CUcUbKIsnyftZxaCYT46rQtXgCaYRdJcbB3hmyrOa 4 | vkhTpX79xJZnQmfuamMbZBqitvscxW9zRR9tBUL6vdi/0rpoUwPMEh8+Bw7CgYR0 5 | FK0DhWYBNDfe9HKcyZEv3max8Cdq18htxjEsdYO0iwzhtKRXomBWTdhD5ykd/fAC 6 | VTr4+KEY+IeLvubHVmLUhbE5NgWXxrRpGasDqzKhCTmsa2Ysf712rl57SlH0Wz/M 7 | r3F7aM9YpErzeYLrl0GhQr9BVJxOvXcVd4kmY+XkiCcrkyS1cnghnllh+LCwQu1s 8 | YwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/exceptions/AlgorithmException.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.exceptions; 2 | 3 | import java.security.GeneralSecurityException; 4 | 5 | public class AlgorithmException extends GeneralSecurityException { 6 | 7 | public AlgorithmException(String message) { 8 | super(message); 9 | } 10 | 11 | public AlgorithmException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public AlgorithmException(Throwable cause) { 16 | super(cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/exceptions/KeystoreException.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.exceptions; 2 | 3 | /** 4 | * Original Code 5 | * https://github.com/web3j/web3j/blob/master/crypto/src/main/java/org/web3j/crypto/CipherException.java 6 | */ 7 | public class KeystoreException extends Exception { 8 | 9 | public KeystoreException(String message) { 10 | super(message); 11 | } 12 | 13 | KeystoreException(Throwable cause) { 14 | super(cause); 15 | } 16 | 17 | public KeystoreException(String message, Throwable cause) { 18 | super(message, cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 ICON Foundation. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | GROUP=foundation.icon 18 | VERSION_NAME=0.8.6 -------------------------------------------------------------------------------- /did-sdk-java.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/exceptions/TransactionException.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.exceptions; 2 | 3 | import foundation.icon.icx.data.TransactionResult; 4 | 5 | public class TransactionException extends RuntimeException { 6 | private static String getMessage(TransactionResult result) { 7 | String message = "Fail transaction !! txHash:" + result.getTxHash(); 8 | TransactionResult.Failure failure = result.getFailure(); 9 | if (failure != null) { 10 | message = message + "\n" + failure.getMessage(); 11 | } 12 | return message; 13 | } 14 | 15 | public TransactionException(String message) { 16 | super(message); 17 | } 18 | 19 | public TransactionException(TransactionResult result) { 20 | super(getMessage(result)); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/TempFileProvider.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | 5 | import java.io.File; 6 | import java.nio.file.Files; 7 | 8 | 9 | 10 | /** 11 | * Base class for tests wishing to use temporary file locations. 12 | */ 13 | public class TempFileProvider { 14 | private File tempDir; 15 | protected String tempDirPath; 16 | 17 | @BeforeEach 18 | public void setUp() throws Exception { 19 | tempDir = Files.createTempDirectory( 20 | TempFileProvider.class.getSimpleName()).toFile(); 21 | tempDirPath = tempDir.getPath(); 22 | } 23 | 24 | public void tearDown() throws Exception { 25 | for (File file:tempDir.listFiles()) { 26 | file.delete(); 27 | } 28 | tempDir.delete(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Class structureJava 13 | 14 | 15 | Java 16 | 17 | 18 | Probable bugsJava 19 | 20 | 21 | 22 | 23 | Android 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/config/DevConfig.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.config; 2 | 3 | import foundation.icon.icx.data.Address; 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.math.BigInteger; 8 | 9 | @NoArgsConstructor(access = AccessLevel.PACKAGE) 10 | final public class DevConfig implements SampleConfig { 11 | private String nodeUrl = "https://test-ctz.solidwallet.io/api/v3"; 12 | private BigInteger networkId = BigInteger.valueOf(2); 13 | 14 | @Override 15 | public String getNodeUrl() { 16 | return nodeUrl; 17 | } 18 | 19 | @Override 20 | public BigInteger getNetworkId() { 21 | return networkId; 22 | } 23 | 24 | @Override 25 | public Address getScoreAddress() { 26 | return new Address("cx9a96c0dcf0567635309809d391908c32fcca5310"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/config/LocalConfig.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.config; 2 | 3 | import foundation.icon.icx.data.Address; 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.math.BigInteger; 8 | 9 | @NoArgsConstructor(access = AccessLevel.PACKAGE) 10 | final public class LocalConfig implements SampleConfig { 11 | private final String nodeUrl = "http://localhost:9000/api/v3"; 12 | private BigInteger networkId = BigInteger.valueOf(3); 13 | 14 | @Override 15 | public String getNodeUrl() { 16 | return nodeUrl; 17 | } 18 | 19 | @Override 20 | public BigInteger getNetworkId() { 21 | return networkId; 22 | } 23 | 24 | @Override 25 | public Address getScoreAddress() { 26 | return new Address("cx26484cf9cb42b6eebbf537fbfe6b7df3f86c5079"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'io.franzbecker.gradle-lombok' version '1.14' 4 | } 5 | 6 | sourceCompatibility = 1.8 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | ext { 13 | logbackVersion = '1.2.3' 14 | iconSdkVersion = '0.9.11' 15 | gsonVersion = '2.8.0' 16 | retrofitVersion = '2.5.0' 17 | okhttpVersion = '3.12.1' 18 | } 19 | 20 | dependencies { 21 | // implementation fileTree(dir: 'libs', include: '*.jar') 22 | implementation project(':lib') 23 | implementation "ch.qos.logback:logback-classic:$logbackVersion" 24 | implementation "foundation.icon:icon-sdk:$iconSdkVersion" 25 | implementation "com.google.code.gson:gson:$gsonVersion" 26 | 27 | implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" 28 | implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" 29 | implementation "com.squareup.okhttp3:logging-interceptor:$okhttpVersion" 30 | } 31 | 32 | lombok { 33 | sha256 = "" 34 | } -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/util/FileUtils.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.util; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonParser; 7 | 8 | import java.io.*; 9 | 10 | public class FileUtils { 11 | 12 | public static void writeClaim(String fileName, String key, String value) { 13 | JsonObject object = new JsonObject(); 14 | object.addProperty(key, value); 15 | 16 | try (Writer writer = new FileWriter(fileName)) { 17 | Gson gson = new GsonBuilder().create(); 18 | gson.toJson(object, writer); 19 | } catch (IOException e) { 20 | e.printStackTrace(); 21 | } 22 | } 23 | 24 | public static String loadClaim(String fileName, String key) throws FileNotFoundException { 25 | InputStreamReader reader = new InputStreamReader(new FileInputStream(fileName)); 26 | JsonParser parser = new JsonParser(); 27 | JsonObject object = parser.parse(reader).getAsJsonObject(); 28 | return object.get(key).getAsString(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lib/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | sourceCompatibility = 1.8 6 | 7 | repositories { 8 | mavenCentral() 9 | } 10 | 11 | ext { 12 | bouncycastleVersion = '1.60' 13 | iconSdkVersion = '0.9.11' 14 | gsonVersion = '2.8.0' 15 | okhttpVersion = '3.11.0' 16 | junitVersion = '5.4.0-RC2' 17 | logbackVersion = '1.2.3' 18 | javaJwtVersion = '3.5.0' 19 | } 20 | 21 | dependencies { 22 | implementation "com.google.code.gson:gson:$gsonVersion" 23 | implementation "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion" 24 | implementation "foundation.icon:icon-sdk:$iconSdkVersion" 25 | implementation "com.squareup.okhttp3:logging-interceptor:$okhttpVersion" 26 | 27 | testImplementation "org.junit.jupiter:junit-jupiter-engine:$junitVersion" 28 | testImplementation "ch.qos.logback:logback-classic:$logbackVersion" 29 | 30 | // test jjwt 31 | testImplementation 'io.jsonwebtoken:jjwt-api:0.10.5' 32 | testRuntimeOnly 'io.jsonwebtoken:jjwt-impl:0.10.5' 33 | testRuntimeOnly('io.jsonwebtoken:jjwt-orgjson:0.10.5') { 34 | exclude group: 'org.json', module: 'json' //provided by Android natively 35 | } 36 | testImplementation 'io.jsonwebtoken:jjwt-jackson:0.10.5' 37 | } 38 | 39 | jar { 40 | baseName = 'icon-did' 41 | } -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/document/EncodeType.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.document; 2 | 3 | import org.bouncycastle.util.encoders.Base64; 4 | import org.bouncycastle.util.encoders.Hex; 5 | 6 | public enum EncodeType implements Encoder { 7 | HEX("publicKeyHex") { 8 | public String encode(byte[] data) { 9 | return Hex.toHexString(data); 10 | } 11 | 12 | public byte[] decode(String data) { 13 | return Hex.decode(data); 14 | } 15 | }, 16 | BASE64("publicKeyBase64") { 17 | public String encode(byte[] data) { 18 | return Base64.toBase64String(data); 19 | } 20 | 21 | public byte[] decode(String data) { 22 | return Base64.decode(data); 23 | } 24 | }; 25 | 26 | private String name; 27 | 28 | EncodeType(String name) { 29 | this.name = name; 30 | } 31 | 32 | public String getValue() { 33 | return name; 34 | } 35 | 36 | public static EncodeType fromString(String type) { 37 | if (type != null) { 38 | for (EncodeType t : EncodeType.values()) { 39 | if (type.equalsIgnoreCase(t.getValue())) { 40 | return t; 41 | } 42 | } 43 | } 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/config/SampleKeys.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.config; 2 | 3 | /** 4 | * Sample Keypair 5 | */ 6 | public class SampleKeys { 7 | 8 | private SampleKeys() { 9 | } 10 | 11 | // hx797a20d278b8aca4fb1927d80a79aab88b328cf3 12 | public final static String ISSUER_WALLET_PRIVATE_KEY = "a03694b07ab48f01e8f617e4c2d3953fcae8a0347d17a14a426030ff0de05e41"; 13 | 14 | public final static String ISSUER_KEY_ID = "EmailIssuer"; 15 | 16 | // hx228ffa8cf35d6ce624a527f7442e5dc94b82eb74 17 | public final static String OWNER_WALLET_PRIVATE_KEY = "a03694b07ab48f01e8f617e4c2d3953fcae8a0347d17a14a426030ff0de05e41"; 18 | 19 | public final static String OWNER_PUBLIC_KEY_ES256 = "3059301306072a8648ce3d020106082a8648ce3d030107034200041b6b33dc71175b5117a61f2738aa6d593812847cc5afebdca19808ef0e3f056e79a2e3bfd84219a8d7f3a453100e31cb5867936f244820f18da69ef435c530c9"; 20 | 21 | public final static String OWNER_PRIVATE_KEY_ES256 = "308193020100301306072a8648ce3d020106082a8648ce3d03010704793077020101042063e447fa7d6d4d99c180b6a51cc829ded2c106bb46b35dd9142f349bcd8c560ea00a06082a8648ce3d030107a144034200041b6b33dc71175b5117a61f2738aa6d593812847cc5afebdca19808ef0e3f056e79a2e3bfd84219a8d7f3a453100e31cb5867936f244820f18da69ef435c530c9"; 22 | 23 | public final static String OWNER_KEY_ID = "owner"; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/jwt/ConvertJwt.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.jwt; 2 | 3 | import foundation.icon.did.Credential; 4 | import foundation.icon.did.Presentation; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * A interface to convert {@linkplain Credential} and {@linkplain Presentation} to 'Json Web Token' 10 | */ 11 | public interface ConvertJwt { 12 | 13 | /** 14 | * The time in seconds from the issued time to expiration 15 | * 16 | * @return the duration in seconds 17 | */ 18 | int getDuration(); 19 | 20 | /** 21 | * Create a new JWT with default iat and exp 22 | *

23 | * default iat : the current time 24 | * default exp : default iat + {@code getDuration()} 25 | * 26 | * @return the Jwt object 27 | */ 28 | default Jwt buildJwt() { 29 | Date issued = new Date(); 30 | long duration = getDuration() * 1000L; // to milliseconds 31 | Date expiration = new Date(issued.getTime() + duration); 32 | return buildJwt(issued, expiration); 33 | } 34 | 35 | /** 36 | * Create a new JWT 37 | * 38 | * @param issued the time at which the JWT was issued 39 | * @param expiration the time at which the JWT must not be accepted for processing 40 | * @return the JWT object 41 | */ 42 | Jwt buildJwt(Date issued, Date expiration); 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/core/RS256Algorithm.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import foundation.icon.did.exceptions.AlgorithmException; 4 | 5 | import java.security.*; 6 | 7 | import static foundation.icon.did.core.AlgorithmProvider.PROVIDER; 8 | import static foundation.icon.did.core.AlgorithmProvider.secureRandom; 9 | 10 | public class RS256Algorithm implements Algorithm { 11 | 12 | private AlgorithmProvider.Type type = AlgorithmProvider.Type.RS256; 13 | 14 | RS256Algorithm() { 15 | } 16 | 17 | @Override 18 | public AlgorithmProvider.Type getType() { 19 | return type; 20 | } 21 | 22 | @Override 23 | public KeyPair generateKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { 24 | KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", PROVIDER); 25 | gen.initialize(2048, secureRandom()); 26 | return gen.generateKeyPair(); 27 | } 28 | 29 | @Override 30 | public byte[] sign(PrivateKey privateKey, byte[] data) throws AlgorithmException { 31 | return signWithSignature(type.getSigAlgorithm(), privateKey, data); 32 | } 33 | 34 | @Override 35 | public boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws AlgorithmException { 36 | return verifyWithSignature(type.getSigAlgorithm(), publicKey, data, signature); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/document/AuthenticationProperty.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.document; 2 | 3 | /** 4 | * This corresponds to the authentication property of the DIDs specification. 5 | * https://w3c-ccg.github.io/did-spec/#authentication 6 | */ 7 | public class AuthenticationProperty { 8 | String type; 9 | String publicKey; 10 | 11 | private AuthenticationProperty(Builder builder) { 12 | type = builder.type; 13 | publicKey = builder.publicKey; 14 | } 15 | 16 | public String getType() { 17 | return type; 18 | } 19 | 20 | public String getPublicKey() { 21 | return publicKey; 22 | } 23 | 24 | public static final class Builder { 25 | private String type; 26 | private String publicKey; 27 | 28 | public Builder() { 29 | } 30 | 31 | public Builder type(String val) { 32 | type = val; 33 | return this; 34 | } 35 | 36 | public Builder publicKey(String val) { 37 | publicKey = val; 38 | return this; 39 | } 40 | 41 | public AuthenticationProperty build() { 42 | return new AuthenticationProperty(this); 43 | } 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "AuthenticationProperty{" + 49 | "type=" + type + 50 | ", publicKey='" + publicKey + '\'' + 51 | '}'; 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/IconServiceFactory.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | import foundation.icon.icx.IconService; 4 | import foundation.icon.icx.transport.http.HttpProvider; 5 | import okhttp3.OkHttpClient; 6 | import okhttp3.logging.HttpLoggingInterceptor; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * Test 용으로 사용 12 | */ 13 | public class IconServiceFactory { 14 | 15 | private static boolean DEBUG = false; 16 | private static String LOCAL_URL = "http://127.0.0.1:9000/api/v3"; 17 | private static String DEV_URL = "https://test-ctz.solidwallet.io/api/v3"; 18 | 19 | private IconServiceFactory() { 20 | } 21 | 22 | public static IconService createLocal() { 23 | return create(LOCAL_URL); 24 | } 25 | 26 | public static IconService createDev() { 27 | return create(DEV_URL); 28 | } 29 | 30 | public static IconService create(String url) { 31 | return new IconService(createHttpProvider(url, DEBUG)); 32 | } 33 | 34 | private static HttpProvider createHttpProvider(String url, boolean debug) { 35 | if (!debug) { 36 | return new HttpProvider(url); 37 | } else { 38 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); 39 | logging.setLevel(HttpLoggingInterceptor.Level.BODY); 40 | OkHttpClient httpClient = new OkHttpClient.Builder() 41 | .addInterceptor(logging) 42 | .readTimeout(100L, TimeUnit.SECONDS) 43 | .build(); 44 | return new HttpProvider(httpClient, url); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/jwt/Header.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.jwt; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | 5 | import java.util.Objects; 6 | 7 | @JsonInclude(JsonInclude.Include.NON_NULL) 8 | public class Header { 9 | // header 10 | private String alg; 11 | private String kid; 12 | 13 | private Header(Builder builder) { 14 | alg = builder.alg; 15 | kid = builder.kid; 16 | } 17 | 18 | public String getAlg() { 19 | return alg; 20 | } 21 | 22 | public String getKid() { 23 | return kid; 24 | } 25 | 26 | 27 | 28 | @Override 29 | public String toString() { 30 | return "Header{" + 31 | "alg='" + alg + '\'' + 32 | ", kid='" + kid + '\'' + 33 | '}'; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object o) { 38 | if (this == o) return true; 39 | if (!(o instanceof Header)) return false; 40 | Header header = (Header) o; 41 | return Objects.equals(alg, header.alg) && 42 | Objects.equals(kid, header.kid); 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return Objects.hash(alg, kid); 48 | } 49 | 50 | public static final class Builder { 51 | private String alg; 52 | private String kid; 53 | 54 | public Builder() { 55 | } 56 | 57 | public Builder alg(String val) { 58 | alg = val; 59 | return this; 60 | } 61 | 62 | public Builder kid(String val) { 63 | kid = val; 64 | return this; 65 | } 66 | 67 | public Header build() { 68 | return new Header(this); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/config/SampleConfig.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.config; 2 | 3 | import foundation.icon.icx.IconService; 4 | import foundation.icon.icx.data.Address; 5 | import foundation.icon.icx.transport.http.HttpProvider; 6 | import okhttp3.OkHttpClient; 7 | import okhttp3.logging.HttpLoggingInterceptor; 8 | 9 | import java.math.BigInteger; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | public interface SampleConfig { 13 | 14 | String OWNER_KEY_FILE_NAME = "owner.json"; 15 | String ISSUER_KEY_FILE_NAME = "issuer.json"; 16 | String CREDENTIAL_FILE_NAME = "credential.json"; 17 | String PRESENTATION_FILE_NAME = "presentation.json"; 18 | 19 | String TEST_PASSWORD = "P@ssw0rd"; 20 | 21 | boolean DEBUG = false; 22 | 23 | String getNodeUrl(); 24 | 25 | BigInteger getNetworkId(); 26 | 27 | Address getScoreAddress(); 28 | 29 | default IconService iconService() { 30 | return new IconService(createHttpProvider(getNodeUrl())); 31 | } 32 | 33 | default HttpProvider createHttpProvider(String url) { 34 | if (!DEBUG) { 35 | return new HttpProvider(url); 36 | } else { 37 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); 38 | logging.setLevel(HttpLoggingInterceptor.Level.BODY); 39 | OkHttpClient httpClient = new OkHttpClient.Builder() 40 | .addInterceptor(logging) 41 | .readTimeout(100L, TimeUnit.SECONDS) 42 | .build(); 43 | return new HttpProvider(httpClient, url); 44 | } 45 | } 46 | 47 | static SampleConfig dev() { 48 | return new DevConfig(); 49 | } 50 | 51 | static SampleConfig local() { 52 | return new LocalConfig(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/test/resources/rsa-private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4ZtdaIrd1BPIJ 3 | tfnF0TjIK5inQAXZ3XlCrUlJdP+XHwIRxdv1FsN12XyMYO/6ymLmo9ryoQeIrsXB 4 | XYqlET3zfAY+diwCb0HEsVvhisthwMU4gZQu6TYW2s9LnXZB5rVtcBK69hcSlA2k 5 | ZudMZWxZcj0L7KMfO2rIvaHw/qaVOE9j0T257Z8Kp2CLF9MUgX0ObhIsdumFRLaL 6 | DvDUmBPr2zuh/34j2XmWwn1yjN/WvGtdfhXW79Ki1S40HcWnygHgLV8sESFKUxxQ 7 | mKvPUTwDOIwLFL5WtE8Mz7N++kgmDcmWMCHc8kcOIu73Ta/3D4imW7VbKgHZo9+K 8 | 3ESFE3RjAgMBAAECggEBAJTEIyjMqUT24G2FKiS1TiHvShBkTlQdoR5xvpZMlYbN 9 | tVWxUmrAGqCQ/TIjYnfpnzCDMLhdwT48Ab6mQJw69MfiXwc1PvwX1e9hRscGul36 10 | ryGPKIVQEBsQG/zc4/L2tZe8ut+qeaK7XuYrPp8bk/X1e9qK5m7j+JpKosNSLgJj 11 | NIbYsBkG2Mlq671irKYj2hVZeaBQmWmZxK4fw0Istz2WfN5nUKUeJhTwpR+JLUg4 12 | ELYYoB7EO0Cej9UBG30hbgu4RyXA+VbptJ+H042K5QJROUbtnLWuuWosZ5ATldwO 13 | u03dIXL0SH0ao5NcWBzxU4F2sBXZRGP2x/jiSLHcqoECgYEA4qD7mXQpu1b8XO8U 14 | 6abpKloJCatSAHzjgdR2eRDRx5PMvloipfwqA77pnbjTUFajqWQgOXsDTCjcdQui 15 | wf5XAaWu+TeAVTytLQbSiTsBhrnoqVrr3RoyDQmdnwHT8aCMouOgcC5thP9vQ8Us 16 | rVdjvRRbnJpg3BeSNimH+u9AHgsCgYEA0EzcbOltCWPHRAY7B3Ge/AKBjBQr86Kv 17 | TdpTlxePBDVIlH+BM6oct2gaSZZoHbqPjbq5v7yf0fKVcXE4bSVgqfDJ/sZQu9Lp 18 | PTeV7wkk0OsAMKk7QukEpPno5q6tOTNnFecpUhVLLlqbfqkB2baYYwLJR3IRzboJ 19 | FQbLY93E8gkCgYB+zlC5VlQbbNqcLXJoImqItgQkkuW5PCgYdwcrSov2ve5r/Acz 20 | FNt1aRdSlx4176R3nXyibQA1Vw+ztiUFowiP9WLoM3PtPZwwe4bGHmwGNHPIfwVG 21 | m+exf9XgKKespYbLhc45tuC08DATnXoYK7O1EnUINSFJRS8cezSI5eHcbQKBgQDC 22 | PgqHXZ2aVftqCc1eAaxaIRQhRmY+CgUjumaczRFGwVFveP9I6Gdi+Kca3DE3F9Pq 23 | PKgejo0SwP5vDT+rOGHN14bmGJUMsX9i4MTmZUZ5s8s3lXh3ysfT+GAhTd6nKrIE 24 | kM3Nh6HWFhROptfc6BNusRh1kX/cspDplK5x8EpJ0QKBgQDWFg6S2je0KtbV5PYe 25 | RultUEe2C0jYMDQx+JYxbPmtcopvZQrFEur3WKVuLy5UAy7EBvwMnZwIG7OOohJb 26 | vkSpADK6VPn9lbqq7O8cTedEHttm6otmLt8ZyEl3hZMaL3hbuRj6ysjmoFKx6CrX 27 | rK0/Ikt5ybqUzKCMJZg2VKGTxg== 28 | -----END PRIVATE KEY----- -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/core/PropertyName.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | public class PropertyName { 4 | 5 | private PropertyName() { 6 | } 7 | 8 | public static final String ALGO_KEY_RSA = "RS256"; 9 | public static final String ALGO_KEY_ECDSA = "ES256"; 10 | public static final String ALGO_KEY_ECDSAK = "ES256K"; 11 | 12 | public static final String EC_CURVE_PARAM_SECP256R1 = "secp256r1"; 13 | public static final String EC_CURVE_PARAM_SECP256K1 = "secp256k1"; 14 | 15 | public static final String KEY_DOCUMENT_CONTEXT = "@context"; 16 | public static final String KEY_VERSION = "version"; 17 | public static final String KEY_DOCUMENT_ID = "id"; 18 | public static final String KEY_DOCUMENT_CREATED = "created"; 19 | public static final String KEY_DOCUMENT_UPDATED = "updated"; 20 | public static final String KEY_DOCUMENT_PUBLICKEY = "publicKey"; 21 | public static final String KEY_DOCUMENT_PUBLICKEY_ID = "id"; 22 | public static final String KEY_DOCUMENT_PUBLICKEY_TYPE = "type"; 23 | public static final String KEY_DOCUMENT_PUBLICKEY_HEX = "publicKeyHex"; 24 | public static final String KEY_DOCUMENT_PUBLICKEY_BASE64 = "publicKeyBase64"; 25 | public static final String KEY_DOCUMENT_PUBLICKEY_CREATED = "created"; 26 | public static final String KEY_DOCUMENT_PUBLICKEY_REVOKED = "revoked"; 27 | public static final String KEY_DOCUMENT_AUTHENTICATION = "authentication"; 28 | public static final String KEY_DOCUMENT_AUTHENTICATION_PUBLICKEY = "publicKey"; 29 | public static final String KEY_DOCUMENT_AUTHENTICATION_TYPE = "type"; 30 | 31 | public static final String VALUE_DOCUMENT_CONTEXT = "https://w3id.org/did/v1"; 32 | 33 | // Update Transaction 34 | public static final String KEY_TX_UPDATE_METHOD = "method"; 35 | public static final String KEY_TX_UPDATE_METHOD_ADDKEY = "addKey"; 36 | public static final String KEY_TX_UPDATE_METHOD_REVOKEKEY = "revokeKey"; 37 | public static final String KEY_TX_UPDATE_PARAM = "param"; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/api/ClaimIssueApi.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.api; 2 | 3 | import com.google.gson.Gson; 4 | import retrofit2.Call; 5 | import retrofit2.Response; 6 | import retrofit2.Retrofit; 7 | import retrofit2.http.Body; 8 | import retrofit2.http.POST; 9 | 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public class ClaimIssueApi { 15 | 16 | private Api api; 17 | 18 | public ClaimIssueApi(Retrofit retrofit) { 19 | this.api = retrofit.create(Api.class); 20 | } 21 | 22 | public AuthResponse checkOwner(String authType, String value, String didToken) throws IOException { 23 | Map params = new HashMap<>(); 24 | params.put("type", authType); 25 | params.put("value", value); 26 | params.put("token", didToken); 27 | Response response = api.checkOwner(params).execute(); 28 | if (response.isSuccessful()) 29 | return response.body(); 30 | else { 31 | return new Gson().fromJson(response.errorBody().charStream(), AuthResponse.class); 32 | } 33 | } 34 | 35 | public CredentialResponse sendCredentialClaim(String authNum, String didToken) throws IOException { 36 | Map params = new HashMap<>(); 37 | params.put("authNum", authNum); 38 | params.put("authToken", didToken); 39 | Response response = api.sendCredentialClaim(params).execute(); 40 | if (response.isSuccessful()) 41 | return response.body(); 42 | else { 43 | return new Gson().fromJson(response.errorBody().charStream(), CredentialResponse.class); 44 | } 45 | } 46 | 47 | interface Api { 48 | @POST("/v1/issuer/checkOwner") 49 | Call checkOwner(@Body Map params); 50 | 51 | @POST("/v1/issuer/sendCredentialClaim") 52 | Call sendCredentialClaim(@Body Map params); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/core/NoneAlgorithm.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import foundation.icon.did.exceptions.AlgorithmException; 4 | import foundation.icon.did.exceptions.KeyPairException; 5 | 6 | import java.security.*; 7 | 8 | public class NoneAlgorithm implements Algorithm { 9 | 10 | private AlgorithmProvider.Type type = AlgorithmProvider.Type.NONE; 11 | 12 | @Override 13 | public AlgorithmProvider.Type getType() { 14 | return type; 15 | } 16 | 17 | @Override 18 | public KeyProvider generateKeyProvider(String keyId) throws AlgorithmException { 19 | return null; 20 | } 21 | 22 | @Override 23 | public KeyPair generateKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { 24 | return null; 25 | } 26 | 27 | @Override 28 | public byte[] signWithSignature(String algorithm, PrivateKey privateKey, byte[] data) throws AlgorithmException { 29 | return new byte[0]; 30 | } 31 | 32 | @Override 33 | public boolean verifyWithSignature(String algorithm, PublicKey publicKey, byte[] data, byte[] signature) throws AlgorithmException { 34 | return true; 35 | } 36 | 37 | @Override 38 | public byte[] publicKeyToByte(PublicKey publicKey) { 39 | return new byte[0]; 40 | } 41 | 42 | @Override 43 | public byte[] privateKeyToByte(PrivateKey privateKey) { 44 | return new byte[0]; 45 | } 46 | 47 | @Override 48 | public PublicKey byteToPublicKey(byte[] b) throws KeyPairException { 49 | return null; 50 | } 51 | 52 | @Override 53 | public PrivateKey byteToPrivateKey(byte[] b) throws KeyPairException { 54 | return null; 55 | } 56 | 57 | @Override 58 | public byte[] sign(PrivateKey privateKey, byte[] data) throws AlgorithmException { 59 | return new byte[0]; 60 | } 61 | 62 | @Override 63 | public boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws AlgorithmException { 64 | return true; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/KeyPoviderSample.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | 4 | import foundation.icon.did.core.Algorithm; 5 | import foundation.icon.did.core.AlgorithmProvider; 6 | import foundation.icon.did.core.KeyProvider; 7 | import org.bouncycastle.util.encoders.Hex; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | class KeyPoviderSample { 12 | 13 | private static Logger logger = LoggerFactory.getLogger(KeyPoviderSample.class); 14 | 15 | 16 | /** 17 | * Create a new KeyProvider using Secp256k algorithm. 18 | */ 19 | private KeyProvider generateES256K() throws Exception { 20 | String keyId = "new-ES256Key"; 21 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256K); 22 | return algorithm.generateKeyProvider(keyId); 23 | } 24 | 25 | public static void main(String[] args) throws Exception { 26 | 27 | KeyPoviderSample sample = new KeyPoviderSample(); 28 | 29 | // Create a new KeyProvider 30 | KeyProvider keyProvider = sample.generateES256K(); 31 | logger.debug("new ES256K KeyProvider : {}", keyProvider); 32 | 33 | // Create the algorithm object 34 | Algorithm algorithm = AlgorithmProvider.create(keyProvider.getType()); 35 | // Important information that user need to have 36 | String keyId = keyProvider.getKeyId(); 37 | String type = keyProvider.getType().getName(); 38 | String privateKey = Hex.toHexString(algorithm.privateKeyToByte(keyProvider.getPrivateKey())); 39 | String publicKey = Hex.toHexString(algorithm.publicKeyToByte(keyProvider.getPublicKey())); 40 | 41 | // Create a KeyProvider from each String 42 | algorithm = AlgorithmProvider.create(keyProvider.getType()); 43 | KeyProvider loadKeyProvider = new KeyProvider.Builder() 44 | .keyId(keyId) 45 | .publicKey(algorithm.byteToPublicKey(Hex.decode(publicKey))) 46 | .privateKey(algorithm.byteToPrivateKey(Hex.decode(privateKey))) 47 | .type(AlgorithmProvider.Type.fromName(type)) 48 | .build(); 49 | 50 | logger.debug("loadDidKeyHolder == didKeyHolder : {}", keyProvider.equals(loadKeyProvider)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/CredentialTest.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | import foundation.icon.did.core.Algorithm; 4 | import foundation.icon.did.core.AlgorithmProvider; 5 | import foundation.icon.did.document.EncodeType; 6 | import foundation.icon.did.jwt.Jwt; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.api.Test; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.security.PrivateKey; 13 | import java.util.Arrays; 14 | import java.util.stream.Collectors; 15 | 16 | public class CredentialTest { 17 | 18 | private static Logger logger = LoggerFactory.getLogger(CredentialTest.class); 19 | 20 | @Test 21 | void testBuildJwt() throws Exception { 22 | 23 | String keyId = TestKeys.ISSUER_KEY_ID; 24 | AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256; 25 | Algorithm algorithm = AlgorithmProvider.create(type); 26 | PrivateKey priKey = algorithm.byteToPrivateKey(EncodeType.HEX.decode(TestKeys.PRIVATE_KEY_ES256)); 27 | 28 | String did = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 29 | Credential credential = new Credential.Builder() 30 | .algorithm(type.getName()) 31 | .keyId(keyId) 32 | .did(did) 33 | .build(); 34 | 35 | Assertions.assertEquals(did, credential.getIssuerDid().getDid()); 36 | 37 | String owner = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 38 | 39 | credential.setTargetDid(owner); 40 | credential.addClaim("email", "aaa@icon.foundation"); 41 | String jwt = credential.buildJwt().sign(priKey); 42 | logger.debug("jwt : {}", jwt); 43 | } 44 | 45 | @Test 46 | void testDecode() throws Exception { 47 | AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256; 48 | Algorithm algorithm = AlgorithmProvider.create(type); 49 | PrivateKey priKey = algorithm.byteToPrivateKey(EncodeType.HEX.decode(TestKeys.PRIVATE_KEY_ES256)); 50 | String encodedJwt = "eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDppY29uOjAwMDA5NjFiNmNkNjQyNTNmYjI4YzliMGQzZDIyNGJlNWY5YjE4ZDQ5ZjAxZGEzOTBmMDgjaXNzdWVyIn0=.eyJjbGFpbSI6eyJFbWFpbCI6ImFhYUBpY29uLmZvdW5kYXRpb24ifSwiZXhwIjoxNTQ3ODIwMzg2LCJpYXQiOjE1NDc3MzM5ODYsImlzcyI6ImRpZDppY29uOjAwMDA5NjFiNmNkNjQyNTNmYjI4YzliMGQzZDIyNGJlNWY5YjE4ZDQ5ZjAxZGEzOTBmMDgiLCJub25jZSI6IjAzMDcxZjJkLThiZTAtNDAxNS04Y2I4LTg4YTI3M2FiZWY2NiIsInN1YiI6ImRpZDppY29uOjAwMDA5NjFiNmNkNjQyNTNmYjI4YzliMGQzZDIyNGJlNWY5YjE4ZDQ5ZjAxZGEzOTBmMDgiLCJ0eXBlIjpbIkNSRURFTlRJQUwiLCJFbWFpbCJdfQ=="; 51 | Jwt decode = Jwt.decode(encodedJwt); 52 | Credential credential = Credential.valueOf(decode); 53 | String jwt = credential.buildJwt(decode.getPayload().getIat(), decode.getPayload().getExp()).sign(priKey); 54 | String actualJwt = String.join(".", Arrays.asList(jwt.split("\\.")).stream().limit(2).collect(Collectors.toList())); 55 | Assertions.assertEquals(encodedJwt, actualJwt); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/core/KeystoreTest.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import foundation.icon.did.exceptions.AlgorithmException; 4 | import foundation.icon.did.exceptions.KeyPairException; 5 | import foundation.icon.did.exceptions.KeystoreException; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | public class KeystoreTest { 13 | 14 | private final String DID = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 15 | private final String FILE_NAME = DID + ".json"; 16 | private final String PASSWORD = "ssw0rd"; 17 | 18 | @Test 19 | void testES256() throws AlgorithmException, IOException, KeystoreException, KeyPairException { 20 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256); 21 | KeyProvider keyProvider = algorithm.generateKeyProvider("key1"); 22 | DidKeyHolder didKeyHolder = new DidKeyHolder.Builder(keyProvider) 23 | .did(DID) 24 | .build(); 25 | 26 | Keystore.storeDidKeyHolder(PASSWORD, didKeyHolder, FILE_NAME); 27 | DidKeyHolder load = Keystore.loadDidKeyHolder(PASSWORD, new File(FILE_NAME)); 28 | assertEquals(didKeyHolder, load); 29 | } 30 | 31 | @Test 32 | void testRS256() throws AlgorithmException, IOException, KeystoreException, KeyPairException { 33 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.RS256); 34 | KeyProvider keyProvider = algorithm.generateKeyProvider("key1"); 35 | DidKeyHolder didKeyHolder = new DidKeyHolder.Builder(keyProvider) 36 | .did(DID) 37 | .build(); 38 | Keystore.storeDidKeyHolder(PASSWORD, didKeyHolder, FILE_NAME); 39 | DidKeyHolder load = Keystore.loadDidKeyHolder(PASSWORD, new File(FILE_NAME)); 40 | assertEquals(didKeyHolder, load); 41 | } 42 | 43 | @Test 44 | void testES256K() throws AlgorithmException, IOException, KeystoreException, KeyPairException { 45 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256K); 46 | KeyProvider keyProvider = algorithm.generateKeyProvider("key1"); 47 | DidKeyHolder didKeyHolder = new DidKeyHolder.Builder(keyProvider) 48 | .did(DID) 49 | .build(); 50 | Keystore.storeDidKeyHolder(PASSWORD, didKeyHolder, FILE_NAME); 51 | DidKeyHolder load = Keystore.loadDidKeyHolder(PASSWORD, new File(FILE_NAME)); 52 | assertEquals(didKeyHolder, load); 53 | } 54 | 55 | private void assertEquals(DidKeyHolder expected, DidKeyHolder actual) { 56 | Assertions.assertEquals(DID, actual.getDid()); 57 | Assertions.assertEquals(expected.getKeyId(), actual.getKeyId()); 58 | Assertions.assertEquals(expected.getType(), actual.getType()); 59 | Algorithm algorithm = AlgorithmProvider.create(expected.getType()); 60 | Assertions.assertArrayEquals(algorithm.privateKeyToByte(expected.getPrivateKey()), 61 | algorithm.privateKeyToByte(actual.getPrivateKey())); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.8.3 (2019-5-15) 4 | - Remove CryptoUtils class 5 | - Move EncodeType class to upper level 6 | 7 | ## 0.8.2 (2019-5-14) 8 | - Change to use encoded token string instead of signature in Jwt class 9 | 10 | ## 0.8.1 (2019-5-3) 11 | - Move generateKeyPair method to Algorithm object 12 | 13 | ## 0.8.0 (2019-5-2) 14 | - Remove korean text in code 15 | - rename package : com.nomadconnection.icondid => foundation.icon.did 16 | - rename files 17 | old : DidOperationFactory, DidClaim, IClaim, DidRegistryScore 18 | new : ScoreParameter, IssuerDid, ConvertJwt, DidScore 19 | - Add DidKeyHolder class 20 | - Move sign method of ConvertJwt to DidKeyHolder 21 | 22 | ## 0.7.3 (2019-4-23) 23 | - DID Document create method 수정 : JWT format 에서 json object format 으로 변경 24 | - deprecated method 제거 25 | 26 | ## 0.7.2 (2019-3-26) 27 | - DID Document 에서 hash property 제거 (createTxHash, updateTxHash) 28 | - JWT Header 에서 type property 제거 29 | - credential, presentation 에 nonce property 추가 30 | - Credential, Presentation 의 type 에 default value 추가 ("credential", "presentation") 31 | - ClaimResponse class 제거 32 | - ClaimRequest 의 requestClaims method param type 변경 (List -> Map) 33 | - ClaimRequest 에 responseId 추가 34 | - ClaimRequest 에 unsigned jwt 추가 35 | - Document 의 publicKey type property 수정 (String -> List) 36 | - DID Document 의 addKey method 수정 37 | - Presentation 의 claims property 이름 변경 (claims -> credential) 38 | 39 | ## 0.6.1 (2019-3-21) 40 | - ClaimRequest 추가 : Issuer 에게 Credential 요청, Owner 에게 Presentation 요청 41 | - PublicKey 의 string encode type 은 BASE64 만 지원 (Hex 인 경우, score 에서 revert) 42 | - Credential 과 Presentation 의 payload 수정 43 | - type 이 list 타입으로 변경 44 | - type 변경으로 credential 의 claim 추가 방법 변경 (기존 Credential.setClaim 메소드 변경) 45 | - PublicKey 의 type 이름 변경 : Secp256k1VerificationKey 46 | - DID Document 의 created, updated, revoked 가 timestamp 에서 block height 으로 변경 47 | 48 | ## 0.5.3 (2019-3-7) 49 | - Change property in Authentication 50 | - Add missing update properties 51 | 52 | ## 0.5.2 (2019-2-27) 53 | - icon-sdk-java version 업데이트 54 | - ES256KAlgorithm sign/verify 할 때, DER format 인코딩/디코딩 제거 55 | - DidOperationFactory class 추가 (DID CRUD 요청 생성) 56 | 57 | ## 0.5.1 (2019-1-30) 58 | - public key revoke 체크 method 추가 59 | 60 | ## 0.5.0 (2019-1-29) 61 | - ES256K Algorithm 추가 62 | - 각 Algotihm 별 클래스 추가 (ES256Algorithm, RS256Algorithm) 63 | - Algorithm 클래스 AlgorithmProvider 로 변경 64 | - ecdsa_verify 추가한 score 연동 테스트 65 | 66 | ## 0.4.0 (2019-1-24) 67 | - `icon.did` 패키지 `core` 패키지로 변경 68 | - PublicKeyProperty class 에서 사용하던 KeyProvider 제거 69 | - KeyProvider 에 private key 추가 70 | - Algorithm 클래스 추가 71 | - JwtUtils 클래스 제거 72 | - Keystore 클래스 추가 (private key 와 did 저장) 73 | 74 | ## 0.3.0 (2019-1-22) 75 | 76 | - JWT 로 score 호출 메소드 추가 77 | - VerifyResult class 추가 78 | - Jwt.verify 할 때, expiration time 확인 추가 79 | - Jwt, JwtFactory `com.nomadconnection.icondid.jwt` 패키지 이동 80 | 81 | ## 0.2.0 (2019-1-15) 82 | 83 | - DidJwt 에서 Jwt 로 변경 84 | - DidKeyPair 에서 KeyProvider 로 변경 85 | - KeyProvider 에 PrivateKey 제거 86 | - 패키지 구조 변경 87 | - document 패키지 추가 88 | 89 | 90 | ## 0.1.1 (2018-12-24) 91 | 92 | - public key `add`, `revoke` 추가 [5a44e36c](https://gitlab.com/did-vault/did-sdk-java/commit/5a44e36cad57b5319ba4ef3f3ff1ad65d64c0de9) 93 | - New bicon-score : cxf847b8a788e2c978b57a8905fcdb7ed359b013a2 -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/score/DidScore.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.score; 2 | 3 | 4 | import foundation.icon.icx.Call; 5 | import foundation.icon.icx.IconService; 6 | import foundation.icon.icx.Transaction; 7 | import foundation.icon.icx.TransactionBuilder; 8 | import foundation.icon.icx.data.Address; 9 | import foundation.icon.icx.transport.jsonrpc.RpcItem; 10 | import foundation.icon.icx.transport.jsonrpc.RpcObject; 11 | import foundation.icon.icx.transport.jsonrpc.RpcValue; 12 | 13 | import java.io.IOException; 14 | import java.math.BigInteger; 15 | 16 | public class DidScore { 17 | 18 | private IconService iconService; 19 | private BigInteger networkId; 20 | private Address scoreAddress; 21 | 22 | public DidScore(IconService iconService, BigInteger networkId, Address scoreAddress) { 23 | this.iconService = iconService; 24 | this.networkId = networkId; 25 | this.scoreAddress = scoreAddress; 26 | } 27 | 28 | public Transaction jwtMethod(Address from, String jwt, String method) { 29 | RpcObject params = new RpcObject.Builder() 30 | .put("jwt", new RpcValue(jwt)) 31 | .build(); 32 | return buildTransaction(from, method, params); 33 | } 34 | 35 | public String getVersion() throws IOException { 36 | return iconService.call(buildCall(null, "getVersion", null, String.class)).execute(); 37 | } 38 | 39 | public Transaction create(Address from, String publicKey) { 40 | RpcObject params = new RpcObject.Builder() 41 | .put("publicKey", new RpcValue(publicKey)) 42 | .build(); 43 | return buildTransaction(from, "create", params); 44 | } 45 | 46 | public RpcItem getDid(Address from) throws IOException { 47 | return iconService.call(buildCall(from, "getDid", null, RpcItem.class)).execute(); 48 | } 49 | 50 | public RpcItem getDidDocument(String did) throws IOException { 51 | RpcObject params = new RpcObject.Builder() 52 | .put("did", new RpcValue(did)) 53 | .build(); 54 | return iconService.call(buildCall("read", params, RpcItem.class)).execute(); 55 | } 56 | 57 | private Transaction buildTransaction(Address from, String method, RpcObject params) { 58 | long timestamp = System.currentTimeMillis() * 1000L; 59 | return TransactionBuilder.newBuilder() 60 | .nid(networkId) 61 | .from(from) 62 | .to(scoreAddress) 63 | .stepLimit(new BigInteger("2000000")) 64 | .timestamp(new BigInteger(Long.toString(timestamp))) 65 | .call(method) 66 | .params(params) 67 | .build(); 68 | } 69 | 70 | private Call buildCall(String method, RpcObject params, Class responseType) { 71 | return new Call.Builder() 72 | .to(scoreAddress) 73 | .method(method) 74 | .params(params) 75 | .buildWith(responseType); 76 | } 77 | 78 | private Call buildCall(Address from, String method, RpcObject params, Class responseType) { 79 | return new Call.Builder() 80 | .from(from) 81 | .to(scoreAddress) 82 | .method(method) 83 | .params(params) 84 | .buildWith(responseType); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/document/Document.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.document; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | /** 7 | * This corresponds to the publicKeys property of the DIDs specification. 8 | * https://w3c-ccg.github.io/did-spec/#did-documents 9 | */ 10 | public class Document { 11 | private String version; 12 | private String id; 13 | private long created; 14 | private long updated; 15 | private Map publicKey; 16 | private List authentication; 17 | 18 | private Document(Builder builder) { 19 | version = builder.version; 20 | id = builder.id; 21 | created = builder.created; 22 | updated = builder.updated; 23 | publicKey = builder.publicKey; 24 | authentication = builder.authentication; 25 | } 26 | 27 | public String getVersion() { return version; } 28 | 29 | public String getId() { 30 | return id; 31 | } 32 | 33 | public long getCreated() { 34 | return created; 35 | } 36 | 37 | public long getUpdated() { 38 | return updated; 39 | } 40 | 41 | public Map getPublicKeyProperty() { 42 | return publicKey; 43 | } 44 | 45 | public PublicKeyProperty getPublicKeyProperty(String publicKeyId) { 46 | return publicKey.get(publicKeyId); 47 | } 48 | 49 | public List getAuthentication() { 50 | return authentication; 51 | } 52 | 53 | public String toJson() { 54 | return Converters.gson().toJson(this); 55 | } 56 | 57 | 58 | public static final class Builder { 59 | private String version; 60 | private String id; 61 | private long created; 62 | private long updated; 63 | private Map publicKey; 64 | private List authentication; 65 | 66 | public Builder() { 67 | } 68 | 69 | public Builder version(String val) { 70 | version = val; 71 | return this; 72 | } 73 | 74 | public Builder id(String val) { 75 | id = val; 76 | return this; 77 | } 78 | 79 | public Builder created(long val) { 80 | created = val; 81 | return this; 82 | } 83 | 84 | public Builder updated(long val) { 85 | updated = val; 86 | return this; 87 | } 88 | 89 | public Builder publicKey(Map val) { 90 | publicKey = val; 91 | return this; 92 | } 93 | 94 | public Builder authentication(List val) { 95 | authentication = val; 96 | return this; 97 | } 98 | 99 | public Document build() { 100 | return new Document(this); 101 | } 102 | } 103 | 104 | @Override 105 | public String toString() { 106 | return "Document{" + 107 | " id='" + id + '\'' + 108 | ", created=" + created + 109 | ", updated=" + updated + 110 | ", publicKey=" + publicKey + 111 | ", authentication=" + authentication + 112 | '}'; 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/core/AlgorithmTest.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import org.bouncycastle.jce.interfaces.ECPublicKey; 4 | import org.bouncycastle.util.encoders.Hex; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.RepeatedTest; 8 | import org.junit.jupiter.api.Test; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.nio.charset.StandardCharsets; 13 | 14 | public class AlgorithmTest { 15 | 16 | private final static Logger logger = LoggerFactory.getLogger(AlgorithmTest.class); 17 | 18 | byte[] message; 19 | 20 | @BeforeEach 21 | void setUp() { 22 | message = "eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDppY29uOjAwMDA5NjFiNmNkNjQyNTNmYjI4YzliMGQzZDIyNGJlNWY5YjE4ZDQ5ZjAxZGEzOTBmMDgjaG9sZGVyIn0=.eyJjcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJbVJwWkRwcFkyOXVPakF3TURBNU5qRmlObU5rTmpReU5UTm1Zakk0WXpsaU1HUXpaREl5TkdKbE5XWTVZakU0WkRRNVpqQXhaR0V6T1RCbU1EZ2phWE56ZFdWeUluMC5leUpqYkdGcGJTSTZleUpGYldGcGJDSTZJbUZoWVVCcFkyOXVMbVp2ZFc1a1lYUnBiMjRpZlN3aVpYaHdJam94TlRRM09ESXdNemcyTENKcFlYUWlPakUxTkRjM016TTVPRFlzSW1semN5STZJbVJwWkRwcFkyOXVPakF3TURBNU5qRmlObU5rTmpReU5UTm1Zakk0WXpsaU1HUXpaREl5TkdKbE5XWTVZakU0WkRRNVpqQXhaR0V6T1RCbU1EZ2lMQ0p1YjI1alpTSTZJakF6TURjeFpqSmtMVGhpWlRBdE5EQXhOUzA0WTJJNExUZzRZVEkzTTJGaVpXWTJOaUlzSW5OMVlpSTZJbVJwWkRwcFkyOXVPakF3TURBNU5qRmlObU5rTmpReU5UTm1Zakk0WXpsaU1HUXpaREl5TkdKbE5XWTVZakU0WkRRNVpqQXhaR0V6T1RCbU1EZ2lMQ0owZVhCbElqcGJJa1Z0WVdsc0lsMTkiXSwiZXhwIjoxNTQ1MjA5MTQyLCJpYXQiOjE1NDUyMDg4NDIsImlzcyI6ImRpZDppY29uOjAwMDA5NjFiNmNkNjQyNTNmYjI4YzliMGQzZDIyNGJlNWY5YjE4ZDQ5ZjAxZGEzOTBmMDgiLCJub25jZSI6IjZhNDFiOGE0LWQxOGQtNGUzMS1iNWY4LWYzOWIxNWRiNTgyNyIsInR5cGUiOlsiUFJFU0VOVEFUSU9OIiwiRW1haWwiXX0=".getBytes(StandardCharsets.UTF_8); 23 | } 24 | 25 | @RepeatedTest(100) 26 | void testES256K() throws Exception { 27 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256K); 28 | KeyProvider keyProvider = algorithm.generateKeyProvider("key1"); 29 | byte[] signature = algorithm.sign(keyProvider.getPrivateKey(), message); 30 | logger.debug("message:{}", Hex.toHexString(message)); 31 | logger.debug("signature:{}", Hex.toHexString(signature)); 32 | ECPublicKey ecPublicKey = (ECPublicKey) keyProvider.getPublicKey(); 33 | logger.debug("publicKey:{}", Hex.toHexString(ecPublicKey.getQ().getEncoded(false))); 34 | boolean verify = algorithm.verify(keyProvider.getPublicKey(), message, signature); 35 | Assertions.assertTrue(verify); 36 | } 37 | 38 | @Test 39 | void testRS256() throws Exception { 40 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.RS256); 41 | KeyProvider keyProvider = algorithm.generateKeyProvider("key1"); 42 | byte[] signature = algorithm.sign(keyProvider.getPrivateKey(), message); 43 | boolean verify = algorithm.verify(keyProvider.getPublicKey(), message, signature); 44 | Assertions.assertTrue(verify); 45 | } 46 | 47 | @RepeatedTest(100) 48 | void testES256() throws Exception { 49 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256); 50 | KeyProvider keyProvider = algorithm.generateKeyProvider("key1"); 51 | byte[] signature = algorithm.sign(keyProvider.getPrivateKey(), message); 52 | boolean verify = algorithm.verify(keyProvider.getPublicKey(), message, signature); 53 | Assertions.assertTrue(verify); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/core/ES256Algorithm.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import foundation.icon.did.exceptions.AlgorithmException; 4 | import foundation.icon.did.exceptions.JwtException; 5 | import org.bouncycastle.asn1.ASN1InputStream; 6 | import org.bouncycastle.asn1.ASN1Integer; 7 | import org.bouncycastle.asn1.DERSequenceGenerator; 8 | import org.bouncycastle.asn1.DLSequence; 9 | import org.bouncycastle.util.BigIntegers; 10 | 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | import java.math.BigInteger; 14 | import java.nio.ByteBuffer; 15 | import java.security.*; 16 | import java.security.spec.ECGenParameterSpec; 17 | 18 | import static foundation.icon.did.core.AlgorithmProvider.PROVIDER; 19 | import static foundation.icon.did.core.AlgorithmProvider.secureRandom; 20 | import static foundation.icon.did.core.PropertyName.EC_CURVE_PARAM_SECP256R1; 21 | 22 | public class ES256Algorithm implements Algorithm { 23 | 24 | public static final int EC_256_NUMBER = 32; 25 | private AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256; 26 | 27 | ES256Algorithm() { 28 | } 29 | 30 | @Override 31 | public AlgorithmProvider.Type getType() { 32 | return type; 33 | } 34 | 35 | @Override 36 | public KeyPair generateKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { 37 | KeyPairGenerator pairGen = KeyPairGenerator.getInstance("EC", PROVIDER); 38 | pairGen.initialize(new ECGenParameterSpec(EC_CURVE_PARAM_SECP256R1), secureRandom()); 39 | return pairGen.generateKeyPair(); 40 | } 41 | 42 | @Override 43 | public byte[] sign(PrivateKey privateKey, byte[] data) throws AlgorithmException { 44 | byte[] signature = signWithSignature(type.getSigAlgorithm(), privateKey, data); 45 | return ecDerDecode(signature, EC_256_NUMBER); 46 | } 47 | 48 | @Override 49 | public boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws AlgorithmException { 50 | try { 51 | signature = ecDerEncode(signature, EC_256_NUMBER); 52 | } catch (IOException e) { 53 | throw new JwtException(e.getMessage(), e.getCause()); 54 | } 55 | return verifyWithSignature(type.getSigAlgorithm(), publicKey, data, signature); 56 | } 57 | 58 | static byte[] ecDerDecode(byte[] signature, int ecNumberSize) { 59 | try (ASN1InputStream decoder = new ASN1InputStream(signature)) { 60 | DLSequence seq = (DLSequence) decoder.readObject(); 61 | ASN1Integer r = (ASN1Integer) seq.getObjectAt(0); 62 | ASN1Integer s = (ASN1Integer) seq.getObjectAt(1); 63 | 64 | ByteBuffer buffer = ByteBuffer.allocate(ecNumberSize * 2); 65 | buffer.put(BigIntegers.asUnsignedByteArray(ecNumberSize, r.getValue())); 66 | buffer.put(BigIntegers.asUnsignedByteArray(ecNumberSize, s.getValue())); 67 | return buffer.array(); 68 | } catch (IOException e) { 69 | throw new RuntimeException(e); 70 | } 71 | } 72 | 73 | static byte[] ecDerEncode(byte[] signature, int ecNumberSize) throws IOException { 74 | byte[] r = new byte[ecNumberSize]; 75 | byte[] s = new byte[ecNumberSize]; 76 | System.arraycopy(signature, 0, r, 0, ecNumberSize); 77 | System.arraycopy(signature, ecNumberSize, s, 0, ecNumberSize); 78 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 79 | DERSequenceGenerator seq = new DERSequenceGenerator(baos); 80 | seq.addObject(new ASN1Integer(new BigInteger(1, r))); 81 | seq.addObject(new ASN1Integer(new BigInteger(1, s))); 82 | seq.close(); 83 | return baos.toByteArray(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/document/PublicKeyProperty.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.document; 2 | 3 | import foundation.icon.did.core.Algorithm; 4 | import foundation.icon.did.core.AlgorithmProvider; 5 | 6 | import java.security.PublicKey; 7 | import java.util.List; 8 | 9 | /** 10 | * This corresponds to the publicKeys property of the DIDs specification. 11 | * https://w3c-ccg.github.io/did-spec/#public-keys 12 | */ 13 | public class PublicKeyProperty { 14 | 15 | private String id; 16 | private PublicKey publicKey; 17 | private List type; 18 | private EncodeType encodeType; 19 | private long created; 20 | private long revoked; 21 | 22 | private PublicKeyProperty(Builder builder) { 23 | id = builder.id; 24 | publicKey = builder.publicKey; 25 | type = builder.type; 26 | encodeType = builder.encodeType; 27 | created = builder.created; 28 | revoked = builder.revoked; 29 | } 30 | 31 | public String getId() { 32 | return id; 33 | } 34 | 35 | public List getType() { 36 | return type; 37 | } 38 | 39 | public AlgorithmProvider.Type getAlgorithmType() { 40 | return AlgorithmProvider.Type.fromIdentifier(type.get(0)); 41 | } 42 | 43 | public long getCreated() { 44 | return created; 45 | } 46 | 47 | public long getRevoked() { 48 | return revoked; 49 | } 50 | 51 | public PublicKey getPublicKey() { 52 | return publicKey; 53 | } 54 | 55 | public EncodeType getEncodeType() { 56 | return encodeType; 57 | } 58 | 59 | public boolean isRevoked() { 60 | return (revoked > 0); 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | Algorithm algorithm = AlgorithmProvider.create(getAlgorithmType()); 66 | String pub = encodeType.encode(algorithm.publicKeyToByte(publicKey)); 67 | return "PublicKeyProperty{" + 68 | "id='" + id + '\'' + 69 | ", publicKey=" + pub + 70 | ", type='" + type + '\'' + 71 | ", encodeType=" + encodeType + 72 | ", created=" + created + 73 | ", revoked=" + revoked + 74 | '}'; 75 | } 76 | 77 | 78 | public static final class Builder { 79 | private String id; 80 | private PublicKey publicKey; 81 | private List type; 82 | private EncodeType encodeType; 83 | private long created; 84 | private long revoked; 85 | 86 | public Builder() { 87 | } 88 | 89 | public Builder id(String val) { 90 | id = val; 91 | return this; 92 | } 93 | 94 | public Builder publicKey(PublicKey val) { 95 | publicKey = val; 96 | return this; 97 | } 98 | 99 | public Builder type(List val) { 100 | type = val; 101 | return this; 102 | } 103 | 104 | public Builder encodeType(EncodeType val) { 105 | encodeType = val; 106 | return this; 107 | } 108 | 109 | public Builder created(long val) { 110 | created = val; 111 | return this; 112 | } 113 | 114 | public Builder revoked(long val) { 115 | revoked = val; 116 | return this; 117 | } 118 | 119 | public PublicKeyProperty build() { 120 | if (id == null) throw new IllegalArgumentException("id cannot be null."); 121 | if (type == null) throw new IllegalArgumentException("type cannot be null."); 122 | if (publicKey == null) throw new IllegalArgumentException("publicKey cannot be null."); 123 | return new PublicKeyProperty(this); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/JJwtTest.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | 4 | import foundation.icon.did.core.Algorithm; 5 | import foundation.icon.did.core.AlgorithmProvider; 6 | import foundation.icon.did.jwt.IssuerDid; 7 | import io.jsonwebtoken.*; 8 | import io.jsonwebtoken.security.SignatureException; 9 | import org.bouncycastle.util.encoders.Hex; 10 | import org.junit.jupiter.api.Test; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.security.PrivateKey; 15 | import java.security.PublicKey; 16 | import java.util.Date; 17 | 18 | public class JJwtTest { 19 | 20 | private static Logger logger = LoggerFactory.getLogger(JJwtTest.class); 21 | 22 | @Test 23 | void testCreate() { 24 | String keyId = "key1"; 25 | String did = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 26 | String kid = did + "#" + keyId; 27 | 28 | long duration = 5 * 60; 29 | Date iat = new Date(); 30 | Date exp = new Date(iat.getTime() + duration * 1000L); 31 | String encodedJwt = Jwts.builder() 32 | .setHeaderParam("alg", "ES256") 33 | .setHeaderParam("kid", kid) 34 | .setIssuer(did) 35 | .setIssuedAt(iat) 36 | .setExpiration(exp) 37 | .compact(); 38 | 39 | logger.debug("encodedJwt: {}", encodedJwt); 40 | try { 41 | Jwt jwt = Jwts.parser() 42 | .parseClaimsJwt(encodedJwt); 43 | logger.debug("decode jwt: {}", jwt); 44 | 45 | } catch (ExpiredJwtException e) { 46 | e.printStackTrace(); 47 | } catch (MalformedJwtException e) { 48 | e.printStackTrace(); 49 | } catch (SignatureException e) { 50 | e.printStackTrace(); 51 | } catch (IllegalArgumentException e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | 56 | @Test 57 | void testExpiredJwt() throws Exception { 58 | String keyId = "key1"; 59 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256); 60 | PublicKey pubKey = algorithm.byteToPublicKey(Hex.decode("3059301306072a8648ce3d020106082a8648ce3d030107034200049548b1aedbb7b812ef2412c7922cde2f9b358939877410cce4a6777ec1b651f17b5a169be5d175d5f801069653a429ccf42c7e6ff9223ffee957dbf8ff5de917")); 61 | PrivateKey priKey = algorithm.byteToPrivateKey(Hex.decode("308193020100301306072a8648ce3d020106082a8648ce3d03010704793077020101042059e7f78342999fc71dc12e63e6747d0a1c9059d3cf1140a2af9a1b7eb4e3e0dda00a06082a8648ce3d030107a144034200049548b1aedbb7b812ef2412c7922cde2f9b358939877410cce4a6777ec1b651f17b5a169be5d175d5f801069653a429ccf42c7e6ff9223ffee957dbf8ff5de917")); 62 | 63 | String did = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 64 | IssuerDid issuerDid = new IssuerDid.Builder() 65 | .did(did) 66 | .algorithm(algorithm.getType().getName()) 67 | .keyId(keyId) 68 | .build(); 69 | 70 | 71 | // epoch timestamp 를 사용해야함 72 | Date issued = new Date(1547627598000L); 73 | Date expiration = new Date(1547627598000L); 74 | 75 | String encodedJwt = issuerDid.buildJwt(issued, expiration).sign(priKey); 76 | logger.debug("encodedJwt: {}", encodedJwt); 77 | 78 | try { 79 | io.jsonwebtoken.Jws jwtparser = Jwts.parser() 80 | .setSigningKey(pubKey) 81 | .parseClaimsJws(encodedJwt); 82 | logger.debug("jwt parser:{}", jwtparser); 83 | } catch (ExpiredJwtException e) { 84 | e.printStackTrace(); 85 | } catch (MalformedJwtException e) { 86 | e.printStackTrace(); 87 | } catch (SignatureException e) { 88 | e.printStackTrace(); 89 | } catch (IllegalArgumentException e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/score/ScoreParameter.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.score; 2 | 3 | import com.google.gson.JsonObject; 4 | import foundation.icon.did.core.DidKeyHolder; 5 | import foundation.icon.did.core.KeyProvider; 6 | import foundation.icon.did.core.PropertyName; 7 | import foundation.icon.did.document.Converters; 8 | import foundation.icon.did.document.EncodeType; 9 | import foundation.icon.did.document.PublicKeyProperty; 10 | import foundation.icon.did.jwt.Jwt; 11 | import foundation.icon.did.DidService; 12 | import foundation.icon.icx.KeyWallet; 13 | 14 | import java.util.Collections; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | /** 19 | * This class is used to create transaction parameters that call a score function that 20 | * can use all the features of the DID document. 21 | */ 22 | public class ScoreParameter { 23 | 24 | private ScoreParameter() { 25 | } 26 | 27 | /** 28 | * Create a parameter for transaction that creates the DID Document. 29 | * {@linkplain DidService#create(KeyWallet, String)} 30 | * 31 | * @param keyProvider the KeyProvider object 32 | * @return the json string 33 | */ 34 | public static String create(KeyProvider keyProvider, EncodeType encodeType) { 35 | PublicKeyProperty publicKeyProperty = createPublicKeyProperty(keyProvider, encodeType); 36 | return Converters.gson().toJson(publicKeyProperty); 37 | } 38 | 39 | /** 40 | * Create a parameter for transaction that update the DID Document. (add publicKey) 41 | * {@linkplain DidService#addPublicKey(KeyWallet, String)} 42 | * 43 | * @param didKeyHolder the DidKeyHolder object to use for authentication 44 | * @param keyProvider the KeyProvider object to add 45 | * @param encodeType the id of the publicKey to revoke 46 | * @return the Jwt object 47 | */ 48 | public static Jwt addKey(DidKeyHolder didKeyHolder, KeyProvider keyProvider, EncodeType encodeType) { 49 | PublicKeyProperty publicKeyProperty = createPublicKeyProperty(keyProvider, encodeType); 50 | JsonObject jsonObject = Converters.gson().toJsonTree(publicKeyProperty).getAsJsonObject(); 51 | 52 | Map map = new HashMap<>(); 53 | map.put("id", didKeyHolder.getDid()); 54 | map.put(PropertyName.KEY_DOCUMENT_PUBLICKEY, jsonObject); 55 | 56 | return new Jwt.Builder() 57 | .alg(didKeyHolder.getType().getName()) 58 | .kid(didKeyHolder.getKid()) 59 | .put(PropertyName.KEY_TX_UPDATE_METHOD, PropertyName.KEY_TX_UPDATE_METHOD_ADDKEY) 60 | .put(PropertyName.KEY_TX_UPDATE_PARAM, map) 61 | .build(); 62 | } 63 | 64 | /** 65 | * Create a parameter for transaction that update the DID Document. (revoke publicKey) 66 | * {@linkplain DidService#revokeKey(KeyWallet, String)} 67 | * 68 | * @param didKeyHolder the DidKeyHolder object to use for authentication 69 | * @param revokeKeyId revoke 할 public key 의 id 70 | * @return the jwt object 71 | */ 72 | public static Jwt revokeKey(DidKeyHolder didKeyHolder, String revokeKeyId) { 73 | Map param = new HashMap<>(); 74 | param.put(PropertyName.KEY_DOCUMENT_ID, didKeyHolder.getDid()); 75 | param.put(PropertyName.KEY_DOCUMENT_PUBLICKEY, revokeKeyId); 76 | return new Jwt.Builder() 77 | .alg(didKeyHolder.getType().getName()) 78 | .kid(didKeyHolder.getKid()) 79 | .put(PropertyName.KEY_TX_UPDATE_METHOD, PropertyName.KEY_TX_UPDATE_METHOD_REVOKEKEY) 80 | .put(PropertyName.KEY_TX_UPDATE_PARAM, param) 81 | .build(); 82 | } 83 | 84 | private static PublicKeyProperty createPublicKeyProperty(KeyProvider keyProvider, EncodeType encodeType) { 85 | return new PublicKeyProperty.Builder() 86 | .id(keyProvider.getKeyId()) 87 | .type(Collections.singletonList(keyProvider.getType().getIdentifier())) 88 | .publicKey(keyProvider.getPublicKey()) 89 | .encodeType(encodeType) 90 | .build(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/RepeatedTest.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | import com.fasterxml.jackson.databind.JavaType; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import foundation.icon.did.core.Algorithm; 6 | import foundation.icon.did.core.AlgorithmProvider; 7 | import foundation.icon.did.core.KeyProvider; 8 | import foundation.icon.did.document.Document; 9 | import foundation.icon.did.document.EncodeType; 10 | import foundation.icon.did.exceptions.AlgorithmException; 11 | import foundation.icon.did.score.ScoreParameter; 12 | import foundation.icon.icx.IconService; 13 | import foundation.icon.icx.KeyWallet; 14 | import foundation.icon.icx.data.Address; 15 | import foundation.icon.icx.data.Bytes; 16 | import org.junit.jupiter.api.Disabled; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | import java.math.BigInteger; 23 | import java.util.ArrayList; 24 | import java.util.Collections; 25 | import java.util.List; 26 | import java.util.Objects; 27 | import java.util.concurrent.Executors; 28 | import java.util.concurrent.ThreadPoolExecutor; 29 | import java.util.stream.Collectors; 30 | 31 | @Disabled 32 | public class RepeatedTest { 33 | 34 | private static Logger logger = LoggerFactory.getLogger(RepeatedTest.class); 35 | 36 | private static int count = 0; 37 | 38 | public static void main(String[] args) throws InterruptedException, IOException { 39 | BigInteger networkId = new BigInteger("3"); 40 | IconService iconService = IconServiceFactory.createLocal(); 41 | Address scoreAddress = new Address("cx26484cf9cb42b6eebbf537fbfe6b7df3f86c5079"); 42 | String keyId = "key"; 43 | 44 | AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256K; 45 | Algorithm algorithm = AlgorithmProvider.create(type); 46 | 47 | ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(5); 48 | List documents = Collections.synchronizedList(new ArrayList<>()); 49 | List wallets = loadMillionWalletJson(10); 50 | wallets.forEach(wallet -> { 51 | executorService.submit(() -> { 52 | try { 53 | KeyProvider keyProvider = algorithm.generateKeyProvider(keyId); 54 | String json = ScoreParameter.create(keyProvider, EncodeType.BASE64); 55 | DidService didService = new DidService(iconService, networkId, scoreAddress); 56 | Document doc = didService.create(wallet, json); 57 | documents.add(doc); 58 | System.out.println((count++) + " - " + doc.toJson()); 59 | } catch (AlgorithmException | IOException e) { 60 | e.printStackTrace(); 61 | } 62 | }); 63 | }); 64 | 65 | while (executorService.getActiveCount() > 0) { 66 | logger.debug("current active thread = " + executorService.getActiveCount()); 67 | Thread.sleep(1000); 68 | } 69 | 70 | executorService.shutdown(); 71 | 72 | logger.debug("count = " + count); 73 | logger.debug("expected count = " + wallets.size()); 74 | logger.debug("document count = " + documents.size()); 75 | } 76 | 77 | static List loadMillionWalletJson(int count) throws IOException { 78 | String path = Objects.requireNonNull(RepeatedTest.class.getClassLoader().getResource("investors.json")).getPath(); 79 | // Path path = Paths.get(System.getProperty("user.dir"), "icon-sdk/keystore", "investors.json"); 80 | File jsonFile = new File(path); 81 | 82 | ObjectMapper mapper = new ObjectMapper(); 83 | JavaType type = mapper.getTypeFactory(). 84 | constructCollectionType(List.class, String.class); 85 | List privateKeys = mapper.readValue(jsonFile, type); 86 | return privateKeys.stream() 87 | .limit(count) 88 | .map(privateKey -> { 89 | System.out.print("."); 90 | return KeyWallet.load(new Bytes(privateKey)); 91 | }) 92 | .collect(Collectors.toList()); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/core/KeyProvider.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | 5 | import java.security.PrivateKey; 6 | import java.security.PublicKey; 7 | import java.util.Arrays; 8 | import java.util.Objects; 9 | 10 | /** 11 | * Generic Public/Private Key provider. 12 | * {@linkplain Algorithm#generateKeyProvider(String)} 13 | */ 14 | public class KeyProvider { 15 | private String keyId; 16 | private AlgorithmProvider.Type type; 17 | private PublicKey publicKey; 18 | private PrivateKey privateKey; 19 | 20 | private KeyProvider(Builder builder) { 21 | keyId = builder.keyId; 22 | type = builder.type; 23 | publicKey = builder.publicKey; 24 | privateKey = builder.privateKey; 25 | } 26 | 27 | public String getKeyId() { 28 | return keyId; 29 | } 30 | 31 | public AlgorithmProvider.Type getType() { 32 | return type; 33 | } 34 | 35 | public PublicKey getPublicKey() { 36 | return publicKey; 37 | } 38 | 39 | public PrivateKey getPrivateKey() { 40 | return privateKey; 41 | } 42 | 43 | public Builder newBuilder() { 44 | return new Builder(this); 45 | } 46 | 47 | public static final class Builder { 48 | private String keyId; 49 | private AlgorithmProvider.Type type; 50 | private PublicKey publicKey; 51 | private PrivateKey privateKey; 52 | 53 | public Builder() { 54 | } 55 | 56 | public Builder(KeyProvider keyProvider) { 57 | keyId = keyProvider.keyId; 58 | type = keyProvider.type; 59 | publicKey = keyProvider.publicKey; 60 | privateKey = keyProvider.privateKey; 61 | } 62 | 63 | public Builder keyId(String val) { 64 | keyId = val; 65 | return this; 66 | } 67 | 68 | public Builder type(AlgorithmProvider.Type val) { 69 | type = val; 70 | return this; 71 | } 72 | 73 | public Builder publicKey(PublicKey val) { 74 | publicKey = val; 75 | return this; 76 | } 77 | 78 | public Builder privateKey(PrivateKey val) { 79 | privateKey = val; 80 | return this; 81 | } 82 | 83 | public KeyProvider build() { 84 | if (keyId == null) throw new IllegalArgumentException("keyId cannot be null."); 85 | if (type == null) throw new IllegalArgumentException("type cannot be null."); 86 | return new KeyProvider(this); 87 | } 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | Algorithm algorithm = AlgorithmProvider.create(type); 93 | String pub = (publicKey == null) ? "" : 94 | Hex.toHexString(algorithm.publicKeyToByte(publicKey)); 95 | String priv = (privateKey == null) ? "" : 96 | Hex.toHexString(algorithm.privateKeyToByte(privateKey)); 97 | return "KeyProvider{" + 98 | "keyId='" + keyId + '\'' + 99 | ", type=" + type + 100 | ", publicKey=" + pub + 101 | ", privateKey=" + priv + 102 | '}'; 103 | } 104 | 105 | @Override 106 | public boolean equals(Object o) { 107 | if (this == o) return true; 108 | if (!(o instanceof KeyProvider)) return false; 109 | KeyProvider that = (KeyProvider) o; 110 | 111 | Algorithm algorithm = AlgorithmProvider.create(type); 112 | 113 | return Objects.equals(keyId, that.keyId) && 114 | type == that.type && 115 | Arrays.equals(algorithm.publicKeyToByte(publicKey), 116 | algorithm.publicKeyToByte(that.publicKey)) && 117 | Arrays.equals(algorithm.privateKeyToByte(privateKey), 118 | algorithm.privateKeyToByte(that.privateKey)); 119 | } 120 | 121 | @Override 122 | public int hashCode() { 123 | Algorithm algorithm = AlgorithmProvider.create(type); 124 | return Objects.hash(keyId, type.name(), 125 | algorithm.publicKeyToByte(publicKey), 126 | algorithm.privateKeyToByte(privateKey)); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/jwt/IssuerDid.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.jwt; 2 | 3 | 4 | import foundation.icon.did.core.DidKeyHolder; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * This class holds DID-related information of the issuer in JWT. 10 | *

11 | * We can get this information from the JWT header. 12 | */ 13 | public class IssuerDid implements ConvertJwt { 14 | 15 | private final static int EXP_DURATION = 5 * 60; // seconds 16 | 17 | private String did; 18 | private String algorithm; 19 | private String keyId; 20 | 21 | IssuerDid(String did, String algorithm, String keyId) { 22 | this.did = did; 23 | this.algorithm = algorithm; 24 | this.keyId = keyId; 25 | } 26 | 27 | @Override 28 | public int getDuration() { 29 | return EXP_DURATION; 30 | } 31 | 32 | /** 33 | * Create a new JWT 34 | * 35 | * @param issued 토큰 발급 시간 36 | * @param expiration 토큰 만료 시간 37 | * @return a Jwt 38 | */ 39 | @Override 40 | public Jwt buildJwt(Date issued, Date expiration) { 41 | String kid = did + "#" + keyId; 42 | return new Jwt.Builder() 43 | .alg(algorithm) 44 | .kid(kid) 45 | .iss(did) 46 | .iat(issued) 47 | .exp(expiration) 48 | .build(); 49 | } 50 | 51 | /** 52 | * Returns the IssuerDid object representation of the String argument. 53 | * 54 | * @param encodedJwt the String returned by calling {@linkplain DidKeyHolder#sign(Jwt)} 55 | * @return the IssuerDid object 56 | */ 57 | public static IssuerDid valueOf(String encodedJwt) { 58 | return valueOf(Jwt.decode(encodedJwt)); 59 | } 60 | 61 | /** 62 | * Returns the IssuerDid object representation of the Jwt argument. 63 | * 64 | * @param jwt the JWT with properties of the Presentation object 65 | * @return the IssuerDid object 66 | * @see IssuerDid#buildJwt(Date, Date) 67 | */ 68 | public static IssuerDid valueOf(Jwt jwt) { 69 | Header header = jwt.getHeader(); 70 | String[] kid = header.getKid().split("#"); 71 | 72 | return new Builder() 73 | .did(kid[0]) 74 | .algorithm(header.getAlg()) 75 | .keyId(kid[1]) 76 | .build(); 77 | } 78 | 79 | public static IssuerDid valueOf(DidKeyHolder didKeyHolder) { 80 | return new Builder() 81 | .did(didKeyHolder.getDid()) 82 | .algorithm(didKeyHolder.getType().getName()) 83 | .keyId(didKeyHolder.getKeyId()) 84 | .build(); 85 | } 86 | 87 | public String getDid() { 88 | return did; 89 | } 90 | 91 | public String getAlgorithm() { 92 | return algorithm; 93 | } 94 | 95 | public String getKeyId() { 96 | return keyId; 97 | } 98 | 99 | 100 | public static final class Builder { 101 | private String did; 102 | private String algorithm; 103 | private String keyId; 104 | 105 | public Builder() { 106 | } 107 | 108 | public Builder did(String did) { 109 | this.did = did; 110 | return this; 111 | } 112 | 113 | public Builder algorithm(String algorithm) { 114 | this.algorithm = algorithm; 115 | return this; 116 | } 117 | 118 | public Builder keyId(String keyId) { 119 | this.keyId = keyId; 120 | return this; 121 | } 122 | 123 | public IssuerDid build() { 124 | checkArgument(did, "did not found"); 125 | checkArgument(algorithm, "algorithm not found"); 126 | checkArgument(keyId, "keyId not found"); 127 | 128 | return new IssuerDid(did, algorithm, keyId); 129 | } 130 | 131 | public static void checkArgument(T object, String message) { 132 | if (object == null) { 133 | throw new IllegalArgumentException(message); 134 | } 135 | } 136 | } 137 | 138 | @Override 139 | public String toString() { 140 | return "IssuerDid{" + 141 | "did='" + did + '\'' + 142 | ", algorithm='" + algorithm + '\'' + 143 | ", keyId='" + keyId + '\'' + 144 | '}'; 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/core/AlgorithmProvider.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import foundation.icon.icx.crypto.LinuxSecureRandom; 4 | import org.bouncycastle.crypto.RuntimeCryptoException; 5 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 6 | 7 | import java.security.Provider; 8 | import java.security.SecureRandom; 9 | import java.security.Security; 10 | 11 | /** 12 | * For test 13 | */ 14 | @SuppressWarnings("Duplicates") 15 | public class AlgorithmProvider { 16 | 17 | public static final String PROVIDER = "BC"; 18 | 19 | private static final SecureRandom SECURE_RANDOM; 20 | private static int isAndroid = -1; 21 | 22 | public static final double MIN_BOUNCY_CASTLE_VERSION = 1.54; 23 | 24 | static { 25 | 26 | if (isAndroidRuntime()) { 27 | new LinuxSecureRandom(); 28 | } 29 | 30 | SECURE_RANDOM = new SecureRandom(); 31 | 32 | Provider provider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); 33 | Provider newProvider = new BouncyCastleProvider(); 34 | 35 | if (newProvider.getVersion() < MIN_BOUNCY_CASTLE_VERSION) { 36 | String message = String.format( 37 | "The version of BouncyCastle should be %f or newer", MIN_BOUNCY_CASTLE_VERSION); 38 | throw new RuntimeCryptoException(message); 39 | } 40 | 41 | if (provider != null) { 42 | Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); 43 | } 44 | 45 | Security.addProvider(newProvider); 46 | 47 | } 48 | 49 | 50 | public static Algorithm create(Type type) { 51 | if (type == null) { 52 | throw new IllegalArgumentException("type cannot be null."); 53 | } 54 | switch (type) { 55 | case RS256: 56 | return new RS256Algorithm(); 57 | case ES256: 58 | return new ES256Algorithm(); 59 | case ES256K: 60 | return new ES256KAlgorithm(); 61 | case NONE: 62 | return new NoneAlgorithm(); 63 | default: 64 | throw new IllegalArgumentException("cannot create sigAlgorithm for " + type); 65 | } 66 | } 67 | 68 | public enum Type { 69 | RS256("RsaVerificationKey2018", "SHA256withRSA", "RSA"), 70 | ES256("Secp256r1VerificationKey", "SHA256withECDSA", "EC"), 71 | ES256K("Secp256k1VerificationKey", "SHA256withECDSA", "EC"), 72 | NONE("none", "none", "none"); 73 | 74 | private String identifier; 75 | private String sigAlgorithm; 76 | private String keyAlgorithm; 77 | 78 | Type(String identifier, String sigAlgorithm, String keyAlgorithm) { 79 | this.identifier = identifier; 80 | this.sigAlgorithm = sigAlgorithm; 81 | this.keyAlgorithm = keyAlgorithm; 82 | } 83 | 84 | public String getName() { 85 | return name(); 86 | } 87 | 88 | public String getIdentifier() { 89 | return identifier; 90 | } 91 | 92 | public String getSigAlgorithm() { 93 | return sigAlgorithm; 94 | } 95 | 96 | public String getKeyAlgorithm() { 97 | return keyAlgorithm; 98 | } 99 | 100 | public static Type fromName(String name) { 101 | if (name != null) { 102 | for (Type t : Type.values()) { 103 | if (name.equalsIgnoreCase(t.getName())) { 104 | return t; 105 | } 106 | } 107 | } 108 | return null; 109 | } 110 | 111 | public static Type fromIdentifier(String identifier) { 112 | if (identifier != null) { 113 | for (Type t : Type.values()) { 114 | if (identifier.equalsIgnoreCase(t.getIdentifier())) { 115 | return t; 116 | } 117 | } 118 | } 119 | return null; 120 | } 121 | } 122 | 123 | public static SecureRandom secureRandom() { 124 | return SECURE_RANDOM; 125 | } 126 | 127 | public static boolean isAndroidRuntime() { 128 | if (isAndroid == -1) { 129 | final String runtime = System.getProperty("java.runtime.name"); 130 | isAndroid = (runtime != null && runtime.equals("Android Runtime")) ? 1 : 0; 131 | } 132 | return isAndroid == 1; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/DIDBuildJwtTest.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | import foundation.icon.did.core.*; 4 | import foundation.icon.did.document.Converters; 5 | import foundation.icon.did.document.EncodeType; 6 | import foundation.icon.did.document.PublicKeyProperty; 7 | import foundation.icon.did.jwt.Jwt; 8 | import foundation.icon.did.score.ScoreParameter; 9 | import org.junit.jupiter.api.Assertions; 10 | import org.junit.jupiter.api.Test; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.security.PrivateKey; 15 | import java.security.PublicKey; 16 | 17 | import static foundation.icon.did.TestKeys.TEMP_DID; 18 | 19 | public class DIDBuildJwtTest { 20 | 21 | private static Logger logger = LoggerFactory.getLogger(DIDBuildJwtTest.class); 22 | 23 | @Test 24 | void testBuildCreateJwt() throws Exception { 25 | String keyId = "key1"; 26 | AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256; 27 | Algorithm algorithm = AlgorithmProvider.create(type); 28 | PublicKey publicKey = algorithm.byteToPublicKey(EncodeType.HEX.decode(TestKeys.PUBLIC_KEY_ES256)); 29 | PrivateKey privateKey = algorithm.byteToPrivateKey(EncodeType.HEX.decode(TestKeys.PRIVATE_KEY_ES256)); 30 | KeyProvider keyProvider = new KeyProvider.Builder() 31 | .keyId(keyId) 32 | .publicKey(publicKey) 33 | .privateKey(privateKey) 34 | .type(type) 35 | .build(); 36 | 37 | 38 | String json = ScoreParameter.create(keyProvider, EncodeType.HEX); 39 | logger.debug("json: {}", json); 40 | PublicKeyProperty publicKeyProperty = Converters.gson().fromJson(json, PublicKeyProperty.class); 41 | Assertions.assertEquals(keyId, publicKeyProperty.getId()); 42 | Assertions.assertEquals(type.getIdentifier(), publicKeyProperty.getType().get(0)); 43 | Assertions.assertArrayEquals(algorithm.publicKeyToByte(publicKey), algorithm.publicKeyToByte(publicKeyProperty.getPublicKey())); 44 | } 45 | 46 | @Test 47 | void testBuildAddKeyJwt() throws Exception { 48 | 49 | // authentication public / private key 50 | String authKeyId = "key1"; 51 | AlgorithmProvider.Type authType = AlgorithmProvider.Type.ES256; 52 | Algorithm algorithm = AlgorithmProvider.create(authType); 53 | PrivateKey authPrivateKey = algorithm.byteToPrivateKey(EncodeType.HEX.decode(TestKeys.PRIVATE_KEY_ES256)); 54 | DidKeyHolder didKeyHolder = new DidKeyHolder.Builder() 55 | .did(TEMP_DID) 56 | .keyId(authKeyId) 57 | .privateKey(authPrivateKey) 58 | .type(authType) 59 | .build(); 60 | 61 | // add public / private key 62 | String key2 = "key2"; 63 | AlgorithmProvider.Type type2 = AlgorithmProvider.Type.RS256; 64 | algorithm = AlgorithmProvider.create(type2); 65 | PublicKey publicKey = algorithm.byteToPublicKey(EncodeType.HEX.decode(TestKeys.PUBLIC_KEY_RS256)); 66 | PrivateKey privateKey2 = algorithm.byteToPrivateKey(EncodeType.HEX.decode(TestKeys.PRIVATE_KEY_RS256)); 67 | KeyProvider keyProvider2 = new KeyProvider.Builder() 68 | .keyId(key2) 69 | .publicKey(publicKey) 70 | .privateKey(privateKey2) 71 | .type(type2) 72 | .build(); 73 | 74 | 75 | String json = ScoreParameter.create(keyProvider2, EncodeType.BASE64); 76 | logger.debug("json: {}", json); 77 | 78 | Jwt addJwt = ScoreParameter.addKey(didKeyHolder, keyProvider2, EncodeType.BASE64); 79 | logger.debug("encodedJwt: {}", didKeyHolder.sign(addJwt)); 80 | } 81 | 82 | @Test 83 | void testBuildRevokeKeyJwt() throws Exception { 84 | String authKeyId = "key1"; 85 | AlgorithmProvider.Type authType = AlgorithmProvider.Type.ES256; 86 | Algorithm algorithm = AlgorithmProvider.create(authType); 87 | PrivateKey authPrivateKey = algorithm.byteToPrivateKey(EncodeType.HEX.decode(TestKeys.PRIVATE_KEY_ES256)); 88 | 89 | DidKeyHolder didKeyHolder = new DidKeyHolder.Builder() 90 | .did(TEMP_DID) 91 | .keyId(authKeyId) 92 | .privateKey(authPrivateKey) 93 | .type(authType) 94 | .build(); 95 | 96 | Jwt jwt = ScoreParameter.revokeKey(didKeyHolder, "key2"); 97 | logger.debug("encodedJwt: {}", didKeyHolder.sign(jwt)); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/core/DidKeyHolder.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import foundation.icon.did.exceptions.AlgorithmException; 4 | import foundation.icon.did.jwt.Jwt; 5 | import org.bouncycastle.util.encoders.Hex; 6 | 7 | import java.security.PrivateKey; 8 | import java.util.Arrays; 9 | import java.util.Objects; 10 | 11 | /** 12 | * This class holds the private key corresponding to the publicKey registered in the DID Document.* 13 | * 14 | *

15 | * To find a privateKey that matches a publicKey registered in a block chain, 16 | * it needs the id of DID document and the id of publicKey. 17 | * 18 | * It is responsible for signing Jwt with the privateKey you have. 19 | */ 20 | public class DidKeyHolder { 21 | 22 | private String did; 23 | private String keyId; 24 | private AlgorithmProvider.Type type; 25 | private PrivateKey privateKey; 26 | 27 | private DidKeyHolder(Builder builder) { 28 | did = builder.did; 29 | keyId = builder.keyId; 30 | type = builder.type; 31 | privateKey = builder.privateKey; 32 | } 33 | 34 | public String getDid() { 35 | return did; 36 | } 37 | 38 | public String getKeyId() { 39 | return keyId; 40 | } 41 | 42 | public String getKid() { 43 | return did + "#" + keyId; 44 | } 45 | 46 | public AlgorithmProvider.Type getType() { 47 | return type; 48 | } 49 | 50 | public PrivateKey getPrivateKey() { 51 | return privateKey; 52 | } 53 | 54 | /** 55 | * Create a signature and encoded jwt 56 | * 57 | * @param jwt the Jwt object 58 | * @return the encoded jwt 59 | */ 60 | public String sign(Jwt jwt) throws AlgorithmException { 61 | return jwt.sign(privateKey); 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | Algorithm algorithm = AlgorithmProvider.create(type); 67 | String priv = (privateKey == null) ? "" : 68 | Hex.toHexString(algorithm.privateKeyToByte(privateKey)); 69 | return "DidKeyHolder{" + 70 | "did='" + did + '\'' + 71 | ", keyId='" + keyId + '\'' + 72 | ", type=" + type + 73 | ", privateKey=" + priv + 74 | '}'; 75 | } 76 | 77 | @Override 78 | public boolean equals(Object o) { 79 | if (this == o) return true; 80 | if (!(o instanceof DidKeyHolder)) return false; 81 | DidKeyHolder keyHolder = (DidKeyHolder) o; 82 | Algorithm algorithm = AlgorithmProvider.create(type); 83 | return Objects.equals(did, keyHolder.did) && 84 | Objects.equals(keyId, keyHolder.keyId) && 85 | type == keyHolder.type && 86 | Arrays.equals(algorithm.privateKeyToByte(privateKey), 87 | algorithm.privateKeyToByte(keyHolder.privateKey)); 88 | } 89 | 90 | @Override 91 | public int hashCode() { 92 | return Objects.hash(did, keyId, type, privateKey); 93 | } 94 | 95 | public static final class Builder { 96 | private String did; 97 | private String keyId; 98 | private AlgorithmProvider.Type type; 99 | private PrivateKey privateKey; 100 | 101 | public Builder() { 102 | } 103 | 104 | public Builder(KeyProvider provider) { 105 | this.keyId = provider.getKeyId(); 106 | this.type = provider.getType(); 107 | this.privateKey = provider.getPrivateKey(); 108 | } 109 | 110 | public Builder did(String val) { 111 | did = val; 112 | return this; 113 | } 114 | 115 | public Builder keyId(String val) { 116 | keyId = val; 117 | return this; 118 | } 119 | 120 | public Builder type(AlgorithmProvider.Type val) { 121 | type = val; 122 | return this; 123 | } 124 | 125 | public Builder privateKey(PrivateKey val) { 126 | privateKey = val; 127 | return this; 128 | } 129 | 130 | public DidKeyHolder build() { 131 | checkArgument(did, "did not found"); 132 | checkArgument(privateKey, "privateKey not found"); 133 | checkArgument(keyId, "keyId not found"); 134 | checkArgument(type, "type not found"); 135 | 136 | return new DidKeyHolder(this); 137 | } 138 | 139 | public static void checkArgument(T object, String message) { 140 | if (object == null) { 141 | throw new IllegalArgumentException(message); 142 | } 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/TestKeys.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | /** 4 | * Test 용으로 사용할 Keypair 5 | */ 6 | public class TestKeys { 7 | 8 | private TestKeys() { 9 | } 10 | 11 | public final static String PUBLIC_KEY_RS256 = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100baeeea62383467611bd54bf59584e0415e" 12 | + "f8f4821f6625f97b1d4b965a664c6cd2d9e7cfe0755dac2ef55876f5978678ff239a098f42ecf471658a637e13af39272d34" 13 | + "7c6d3efbae7299034a9dd8966d01af925c491bc3526aebec5750f149367010e9615fc2f9aa3654dd4b0e9e833bb14e92f329" 14 | + "7793e84a2f274276dbb6cc209d4f172268679eb0cb2b6af133651df98794f6fc82132a060410d4b9d32ae54cbe51c37955af" 15 | + "3c8dee2c456dd2454420031a66639266fb80bbc3567c7ac322c4bd29e576d27152aea85e365e5e71e02d76f921eb2dffee61" 16 | + "b67897e4d9ea095787fcd9423c5896a6c9eea1fa933bb0a0042dafbb472e5b0ae56c0bc876f61b0203010001"; 17 | 18 | public final static String PRIVATE_KEY_RS256 = "308204be020100300d06092a864886f70d0101010500048204a8308204a40201000282010100baeeea62383467611bd54bf5" 19 | + "9584e0415ef8f4821f6625f97b1d4b965a664c6cd2d9e7cfe0755dac2ef55876f5978678ff239a098f42ecf471658a637e13" 20 | + "af39272d347c6d3efbae7299034a9dd8966d01af925c491bc3526aebec5750f149367010e9615fc2f9aa3654dd4b0e9e833b" 21 | + "b14e92f3297793e84a2f274276dbb6cc209d4f172268679eb0cb2b6af133651df98794f6fc82132a060410d4b9d32ae54cbe" 22 | + "51c37955af3c8dee2c456dd2454420031a66639266fb80bbc3567c7ac322c4bd29e576d27152aea85e365e5e71e02d76f921" 23 | + "eb2dffee61b67897e4d9ea095787fcd9423c5896a6c9eea1fa933bb0a0042dafbb472e5b0ae56c0bc876f61b020301000102" 24 | + "8201003de7d4300dcbf0f67c58b14c95a508fd2d1e061f3795c4faaf565ecb5e89a5429ebb32252a00c4323ae4de549af580" 25 | + "bf7e430486a8ee71796c3a43cfba177bbe52268accb0031d2742e5406d4c03c7a136df3579a16e73cb4e046e62e637d9d690" 26 | + "db50e681e79875d1835e3bb68e3108aa29cac9835089e771f5d6ab10a0a792b5ee36e77417ccfe1652e7cd1f9e7c0f240c50" 27 | + "c2883d325083f893a85a0d2f60898729daf09c332e4176b0a175312d091e31ba3a9d91d1611bf513d3c7cc20224c8bec7952" 28 | + "208e37e69038a08dc164dcd594eb84fdbacd512e30750c306b2abc1f2f19e51a67487c6e21fabbb6f6f1eff082e4fecc86ff" 29 | + "dd7d5410a63f703e1902818100eb3c851e2c65c5d3b5affa46324738d399e5ae7cb3a2ce5ec528dfb0e065accc44247164e4" 30 | + "8f989a82e8f0f4770ef304ddc4b7a03ed840e39c2ca5f4961fdeebd2d2619f98f7443b211ab979a72a237e2a6e9b24e49e55" 31 | + "87259045cdafc8e3f2feeea905aeaf724beab2d3a4c33a64f29330939275eefce6bf67407ac11768e902818100cb6eebf572" 32 | + "2d3f5e39643d2e2a5159ecd8dc062967716502aa19570d1372d1aa43c6a11a9de9dc3795237647861aab3ecd4b77bf79bfa9" 33 | + "e9b5c9d119085e34ee54aec5f84e232bb7e6b97d8b3f55ba13d3bf83177640ee65cd3f748f1565d190aeadbb8149e24c9116" 34 | + "2a97c80d639ef52d1b40af8ce8bf0a288058c030ffc46302818100a5a464082f332aee3d0dc1aa14555a8a85dfd9d22d9c02" 35 | + "bd710fab141daefad28b592e0fe171cbff61172e34b6716c668cd2ac8ac635f8db4d6993227b63cdd1390eb377727b0b2ca7" 36 | + "ceedb3e02627fffb590f1e176df6c3c78ba3db86606b42fc8889b45aa1685654d9bf76519298eac70279a7b42ba921c2bdae" 37 | + "05c259fda102818020b930ff84643ee33b3c2293407f6185b5c47ec13e40814d126b8d132f63b2d23ea0bedd77355d151bca" 38 | + "5e8656ce0769ccc02cfd01ea90bc655f9760fd7599f2147120ab367675dbd923514c8add91570b22ad45a2e202f72132e9de" 39 | + "f24434979f6196ddcf32b5ba48e48511cb05915642f1355087cc109ee3b0f195c8cbfb9d02818100d032dbbd3a7b4cb468ce" 40 | + "211e79b1525acde27cb424a5949cf77e6efe0bc608d34f13e11f9664381652075631498cf9a28fb9bcce80cf009a84d73d40" 41 | + "2ddb8cab16654944a8766b47f880dcf57a889db419bba840f8d436806fb504f3b43b6be16caedcc139ff4968ba21dea4fda0" 42 | + "3d48530782a0475964412eb8f4b62af93869"; 43 | 44 | public final static String PUBLIC_KEY_ES256 = "3059301306072a8648ce3d020106082a8648ce3d030107034200049548b1aedbb7b812ef2412c7922cde2f9b358939877410cce4a6777ec1b651f17b5a169be5d175d5f801069653a429ccf42c7e6ff9223ffee957dbf8ff5de917"; 45 | 46 | public final static String PRIVATE_KEY_ES256 = "308193020100301306072a8648ce3d020106082a8648ce3d03010704793077020101042059e7f78342999fc71dc12e63e6747d0a1c9059d3cf1140a2af9a1b7eb4e3e0dda00a06082a8648ce3d030107a144034200049548b1aedbb7b812ef2412c7922cde2f9b358939877410cce4a6777ec1b651f17b5a169be5d175d5f801069653a429ccf42c7e6ff9223ffee957dbf8ff5de917"; 47 | 48 | public final static String ICON_WALLET_PRIVATE_KEY = "88251765d29d0415c1922b002a3cc291665d1f9fbdfec7671f35a952472755be"; 49 | 50 | public final static String ISSUER_KEY_ID = "issuer"; 51 | 52 | public final static String HOLDER_KEY_ID = "holder"; 53 | 54 | public final static String TEMP_DID = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/core/AlgorithmProvider.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import foundation.icon.icx.crypto.LinuxSecureRandom; 4 | import org.bouncycastle.crypto.RuntimeCryptoException; 5 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 6 | 7 | import java.security.Provider; 8 | import java.security.SecureRandom; 9 | import java.security.Security; 10 | 11 | /** 12 | * This class to create a new algorithm instance 13 | */ 14 | public class AlgorithmProvider { 15 | 16 | public static final String PROVIDER = "BC"; 17 | 18 | private static final SecureRandom SECURE_RANDOM; 19 | private static int isAndroid = -1; 20 | 21 | public static final double MIN_BOUNCY_CASTLE_VERSION = 1.54; 22 | 23 | static { 24 | 25 | if (isAndroidRuntime()) { 26 | new LinuxSecureRandom(); 27 | } 28 | 29 | SECURE_RANDOM = new SecureRandom(); 30 | 31 | Provider provider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); 32 | Provider newProvider = new BouncyCastleProvider(); 33 | 34 | if (newProvider.getVersion() < MIN_BOUNCY_CASTLE_VERSION) { 35 | String message = String.format( 36 | "The version of BouncyCastle should be %f or newer", MIN_BOUNCY_CASTLE_VERSION); 37 | throw new RuntimeCryptoException(message); 38 | } 39 | 40 | if (provider != null) { 41 | Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); 42 | } 43 | 44 | Security.addProvider(newProvider); 45 | 46 | } 47 | 48 | /** 49 | * Returns the algorithm object for the type 50 | * 51 | * @param type the type object 52 | * @return the algorithm object 53 | */ 54 | public static Algorithm create(Type type) { 55 | if (type == null) { 56 | throw new IllegalArgumentException("type cannot be null."); 57 | } 58 | switch (type) { 59 | case RS256: 60 | throw new IllegalArgumentException("Not supported yet."); 61 | case ES256: 62 | throw new IllegalArgumentException("Not supported yet."); 63 | case ES256K: 64 | return new ES256KAlgorithm(); 65 | case NONE: 66 | return new NoneAlgorithm(); 67 | default: 68 | throw new IllegalArgumentException("cannot create sigAlgorithm for " + type); 69 | } 70 | } 71 | 72 | /** 73 | * Type-safe representation of signature algorithm names as defined in the icon-DID specification 74 | */ 75 | public enum Type { 76 | RS256("RsaVerificationKey2018", "SHA256withRSA", "RSA"), 77 | ES256("Secp256r1VerificationKey", "SHA256withECDSA", "EC"), 78 | ES256K("Secp256k1VerificationKey", "SHA256withECDSA", "EC"), 79 | NONE("none", "none", "none"); 80 | 81 | private String identifier; 82 | private String sigAlgorithm; 83 | private String keyAlgorithm; 84 | 85 | Type(String identifier, String sigAlgorithm, String keyAlgorithm) { 86 | this.identifier = identifier; 87 | this.sigAlgorithm = sigAlgorithm; 88 | this.keyAlgorithm = keyAlgorithm; 89 | } 90 | 91 | public String getName() { 92 | return name(); 93 | } 94 | 95 | public String getIdentifier() { 96 | return identifier; 97 | } 98 | 99 | public String getSigAlgorithm() { 100 | return sigAlgorithm; 101 | } 102 | 103 | public String getKeyAlgorithm() { 104 | return keyAlgorithm; 105 | } 106 | 107 | public static Type fromName(String name) { 108 | if (name != null) { 109 | for (Type t : Type.values()) { 110 | if (name.equalsIgnoreCase(t.getName())) { 111 | return t; 112 | } 113 | } 114 | } 115 | return null; 116 | } 117 | 118 | public static Type fromIdentifier(String identifier) { 119 | if (identifier != null) { 120 | for (Type t : Type.values()) { 121 | if (identifier.equalsIgnoreCase(t.getIdentifier())) { 122 | return t; 123 | } 124 | } 125 | } 126 | return null; 127 | } 128 | } 129 | 130 | public static SecureRandom secureRandom() { 131 | return SECURE_RANDOM; 132 | } 133 | 134 | public static boolean isAndroidRuntime() { 135 | if (isAndroid == -1) { 136 | final String runtime = System.getProperty("java.runtime.name"); 137 | isAndroid = (runtime != null && runtime.equals("Android Runtime")) ? 1 : 0; 138 | } 139 | return isAndroid == 1; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/PemUtilsTest.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | 4 | import foundation.icon.did.core.Algorithm; 5 | import foundation.icon.did.core.AlgorithmProvider; 6 | import foundation.icon.did.core.KeyProvider; 7 | import foundation.icon.did.document.EncodeType; 8 | import org.bouncycastle.util.io.pem.PemObject; 9 | import org.bouncycastle.util.io.pem.PemReader; 10 | import org.bouncycastle.util.io.pem.PemWriter; 11 | import org.junit.jupiter.api.Assertions; 12 | import org.junit.jupiter.api.Test; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import java.io.*; 17 | import java.security.*; 18 | import java.security.spec.EncodedKeySpec; 19 | import java.security.spec.InvalidKeySpecException; 20 | import java.security.spec.PKCS8EncodedKeySpec; 21 | import java.security.spec.X509EncodedKeySpec; 22 | 23 | public class PemUtilsTest { 24 | 25 | private final static Logger logger = LoggerFactory.getLogger(PemUtilsTest.class); 26 | 27 | @Test 28 | void testWriteRS256PemFile() throws Exception { 29 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.RS256); 30 | PublicKey key = algorithm.byteToPublicKey(EncodeType.HEX.decode(TestKeys.PUBLIC_KEY_RS256)); 31 | 32 | PemObject pemObject = new PemObject("PUBLIC KEY", key.getEncoded()); 33 | File file = new File("test-public.pem"); 34 | FileOutputStream fileOutputStream = new FileOutputStream(file); 35 | PemWriter pemWriter = new PemWriter(new OutputStreamWriter(fileOutputStream)); 36 | pemWriter.writeObject(pemObject); 37 | pemWriter.close(); 38 | 39 | byte[] b = parsePEMFile(file); 40 | PublicKey p = getPublicKey(b, "RSA"); 41 | Assertions.assertArrayEquals(key.getEncoded(), p.getEncoded()); 42 | } 43 | 44 | @Test 45 | void testWriteES256PemFile() throws Exception { 46 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256); 47 | PublicKey key = algorithm.byteToPublicKey(EncodeType.HEX.decode(TestKeys.PUBLIC_KEY_ES256)); 48 | 49 | PemObject pemObject = new PemObject("PUBLIC KEY", key.getEncoded()); 50 | File file = new File("test-es-public.pem"); 51 | FileOutputStream fileOutputStream = new FileOutputStream(file); 52 | PemWriter pemWriter = new PemWriter(new OutputStreamWriter(fileOutputStream)); 53 | pemWriter.writeObject(pemObject); 54 | pemWriter.close(); 55 | 56 | byte[] b = parsePEMFile(file); 57 | PublicKey p = getPublicKey(b, "EC"); 58 | Assertions.assertArrayEquals(key.getEncoded(), p.getEncoded()); 59 | } 60 | 61 | @Test 62 | void testWriteNewES256PemFile() throws Exception { 63 | AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256; 64 | Algorithm algorithm = AlgorithmProvider.create(type); 65 | KeyPair keyPair = algorithm.generateKeyPair(); 66 | KeyProvider keyProvider = new KeyProvider.Builder() 67 | .keyId("new") 68 | .publicKey(keyPair.getPublic()) 69 | .type(type) 70 | .build(); 71 | PublicKey key = keyProvider.getPublicKey(); 72 | PemObject pemObject = new PemObject("PUBLIC KEY", key.getEncoded()); 73 | File file = new File("test-es-public.pem"); 74 | FileOutputStream fileOutputStream = new FileOutputStream(file); 75 | PemWriter pemWriter = new PemWriter(new OutputStreamWriter(fileOutputStream)); 76 | pemWriter.writeObject(pemObject); 77 | pemWriter.close(); 78 | 79 | byte[] b = parsePEMFile(file); 80 | PublicKey p = getPublicKey(b, "EC"); 81 | Assertions.assertArrayEquals(key.getEncoded(), p.getEncoded()); 82 | } 83 | 84 | private static byte[] parsePEMFile(File pemFile) throws IOException { 85 | if (!pemFile.isFile() || !pemFile.exists()) { 86 | throw new FileNotFoundException(String.format("The file '%s' doesn't exist.", pemFile.getAbsolutePath())); 87 | } 88 | PemReader reader = new PemReader(new FileReader(pemFile)); 89 | PemObject pemObject = reader.readPemObject(); 90 | byte[] content = pemObject.getContent(); 91 | reader.close(); 92 | return content; 93 | } 94 | 95 | private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) { 96 | PublicKey publicKey = null; 97 | try { 98 | KeyFactory kf = KeyFactory.getInstance(algorithm); 99 | EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); 100 | publicKey = kf.generatePublic(keySpec); 101 | } catch (NoSuchAlgorithmException e) { 102 | System.out.println("Could not reconstruct the public key, the given algorithm could not be found."); 103 | } catch (InvalidKeySpecException e) { 104 | System.out.println("Could not reconstruct the public key"); 105 | } 106 | 107 | return publicKey; 108 | } 109 | 110 | private static PrivateKey getPrivateKey(byte[] keyBytes, String algorithm) { 111 | PrivateKey privateKey = null; 112 | try { 113 | KeyFactory kf = KeyFactory.getInstance(algorithm); 114 | EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 115 | privateKey = kf.generatePrivate(keySpec); 116 | } catch (NoSuchAlgorithmException e) { 117 | System.out.println("Could not reconstruct the private key, the given algorithm could not be found."); 118 | } catch (InvalidKeySpecException e) { 119 | System.out.println("Could not reconstruct the private key"); 120 | } 121 | 122 | return privateKey; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /lib/src/test/java/foundation/icon/did/IssuerDidTest.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | 4 | import foundation.icon.did.core.*; 5 | import foundation.icon.did.document.EncodeType; 6 | import foundation.icon.did.jwt.IssuerDid; 7 | import foundation.icon.did.jwt.Jwt; 8 | import org.junit.jupiter.api.Assertions; 9 | import org.junit.jupiter.api.Test; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.security.PrivateKey; 14 | import java.security.PublicKey; 15 | import java.util.Date; 16 | 17 | class IssuerDidTest { 18 | 19 | private static Logger logger = LoggerFactory.getLogger(IssuerDidTest.class); 20 | 21 | @Test 22 | void testCreate() { 23 | String keyId = "key1"; 24 | String did = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 25 | AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256; 26 | IssuerDid issuerDid = new IssuerDid.Builder() 27 | .did(did) 28 | .algorithm(type.getName()) 29 | .keyId(keyId) 30 | .build(); 31 | 32 | logger.debug("issuerDid: {}", issuerDid); 33 | 34 | Assertions.assertEquals(keyId, issuerDid.getKeyId()); 35 | Assertions.assertEquals(did, issuerDid.getDid()); 36 | Assertions.assertEquals(type.getName(), issuerDid.getAlgorithm()); 37 | } 38 | 39 | @Test 40 | void testParse() throws Exception { 41 | String keyId = "key1"; 42 | String did = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 43 | AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256; 44 | Algorithm algorithm = AlgorithmProvider.create(type); 45 | PrivateKey priKey = algorithm.byteToPrivateKey(EncodeType.HEX.decode(TestKeys.PRIVATE_KEY_ES256)); 46 | 47 | IssuerDid issuerDid = new IssuerDid.Builder() 48 | .did(did) 49 | .algorithm(type.getName()) 50 | .keyId(keyId) 51 | .build(); 52 | 53 | logger.debug("issuerDid: {}", issuerDid); 54 | 55 | String jwt = issuerDid.buildJwt().sign(priKey); 56 | 57 | logger.debug("buildJwt: {}", jwt); 58 | 59 | IssuerDid parseIssuerDid = IssuerDid.valueOf(jwt); 60 | 61 | logger.debug("parse IssuerDid: {}", parseIssuerDid); 62 | 63 | Assertions.assertEquals(keyId, parseIssuerDid.getKeyId()); 64 | Assertions.assertEquals(did, parseIssuerDid.getDid()); 65 | Assertions.assertEquals(type.getName(), parseIssuerDid.getAlgorithm()); 66 | } 67 | 68 | @Test 69 | void testES256Compact() throws Exception { 70 | String keyId = "key1"; 71 | AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256; 72 | Algorithm algorithm = AlgorithmProvider.create(type); 73 | PublicKey pubKey = algorithm.byteToPublicKey(EncodeType.HEX.decode(TestKeys.PUBLIC_KEY_ES256)); 74 | PrivateKey priKey = algorithm.byteToPrivateKey(EncodeType.HEX.decode(TestKeys.PRIVATE_KEY_ES256)); 75 | 76 | String did = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 77 | IssuerDid issuerDid = new IssuerDid.Builder() 78 | .did(did) 79 | .keyId(keyId) 80 | .algorithm(type.getName()) 81 | .build(); 82 | 83 | 84 | long issuedTimestamp = 1545208842000L; 85 | Date issued = new Date(issuedTimestamp); 86 | long duration = issuerDid.getDuration() * 1000L; // to milliseconds 87 | Date expiration = new Date(issuedTimestamp + duration); 88 | 89 | String jwt = issuerDid.buildJwt(issued, expiration).sign(priKey); 90 | logger.debug("buildJwt: {}", jwt); 91 | 92 | Jwt didJwt = Jwt.decode(jwt); 93 | 94 | Assertions.assertFalse(didJwt.verify(pubKey).isSuccess()); 95 | Assertions.assertEquals(PropertyName.ALGO_KEY_ECDSA, didJwt.getHeader().getAlg()); 96 | Assertions.assertEquals(did + "#" + keyId, didJwt.getHeader().getKid()); 97 | Assertions.assertEquals(issued, didJwt.getPayload().getIat()); 98 | Assertions.assertEquals(expiration, didJwt.getPayload().getExp()); 99 | 100 | } 101 | 102 | @Test 103 | void testRS256Compact() throws Exception { 104 | String keyId = "key1"; 105 | AlgorithmProvider.Type type = AlgorithmProvider.Type.RS256; 106 | Algorithm algorithm = AlgorithmProvider.create(type); 107 | PublicKey publicKey = algorithm.byteToPublicKey(EncodeType.HEX.decode(TestKeys.PUBLIC_KEY_RS256)); 108 | PrivateKey priKey = algorithm.byteToPrivateKey(EncodeType.HEX.decode(TestKeys.PRIVATE_KEY_RS256)); 109 | 110 | String did = "did:icon:0000961b6cd64253fb28c9b0d3d224be5f9b18d49f01da390f08"; 111 | IssuerDid issuerDid = new IssuerDid.Builder() 112 | .did(did) 113 | .keyId(keyId) 114 | .algorithm(type.getName()) 115 | .build(); 116 | 117 | long issuedTimestamp = 1545208842000L; 118 | Date issued = new Date(issuedTimestamp); 119 | long duration = issuerDid.getDuration() * 1000L; // to milliseconds 120 | Date expiration = new Date(issuedTimestamp + duration); 121 | 122 | String jwt = issuerDid.buildJwt(issued, expiration).sign(priKey); 123 | logger.debug("buildJwt: {}", jwt); 124 | 125 | Jwt didJwt = Jwt.decode(jwt); 126 | Assertions.assertFalse(didJwt.verify(publicKey).isSuccess()); 127 | Assertions.assertEquals(PropertyName.ALGO_KEY_RSA, didJwt.getHeader().getAlg()); 128 | Assertions.assertEquals(did + "#" + keyId, didJwt.getHeader().getKid()); 129 | Assertions.assertEquals(issued, didJwt.getPayload().getIat()); 130 | Assertions.assertEquals(expiration, didJwt.getPayload().getExp()); 131 | 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /lib/src/main/java/foundation/icon/did/core/ES256KAlgorithm.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did.core; 2 | 3 | import foundation.icon.did.exceptions.AlgorithmException; 4 | import foundation.icon.did.exceptions.KeyPairException; 5 | import foundation.icon.icx.crypto.ECDSASignature; 6 | import foundation.icon.icx.crypto.IconKeys; 7 | import foundation.icon.icx.data.Bytes; 8 | import org.bouncycastle.crypto.params.ECDomainParameters; 9 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 10 | import org.bouncycastle.crypto.signers.ECDSASigner; 11 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 12 | import org.bouncycastle.jcajce.provider.digest.SHA3; 13 | import org.bouncycastle.jce.ECNamedCurveTable; 14 | import org.bouncycastle.jce.interfaces.ECPublicKey; 15 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 16 | import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; 17 | import org.bouncycastle.jce.spec.ECParameterSpec; 18 | import org.bouncycastle.jce.spec.ECPrivateKeySpec; 19 | import org.bouncycastle.jce.spec.ECPublicKeySpec; 20 | import org.bouncycastle.util.BigIntegers; 21 | 22 | import java.math.BigInteger; 23 | import java.security.*; 24 | import java.security.spec.ECGenParameterSpec; 25 | import java.security.spec.InvalidKeySpecException; 26 | import java.util.Arrays; 27 | 28 | import static foundation.icon.did.core.AlgorithmProvider.PROVIDER; 29 | import static foundation.icon.did.core.AlgorithmProvider.secureRandom; 30 | import static foundation.icon.did.core.PropertyName.EC_CURVE_PARAM_SECP256K1; 31 | 32 | public class ES256KAlgorithm implements Algorithm { 33 | 34 | private AlgorithmProvider.Type type = AlgorithmProvider.Type.ES256K; 35 | 36 | private ECNamedCurveParameterSpec spec; 37 | private ECParameterSpec ecparameterSpec; 38 | 39 | ES256KAlgorithm() { 40 | spec = ECNamedCurveTable.getParameterSpec("secp256k1"); 41 | ecparameterSpec = new ECParameterSpec(spec.getCurve(), spec.getG(), spec.getN()); 42 | } 43 | 44 | @Override 45 | public AlgorithmProvider.Type getType() { 46 | return type; 47 | } 48 | 49 | @Override 50 | public KeyPair generateKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { 51 | KeyPairGenerator pairGen = KeyPairGenerator.getInstance("EC", PROVIDER); 52 | pairGen.initialize(new ECGenParameterSpec(EC_CURVE_PARAM_SECP256K1), secureRandom()); 53 | return pairGen.generateKeyPair(); 54 | } 55 | 56 | @Override 57 | public byte[] sign(PrivateKey privateKey, byte[] data) throws AlgorithmException { 58 | Bytes p = new Bytes(privateKeyToByte(privateKey)); 59 | ECDSASignature signature = new ECDSASignature(p); 60 | byte[] hash = new SHA3.Digest256().digest(data); 61 | BigInteger[] sig = signature.generateSignature(hash); 62 | return signature.recoverableSerialize(sig, hash); 63 | } 64 | 65 | @Override 66 | public boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws AlgorithmException { 67 | try { 68 | byte[] pub = ((ECPublicKey) publicKey).getQ().getEncoded(false); 69 | byte[] sigr = Arrays.copyOfRange(signature, 0, ES256Algorithm.EC_256_NUMBER); 70 | byte[] sigs = Arrays.copyOfRange(signature, ES256Algorithm.EC_256_NUMBER, ES256Algorithm.EC_256_NUMBER * 2); 71 | 72 | ECDomainParameters domain = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN()); 73 | ECPublicKeyParameters publicKeyParams = 74 | new ECPublicKeyParameters(spec.getCurve().decodePoint(pub), domain); 75 | 76 | ECDSASigner signer = new ECDSASigner(); 77 | signer.init(false, publicKeyParams); 78 | byte[] hash = new SHA3.Digest256().digest(data); 79 | return signer.verifySignature(hash, new BigInteger(1, sigr), new BigInteger(1, sigs)); 80 | } catch (Exception e) { 81 | return false; 82 | } 83 | } 84 | 85 | @Override 86 | public byte[] publicKeyToByte(PublicKey publicKey) { 87 | return ((ECPublicKey) publicKey).getQ().getEncoded(false); 88 | } 89 | 90 | @Override 91 | public byte[] privateKeyToByte(PrivateKey privateKey) { 92 | return BigIntegers.asUnsignedByteArray(IconKeys.PRIVATE_KEY_SIZE, ((BCECPrivateKey) privateKey).getD()); 93 | } 94 | 95 | @Override 96 | public PublicKey byteToPublicKey(byte[] b) throws KeyPairException { 97 | try { 98 | KeyFactory fact = KeyFactory.getInstance(type.getKeyAlgorithm(), BouncyCastleProvider.PROVIDER_NAME); 99 | return fact.generatePublic(new ECPublicKeySpec(spec.getCurve().decodePoint(b), ecparameterSpec)); 100 | } catch (NoSuchAlgorithmException | NoSuchProviderException e) { 101 | throw new KeyPairException("Could not reconstruct the public key, the given algorithm could not be found."); 102 | } catch (InvalidKeySpecException e) { 103 | throw new KeyPairException("Could not reconstruct the public key"); 104 | } 105 | } 106 | 107 | @Override 108 | public PrivateKey byteToPrivateKey(byte[] b) throws KeyPairException { 109 | try { 110 | KeyFactory fact = KeyFactory.getInstance(type.getKeyAlgorithm(), BouncyCastleProvider.PROVIDER_NAME); 111 | return fact.generatePrivate(new ECPrivateKeySpec(new BigInteger(1, b), ecparameterSpec)); 112 | } catch (NoSuchAlgorithmException | NoSuchProviderException e) { 113 | throw new KeyPairException("Could not reconstruct the private key, the given algorithm could not be found."); 114 | } catch (InvalidKeySpecException e) { 115 | throw new KeyPairException("Could not reconstruct the private key"); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /sample/src/main/java/foundation/icon/did/DidManageSample.java: -------------------------------------------------------------------------------- 1 | package foundation.icon.did; 2 | 3 | import foundation.icon.did.config.SampleConfig; 4 | import foundation.icon.did.config.SampleKeys; 5 | import foundation.icon.did.core.*; 6 | import foundation.icon.did.document.Document; 7 | import foundation.icon.did.document.EncodeType; 8 | import foundation.icon.did.exception.SampleException; 9 | import foundation.icon.did.exceptions.AlgorithmException; 10 | import foundation.icon.did.exceptions.KeyPairException; 11 | import foundation.icon.did.exceptions.KeystoreException; 12 | import foundation.icon.did.jwt.Jwt; 13 | import foundation.icon.did.score.ScoreParameter; 14 | import foundation.icon.icx.IconService; 15 | import foundation.icon.icx.KeyWallet; 16 | import foundation.icon.icx.data.Address; 17 | import foundation.icon.icx.data.Bytes; 18 | import foundation.icon.icx.data.IconAmount; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import java.io.File; 23 | import java.io.IOException; 24 | import java.math.BigInteger; 25 | 26 | @SuppressWarnings("Duplicates") 27 | public class DidManageSample { 28 | 29 | public static Logger logger = LoggerFactory.getLogger(DidManageSample.class); 30 | 31 | public static void printDocument(Document document) { 32 | logger.debug("DID Document : {}", document); 33 | logger.debug("DID Document json: {}\n", document.toJson()); 34 | } 35 | 36 | public static void main(String[] args) throws IOException, KeystoreException, KeyPairException { 37 | 38 | // Create objects for connecting ICON Network 39 | SampleConfig config = SampleConfig.local(); 40 | BigInteger networkId = config.getNetworkId(); 41 | IconService iconService = config.iconService(); 42 | 43 | // DID Document Score Address 44 | Address scoreAddress = config.getScoreAddress(); 45 | 46 | // Create the DidService object 47 | DidService didService = new DidService(iconService, networkId, scoreAddress); 48 | 49 | KeyWallet wallet = KeyWallet.load(new Bytes(SampleKeys.ISSUER_WALLET_PRIVATE_KEY)); 50 | BigInteger balance = iconService.getBalance(wallet.getAddress()).execute(); 51 | if (balance.compareTo(IconAmount.of("1", IconAmount.Unit.ICX).toLoop()) < 0) { 52 | throw new SampleException("the balance of " + wallet.getAddress() + " < 1 icx\n Use your KeyWallet"); 53 | } 54 | 55 | logger.debug(" ### CREATE DID Document"); 56 | // the id for publicKey/privateKey 57 | String keyId = "sampleKey1"; 58 | 59 | // Generate a new KeyProvider object 60 | Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256K); 61 | KeyProvider keyProvider = null; 62 | try { 63 | keyProvider = algorithm.generateKeyProvider(keyId); 64 | } catch (Exception e) { 65 | throw new SampleException("KeyProvider creation failed"); 66 | } 67 | logger.debug("key provider : {}", keyProvider); 68 | 69 | EncodeType encodeType = EncodeType.BASE64; 70 | 71 | // Send a transaction to create a DID document 72 | Document document = null; 73 | try { 74 | String json = ScoreParameter.create(keyProvider, encodeType); 75 | document = didService.create(wallet, json); 76 | } catch (IOException e) { 77 | throw new SampleException("DID document creation failed \n" + e.getMessage()); 78 | } 79 | 80 | // print DID Document 81 | printDocument(document); 82 | 83 | logger.debug(" ### Store the key information for using did."); 84 | String password = "P@ssw0rd"; 85 | File file = new File("./"); 86 | DidKeyHolder didKeyHolder = new DidKeyHolder.Builder(keyProvider) 87 | .did(document.getId()) 88 | .build(); 89 | Keystore.storeDidKeyHolder(password, didKeyHolder, file); 90 | DidKeyHolder k = Keystore.loadDidKeyHolder(password, new File(document.getId() + ".json")); 91 | logger.debug("load KeyProvider : {}\n", k); 92 | 93 | logger.debug(" ### READ Document"); 94 | // Get a id of DID Document 95 | String did = document.getId(); 96 | logger.debug("DID : {}", did); 97 | 98 | try { 99 | document = didService.readDocument(did); 100 | } catch (IOException e) { 101 | throw new SampleException(e.getMessage()); 102 | } 103 | 104 | // print DID Document 105 | printDocument(document); 106 | 107 | 108 | logger.debug(" ### Add public key"); 109 | // the id of publicKey/privateKey to add 110 | String newKeyId = "sampleKey2"; 111 | 112 | // Generate a new KeyProvider object 113 | KeyProvider newKeyProvider = null; 114 | algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256K); 115 | try { 116 | newKeyProvider = algorithm.generateKeyProvider(newKeyId); 117 | } catch (Exception e) { 118 | throw new SampleException("KeyProvider creation failed"); 119 | } 120 | logger.debug("add key porvider : {}", keyProvider); 121 | 122 | encodeType = EncodeType.BASE64; 123 | 124 | // Send a transaction to add a publicKey to the DID document 125 | try { 126 | Jwt addJwt = ScoreParameter.addKey(didKeyHolder, newKeyProvider, encodeType); 127 | document = didService.addPublicKey(wallet, didKeyHolder.sign(addJwt)); 128 | } catch (IOException e) { 129 | throw new SampleException("Failed to add public key \n" + e.getMessage()); 130 | } catch (AlgorithmException e) { 131 | throw new SampleException("Failed to sign \n" + e.getMessage()); 132 | } 133 | 134 | // print DID Document 135 | printDocument(document); 136 | 137 | logger.debug(" ### Revoke public key"); 138 | logger.debug("revoke key id : {}", newKeyId); 139 | try { 140 | Jwt revokeJwt = ScoreParameter.revokeKey(didKeyHolder, newKeyId); 141 | document = didService.revokeKey(wallet, didKeyHolder.sign(revokeJwt)); 142 | } catch (IOException e) { 143 | throw new SampleException("Failed to revoke public key \n" + e.getMessage()); 144 | } catch (AlgorithmException e) { 145 | throw new SampleException("Failed to sign \n" + e.getMessage()); 146 | } 147 | 148 | // print DID Document 149 | printDocument(document); 150 | 151 | logger.debug(" ### The end"); 152 | 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 34 | 35 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |